Tampilkan postingan dengan label Box2D. Tampilkan semua postingan
Tampilkan postingan dengan label Box2D. Tampilkan semua postingan

Integrating Level Helper to hybrid project iOS+Android using Cocos2d-x

The wonder of Cocos2dx is: it's written in C++. And because it's written in C++, it can run on any system that supports C++. That means all systems, except Windows phone (as of this writing).

To integrate Level Helper to hybrid iOS Android Cocos2d-x project (common C++ codes for both iOS and Android), first create hybrid project as is explained in this post: http://growingshoot.blogspot.com/2013/04/cocos2dx-hybrid-project-ios-and-android.html

>> The trick to integrate Level Helper <<

Most Android does not support pvr.ccz, while most (or all?) iPhones support it. I want pvr.ccz for iPhone project, and plain PNG for Android project. pvr.ccz for iPhone project is even more compelling because most of the time, the PNG generated by SpriteHelper causes pngcrush caught libpng error  on compile time, while the same PNG file does not cause trouble for Android.

To achieve that,  I create the Sprite Helper project, and save the pvr.ccz version inside {PROJECT_NAME}/Resources directory and save the PNG version inside {PROJECT_NAME}/android/assets directory. Then, I create the Level Helper project inside {PROJECT_NAME}/Resources directory.

The {PROJECT_NAME}/android/build_native.sh needs modification so it does not delete the assets directory and replace it with contents of Resources directory. It also must copy only PNG and plhs (project file of Level Helper) from inside Resources directory into the assets directory. When copying plhs file, it must replace all occurences of pvr.ccz strings with png strings.

Following screenshots, respectively: saving Sprite Sheet for iPhone project (pvr.ccz, cocos2d-x resource handling, hd suffix set, 2x hd suffix empty), saving Sprite Sheet for Android project (png, cocos2d-x resource handling, hd suffix empty, 2x hd suffix empty), new build_native.sh file, old build_native.sh file.





New build_native.sh below and old build_native.sh above:



2. Do not forget to set content scale factor to 2 in App Delegate!     pDirector->setContentScaleFactor(2); You must also tell iPhone to look inside hd directory for files when in Retina mode: CCFileUtils::sharedFileUtils()->setResourceDirectory("hd"); This must be done before loading level. Can not be done in App Delegate!

3. C++ codes generated by Level Helper must be referenced in the {Project name}/android/jni/Android.mk file (LOCAL_SRC_FILES for the cpp files and LOCAL_C_INCLUDES for the headers). The following are my Android.mk file and my project structure (I put LevelHelper dir, which is codes generated by LevelHelper, under Classes dir).



Cocos2dx hybrid project iOS and Android

So I'm porting a game from iOS to Android. It's a Cocos2d game. Naturally I look to implementing the Android port using Cocos2dX, since it's a C++ port of Cocos2d.
I found many tutorials online of how to create a shared project for Android and iOS. By "shared", I mean the C++ code. The C++ code is common to iOS and Android. With some Objective-C code binding for the iOS project and some Java code for the Android project.
But the Objective-C and Java codes are small and do not change.
The ones that change is the C++ codes, shared by iOS and Android project.
Among all tutorials online, I find this one most concise and easy to follow: http://www.bold-it.com/ios/cocos2d-x-box2d-iosandroid-hybrid-tutorial/

It's just that the tutorial is written for Cocos2d-2.0-x-2.0.2. The most current version as of this writing is Cocos2d-2.0-x-2.0.4.
Only minor version change but I had to make some extra adjustment to compile and run both project successfully.

So, follow the steps in the above tutorial:
1. Download Cocos2dx.
2. Install the XCode template: ./install-templates-xcode.sh
3. Create XCode project for iOS.
4. Edit the create-android-project.sh file before creating the Android project. Set the NDK_ROOT_LOCAL and ANDROID_SDK_ROOT_LOCAL variables.
5. Execute create-android-project.sh --box2d to create Android box2D project.
6. Make sure it compiles by executing ./build_native.sh
7. Move the necessary Android files:

  • Move the /testgame/proj.android folder to the iphone project folder, next to ios. Rename it android.
  • Copy the /external/Box2D folder into /libs/, replace what is there.
  • Copy the /cocos2dx folder into /libs/, replace what is there.
  • Copy the /CocosDenshion/android folder into /libs/CocosDenshion/
  • Copy the /extensions folder to /libs/, replace what is there.
8. In /android/build_native.sh, change line 40: COCOS2DX_ROOT="$DIR/../libs"
9. Next, in android/jni/Android.mk, add a LOCAL_C_INCLUDES path to Box2D and change “$(call import-module,external/Box2D)” to “$(call import-module,Box2D)”. To be able to see logs in Logcat, add LOCAL_CFLAGS += -DCOCOS2D_DEBUG=1. Delete this line or set the value to 0 for production to improve efficiency.
This is how my Android.mk looks like:

After the log is enabled, you can see the log output from console by typing: adb logcat | grep D/cocos2d-x. Don't forget to use CCLOG to print debugging messages.
10. Update android.library.reference.1 in android/project.properties:

# Project target.
target=android-10
android.library.reference.1=../libs/cocos2dx/platform/android/java
11. Define sdk.dir in libs/cocos2dx/platform/android/java/ant.properties as such:
sdk.dir=/Users/firman/Documents/android-sdk-macosx
12. Edit the target in libs/cocos2dx/platform/android/java/project.properties to match the target defined in android/project.properties:
target=android-10

Done! Now you can run the iOS project from inside XCode. As for the Android project:


./build_native.sh clean
./build_native.sh
ant debug
adb install -r bin/testgame-debug.apk
adb shell am start -a android.intent.action.MAIN -n com.boldit.testgame/.testgame

Box2D weld joint

So one might think this is the simplest joint, but it turns out there are so much subtlety into it. After a gruesome few days of trying to figure out what went wrong with my weld joint, finally I (think I) get a good grasp of it.

Let's start with the basic:

There are 3 major components with joints.
1. The reference body. This is the body that goes first in the b2JointDef.Initialize method. The reference is the body to which the joint is attached.
2. The target body. This is the body that goes second in the b2JointDef.Initialize method. The target body is the attached body. So the movement of the target body follows that of the reference body.
3. Anchor. Where to attach the target body to the reference body. This is a b2Vec2 coordinate relative to the reference body.

i.e:

b2WeldJointDef.Initialize(refBody, targetBody, b2Vec2(-5, 10));

The target body will be glued to the reference body at -5, 10 from the ref body position.

As for the weld joint, one must define the following for it to work:


    bodyDef.fixedRotation = FALSE;
    fixDef.density = 2.0f;


Basically one must define the density and set fixedRotation to false. Otherwise, the weld joint will fall off. The bodies glued together using weld joint must also be large enough. There will be an area check somewhere inside the Box2D engine to make sure that areas joined exceed a certain delta value!

Find the total force when two fixtures collide

contact->GetManifold().localPoint.Length()

Small notes

Some lessons I learnt the past few days:

Cocos2D: Don't load heavy resources in the init method. This will crash the app. Instead, load them in a scheduled method.
Box2D: Don't change scene while world is locked (when detecting and responding to touch). This will cause a memory leak. Instead, change the scene in an scheduled method.
Android: The method insert in SQLiteDatabase doesn't throw exception. It does throw exception and print the stack trace but it's internally handled by that method and the exception won't by caught by the try catch block. If you need to catch exception, use insertOrThrow instead.

Code snippet to pad numbers with zero:

        NSString *paddingFormat = [[NSString stringWithFormat:@"time\n%%0%dd:%%0%dd", 2, 2] retain];
        NSString *paddedNumber = [NSString stringWithFormat:paddingFormat, 0, 0];

Result:

time
00:00

Correcting gravity of Box2D for portrait mode


In the accelerometer function of HelloWorldLayer.mm the gravity is set with this line
b2Vec2 gravity( -accelY * 10, accelX * 10);
In order to simulate the desired effect in portrait mode, the line must be re-ordered to:
b2Vec2 gravity( accelX * 10, accelY * 10);