Home > AndEngine, programming > Basic AndEngine And Box2D Extension Project

Basic AndEngine And Box2D Extension Project

For those waiting for me to resume my jPCT-AE project, I’m still working on it. I just got delayed a bit by my personal life demanding attention, as well as figuring out some details of the jPCT-AE API. I hope to release the next article on it later this or maybe next week.

Anyway, a few days ago someone on IRC’s Freenode network mentioned a game idea which we discussed for a bit before he suggested that I could develop it for Android. It is a simple 2D game and aside from being a possible and much-needed income source for me, it would also be a fun opportunity to explore the 2D engine options for Android a bit more. My earlier research on an Android 3D game engine had already led me to AndEngine [1], which is a very feature-complete 2D game engine with useful extensions including physics using the Box2D 2D physics engine.

There’s one major hurdle with using AndEngine, however, as I soon discovered after picking it. There is no documentation. Nothing. No API reference. No Hello World sample. All you get are random tutorials on the forum and scattered around the internet using which you have to piece together how to do things. And many of those tutorials will be outdated, wrong, or both. Ergo it took me a few days just to sort through the mess and get a basic physics sample working. Since I’m a very generous person, I’ll be sharing my experiences with you all :)

The fun starts with just obtaining AndEngine. Previous the core project [2] and Box2D extension [3] were distributed as JAR libraries and pre-compiled native code libraries. Very recently this all changed and you now have to get the source and link the source into your own project. The easiest way to handle this is by using the Eclipse IDE with the ADT plugin [4]. Install the Mercurial plugin for Eclipse (Help -> Install New Software…), and then choose import in Eclipse, pick Mercurial as source and input the address of the source repository. For the core project this is https://code.google.com/p/andengine/ and for the Box2D extension this is http://code.google.com/p/andenginephysicsbox2dextension.

In your project’s properties, go to the build path, then the source tab and link the source folder of each of the projects you have imported. Don’t forget to rename the src folder in the imported project to something else when you link them, or it’ll complain about duplicate folders in your project. You now have added AndEngine successfully to your project. If you also want to use the Box2D extension there is one additional thing to do. As it uses native code for the actual physics calculations, it uses the Native Development Kit (NDK, available on the Android development site). To use the Box2D extension you have to get the NDK and extract it somewhere, then also ensure you got Cygwin with the MingW compiler and make tool installed. This assuming you are on Windows.

Go into the Box2D extension project’s folders and enter the /jni folder. Inside it you’ll see two important files: build.sh and build.bat. Open both files in an editor and change the paths in there to point to the Cygwin bin and the NDK’s ndk-build folder, overwriting the existing paths. Also make sure that in build.bat it changes to the right drive. Now execute build.bat and it should start compiling the extension project for the ARM architecture. After this completes, you will see that the extension project folder has a new /libs folder with in it a number of folders, the most important one being /libs/armeabi, which contains a number of files, the important one being the .so file which is the actual library we needed. In the /libs/armeabi-v7a folder also created, rename or remove the gdbserver instance, or you will get errors. I renamed it to gdbserver.bak, just in case I need it for debugging later on.

Copy the /libs/armeabi folder to a /libs folder in the root folder of your own project. You now have the AndEngine’s Box2D extension installed and ready to be used.

Putting an example together was harder than I thought, but the basics of it are as follows:

public class Main extends BaseGameActivity implements IAccelerometerListener {
DisplayMetrics metrics;

private Camera mCamera;
private Scene mScene;
private PhysicsWorld mPhysicsWorld;
private BitmapTextureAtlas mBitmapTextureAtlas;
private TextureRegion mCircleFaceTextureRegion;

private static final int CAMERA_WIDTH = 480;
private static final int CAMERA_HEIGHT = 800;

private static final FixtureDef FIXTURE_DEF = PhysicsFactory.createFixtureDef(1, 0.5f, 0.5f);

@Override
public Engine onLoadEngine() {
metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
this.mCamera = new Camera(0, 0, CAMERA_WIDTH,  CAMERA_HEIGHT);
return new Engine(new EngineOptions(true, ScreenOrientation.PORTRAIT,
new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT),
this.mCamera));
}

We make use of the BaseGameActivity class instead of Activity for our application. We implement the IAccelerometerListener because we want to make use of the accelerometer sensor of the phone. We declare the resources and the fixed values such as the size of the game window, which I have set here to the height and width of the screen of my phone, which has a 480×800 resolution. A FixtureDef is used with the physics engine. It sets the density of the object, its elasticity and friction values. This will affect the behaviour of an object while it’s inside the physics world.

I obtain the display metrics object so that I can use the values of it later in converting from pixels to display independent pixels. A Camera is created with the previously determined width and height. Finally an Engine is created with the screen orientation set to portrait and with the ratio fixed to the one determined by the camera width and height, as well as linking the Engine to the Camera.

@Override
public void onLoadResources() {
/* Textures. */
this.mBitmapTextureAtlas = new BitmapTextureAtlas(64, 128, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
this.mCircleFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory
.createFromAsset(mBitmapTextureAtlas, this, "face_circle.png", 0, 0);
this.mEngine.getTextureManager().loadTexture(this.mBitmapTextureAtlas);
}

In OnLoadResources we load all of the textures and other resources we will be using later. For OpenGL ES it is most efficient to use a single, large texture (1024×1024 on older phones, 2048×2048 on newer) instead of many small ones, as OpenGL ES can only hold one texture in memory at any time, making switching between textures an unnecessary burden. We use a single texture (texture atlas, previously called Texture in AndEngine) with all of the textures we will use copied onto it. TextureRegions are used to read sections from this unified texture. In the above code we create a TextureRegion while adding its contents to the texture atlas. We then tell the Engine’s texture manager to load the texture atlas resource into memory.

@Override
public Scene onLoadScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
mScene = new Scene();
mScene.setBackground(new ColorBackground(0, 0, 0));
this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);

// create demo scene
final Sprite face;
final Body body;

face = new Sprite(0.0f, 0.0f, this.mCircleFaceTextureRegion);
body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF);

Line line_top = new Line(0, 0, CAMERA_WIDTH, 0, 5.0f * metrics.density);
Line line_left = new Line(0, 0, 0, CAMERA_HEIGHT, 5.0f * metrics.density);
Line line_right = new Line(CAMERA_WIDTH, 0, CAMERA_WIDTH, CAMERA_HEIGHT, 5.0f * metrics.density);
line_top.setColor(1, 1, 1); // RGB
line_left.setColor(1, 1, 1);
line_right.setColor(1, 1, 1);
Body wall_top = PhysicsFactory.createLineBody(mPhysicsWorld, line_top, FIXTURE_DEF);
Body wall_left = PhysicsFactory.createLineBody(mPhysicsWorld, line_left, FIXTURE_DEF);
Body wall_right = PhysicsFactory.createLineBody(mPhysicsWorld, line_right, FIXTURE_DEF);

this.mScene.attachChild(face);
this.mScene.attachChild(line_top);
this.mScene.attachChild(line_left);
this.mScene.attachChild(line_right);
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face, body, true, true));
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(line_top, wall_top, true, true));
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(line_left, wall_left, true, true));
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(line_right, wall_right, true, true));

this.enableAccelerometerSensor(this);
mScene.registerUpdateHandler(mPhysicsWorld);

return mScene;
}

OnLoadScene is where the scene is set up. If your games uses levels you would make this part a lot more complex, but since we are just setting up a basic example here, we do just that here. We create a new Scene, set the background to black and create a new PhysicsWorld, which is essentially a Box2D World [6]. This PhysicsWorld is given a gravity vector (downwards Y vector is set to ~1 G, or Earth gravity), with allow sleep set to false.

Next a texture we loaded previously from /assets/gfx is now used to create a new Sprite, essentially a bitmap image. A body is created as well, which is required for the PhysicsWorld. We make this a Dynamic Body, meaning that it will be affected by the PhysicsWorld without requiring external input. We also apply the FixtureDef we previously created to it.

Finally we create three lines which create the boundaries of the screen. These are set to a white colour, with a width adapting to the screen’s density. They also get the same FixtureDef. After completing all four objects this way, we add their faces to the Scene and their Bodies to the PhysicsWorld, using a PhysicsConnector. The latter is the link between the visual and physics representations and ensure that the results of the PhyicsWorld update the Scene.

Next we enable the accelerometer and register the PhysicsWorld as being responsible for handling updates for the Scene.

@Override
public void onLoadComplete() {
// TODO Auto-generated method stub

}

@Override
public void onAccelerometerChanged(final AccelerometerData pAccelerometerData) {
final Vector2 gravity = Vector2Pool.obtain(pAccelerometerData.getX(), pAccelerometerData.getY());
this.mPhysicsWorld.setGravity(gravity);
Vector2Pool.recycle(gravity);
}

}

In onLoadComplete we can do something after the resources finish loading, but here we have no need to do so. Instead we move on to implementing the accelerometer listener. Upon changing its input, we change the gravity vector of the PhysicsWorld to the vector created by the accelerometer data. This has as result that when you move the phone into a particular direction, the ball will follow. Tilting the phone also has the same result.

Since we didn’t put in a bottom line, the ball is free to bounce between the three lines and vanish into the unlimited Scene beyond the screen’s boundaries. By detecting this last condition, we could indicate a ‘game over’ status in a game.

Hopefully this brief tutorial was useful and informative. If there are any questions about AndEngine or related, please don’t hesitate to ask them in the comments. I may be a beginner with AndEngine, but I am a quick study and love helping fellow developers :)

Maya

[1] http://www.andengine.org/
[2] http://code.google.com/p/andengine/
[3] http://code.google.com/p/andenginephysicsbox2dextension/
[4] http://www.andengine.org/forums/tutorials/to-everyone-looking-for-jar-files-t4686.html
[5] http://www.andengine.org/forums/tutorials/build-the-examples-and-box2d-extension-t4720.html
[6] http://www.box2d.org/manual.html#_Toc258082968

 

About these ads
Categories: AndEngine, programming
  1. June 29, 2012 at 8:37 AM

    I have put all the hat files n armeabi folder contains .so file
    your program is showing errors in onloadscene method .
    when i put my cursor on it it gives configure build path error..
    i dun know how to use .so file n configure the build path.

    can u put this files here.. so that i can download n tell how to use that.. ?
    or simply give the download link of this project so that i can learn this demo :(
    thnks

  2. August 21, 2012 at 5:33 PM

    Thanks a lot for your post, it still helps here, in 2012 :)
    I could never imagine than a .so file has anything to do with windows, but it does.

  1. March 18, 2013 at 4:14 PM

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 1,557 other followers

%d bloggers like this: