Jan 132011
 

I could not find a simple tutorial that demonstrated a Box2D side scroller with the camera fixed on the player. Meaning, it would give the appearance of the background scrolling while the player remains stationary in one spot on the screen. So for this article, I am going go through a simple tutorial to do just that. If you are not familiar with the basics of the Box2D physics engine, you may want to check out my previous article Beginning Box2D Physics Engine.

Video Demo

Here is the video of the demo in action. The circle is the object that the camera should be following. I placed a little Cocos2D sprite in the center just for kicks. Please excuse the choppy video.

Box2D Side Scroller from Min K on Vimeo.

Platform Generation


For this simple demo, I used the excellent and free software called Tiled Map Editor to lay out the scene. You do not have to use the program to draw your scene. You can create the Box2D objects manually. Please check the software’s web site to learn more about using the software. There really is not much to learn though, because it is very simple to use and understand. The image on the left shows that I have two layers. One layer is the tile layer and the other is actually an object layer, which I titled Collision. I simply drew two boxes in the Collision layer. For this example, the tile layer is actually empty, but you can decide to lay down some actual tile images if you want. Then in the code, I will read the location and size of these collision boxes and create a Box2D object out of them. Here is the code to achieve this.

- (void) addScrollingBackgroundWithTileMap {
	tileMapNode = [CCTMXTiledMap tiledMapWithTMXFile:@"scroller.tmx"];
	tileMapNode.anchorPoint = ccp(0, 0);
	[self addChild:tileMapNode];
}

The code above reads in the tile map with the filename “scroller.tmx” that I saved using the application. If you delve into the CCTMXTiledMap source, you will see that the tiles are actually CCSprite objects. But again, for this example, my tile layer is empty because I am only using the object layer which I labeled Collision in Tiled.

- (void) drawCollisionTiles {
	CCTMXObjectGroup *objects = [tileMapNode objectGroupNamed:@"Collision"];
	NSMutableDictionary * objPoint;
	
	int x, y, w, h;	
	for (objPoint in [objects objects]) {
		x = [[objPoint valueForKey:@"x"] intValue];
		y = [[objPoint valueForKey:@"y"] intValue];
		w = [[objPoint valueForKey:@"width"] intValue];
		h = [[objPoint valueForKey:@"height"] intValue];	
		
		CGPoint _point=ccp(x+w/2,y+h/2);
		CGPoint _size=ccp(w,h);
		
		[self makeBox2dObjAt:_point 
					withSize:_size 
					 dynamic:false 
					rotation:0 
					friction:0.0f 
					 density:0.0f 
				 restitution:0 
					   boxId:-1];
	}
}

The drawCollisionTiles method above will read the Collision boxes from the tile map and create Box2D objects out of them. It loops through the node to find all the objects and then uses the location (x, y) and the size (w, h) to create the Box2D object to be placed into the physics world and then simulated. The thing to notice here is that the friction value is set to 0.0. This will cause the circle, which I am using as the player object, to slide across the platform. Had I actually set the friction to a non-zero value (like 4.0) you would have seen the circle actually rotate and roll across the screen instead of gliding across the screen. Actually, you need to set the friction to a non-zero value for both the platforms and the player object.

- (void) makeBox2dObjAt:(CGPoint)p 
			   withSize:(CGPoint)size 
				dynamic:(BOOL)d 
			   rotation:(long)r 
			   friction:(long)f 
				density:(long)dens 
			restitution:(long)rest 
				  boxId:(int)boxId {
	
	// Define the dynamic body.
	//Set up a 1m squared box in the physics world
	b2BodyDef bodyDef;
//	bodyDef.angle = r;

	bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
	bodyDef.userData = NULL;
	//bodyDef.userData = sprite;
	
	b2Body *body = world->CreateBody(&bodyDef);
	
	// Define another box shape for our dynamic body.
	b2PolygonShape dynamicBox;
	dynamicBox.SetAsBox(size.x/2/PTM_RATIO, size.y/2/PTM_RATIO);
	
	// Define the dynamic body fixture.
	b2FixtureDef fixtureDef;
	fixtureDef.shape = &dynamicBox;	
	fixtureDef.density = dens;
	fixtureDef.friction = f;
	fixtureDef.restitution = rest;
	body->CreateFixture(&fixtureDef);
}

The method above handles the Box2D object creation. It is standard code for creating a Box2D object. Please refer to Beginning Box2D Physics Engine if you do not understand the code above.

Let’s Continue…

Now, let’s put everything together.

-(id) init {
	if( (self=[super init])) {
		b2Vec2 gravity = b2Vec2(0.0f, -9.8f);
		bool doSleep = true;
		world = new b2World(gravity, doSleep);
		
		m_debugDraw = new GLESDebugDraw(PTM_RATIO);
		world->SetDebugDraw(m_debugDraw);
		uint32 flags = 0;
		flags += b2DebugDraw::e_shapeBit;
		m_debugDraw->SetFlags(flags);
		
		
		[self addScrollingBackgroundWithTileMap];
		[self drawCollisionTiles];
		
		CCSprite *sprite = [CCSprite spriteWithFile:@"Icon-Small.png"];
		sprite.position = ccp(100.0f, 180.0f);
		
		[self addChild:sprite];
		
		b2BodyDef playerBodyDef;
		playerBodyDef.type = b2_dynamicBody;
		playerBodyDef.position.Set(sprite.position.x/PTM_RATIO, sprite.position.y/PTM_RATIO);
		playerBodyDef.userData = sprite;
		
		playerBody = world->CreateBody(&playerBodyDef);
		
		b2CircleShape circleShape;
		circleShape.m_radius = 1.0f;
		
		b2FixtureDef fixtureDef;
		fixtureDef.shape = &circleShape;
		fixtureDef.density = 1.0f;
		fixtureDef.friction = 0.0f;
		fixtureDef.restitution =  0.0f;
		playerBody->CreateFixture(&fixtureDef);
		
		b2Vec2 impulse = b2Vec2(10, 0);
		playerBody->ApplyLinearImpulse(impulse, playerBody->GetWorldCenter());		
		
		[self scheduleUpdate];
	}
	return self;
}

First, in the code above, it creates the Box2D world and sets up the debug drawing. It then calls addScrollingBackgroundWithTileMap and drawCollisionTiles to load the tile map and then creates the Box2D objects from the Collision layer. I used the small Cocos2D logo as the sprite for the player object. Standard Box2D object creation code follows, and then an impulse force is applied to the circle to make it move to the right of the screen. Set the impulse force to a bigger value to speed up the scroll speed.

- (void) update:(ccTime)dt {
	//It is recommended that a fixed time step is used with Box2D for stability
	//of the simulation, however, we are using a variable time step here.
	//You need to make an informed choice, the following URL is useful
	//http://gafferongames.com/game-physics/fix-your-timestep/
	
	int32 velocityIterations = 8;
	int32 positionIterations = 1;
	
	// Instruct the world to perform a single step of simulation. It is
	// generally best to keep the time step and iterations fixed.
	world->Step(dt, velocityIterations, positionIterations);
	
	
	//Iterate over the bodies in the physics world
	for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) {
		if (b->GetUserData() != NULL) {
			//Synchronize the AtlasSprites position and rotation with the corresponding body
			CCSprite *myActor = (CCSprite*)b->GetUserData();
			myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, 
					   b->GetPosition().y * PTM_RATIO);
			myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
		}	
	}	
	
	b2Vec2 pos = playerBody->GetPosition();
	CGPoint newPos = ccp(-1 * pos.x * PTM_RATIO + 50, self.position.y * PTM_RATIO);	
	[self setPosition:newPos];
}

Again, the block of code above is standard Box2D code to simulate the physics world for a slice of time, updating the sprite position and rotation to the value calculated by the Box2D physics engine. Again, remember to set the friction value to something other than 0.0 to see the circle rotate and roll across the screen instead of gliding. The code to actually scroll the background is the last three lines of the method. First, we get the location of the circle in the Box2D world. Please note that I said Box2D world. The value needs to be converted into pixels because we are setting the new position for a Cocos2D node, so they are multiplied by the constant PTM_RATIO. The “y” position is used as is, but the “x” position is updated to scroll the screen. The screen is scrolling from right to left, so it is multiplied by negative one. Then a buffer value of 50 is added to move the circle slightly to the right of the screen. If you do not add the buffer value, then the circle will be flushed all the way to the left of the screen with the left half of the circle clipped and only the right half the circle showing.

Sample Code

I hope this has been helpful to someone. And here is the sample code.

Happy coding!

Jan 072011
 

What Is It?

Cocos2d comes with two physics engines. One is called Chipmunk and the other is called Box2d. Chipmunk sounds cuter and is written in C, whereas Box2D is written in C++. I chose to learn Box2d because it seems to be the more popular and mature of the two. Instead of trying to explain what a physics engine is, it is probably better for you to read the full definition on Wikipedia. Pretty cool! So how does Box2d physics engine work with Cocos2D? It is fairly simple. The way Box2d interacts with Cocos2d is that you update the positions of your Cocos2d objects with the positions calculated by Box2d.

Getting Started

When you create a new Cocos2d project, there is an application template called “cocos2d box2d Application”. If you select that, you will have a sample Box2D Cocos2d application that you can compile and test right away. I am going to try to deconstruct the sample Box2d application, attempting to explain the individual pieces of the code. In the sample application, you touch the screen and a box is created at the location that you touch the screen. The box falls to the ground following the law of gravity and also interacts with the ground and other boxes. Take a look at the image to your left or the video of the application in action below.

Cocos2D Box2D Sample App from Min K on Vimeo.

Definitions and Idiosyncrasies

Here are some terms that you will see being used when people talk about Box2D and physics engines. Along with the definitions are a few things you should know about Box2d before you get started. It is probably a good idea to at least skim over them. You can find most of the definitions on the Box2d web site, but I will include it here for convenience.

Shape is a 2 dimensional geometrical object, such as a circle, square or a polygon.

Rigid bodies are physics objects that are, well, rigid or stiff. They do not change shape (like being squish-able).

Dynamic bodies are objects that interact with objects in the world. They have attributes such as density, friction and restitution.

Static bodies are objects that are the opposite of dynamic bodies. Objects that are static bodies do not move. Static bodies only collide with dynamic bodies and do not collide with other static bodies.

Constraint is a physical connection that removes degrees of freedom from bodies. In 2 dimensions, a body has 3 degrees of freedom (two translation coordinates and one rotating coordinate). If we take a body and pin it to the wall (like a pendulum) we have constrained the body to the wall. At this point the body can only rotate about the pin, so the constraint has removed 2 degrees of freedom.

Fixture binds a shape to a body and adds material properties such as density, friction, and restitution (body is a parent of a fixture).

Friction determines how slippery the object is when moving over a surface.

Density or mass determines the weight of the object. The heavier it is, the harder it is to move, for example.

Restitution determines the bounciness of the object.

Joints can be used to connect together bodies.

Joint limit restricts the range of motion of a joint. For example, the human elbow allows a certain range of angles.

Joint motor drives the motion of the connected bodies according to the joint’s degrees of freedom. For example, you can use a motor to drive the rotation of an elbow.

Sleeping bodies are bodies that have come to rest and do not need to be processed by the physics engine.

World means a physics world with a collection of bodies, fixtures and constraints that interact together. Box2D supports the creation of multiple worlds, but this is usually not necessary or desirable.

Here are some things to keep in mind about Box2D.

  1. Box2d does all its maths using the metric system (meters, kilograms, seconds aka MKS units).
  2. Box2d uses radians for angles.
  3. Box2d works best with objects that are between 0.1 and 10 meters. But it is the most optical if you keep your game objects close to 1 meter as possible.
  4. You’ll notice a constant called PTM_RATIO (value is 32) in the sample. You can use it to convert pixels to meters (divide by PTM_RATIO) and meters to pixels (multiply by PTM_RATIO). You can set PTM_RATIO to any value you want, but you should pick a good value so that when divided by it, the resulting value will be no less than 0.1 meters and no more than 10 meters (and optimally close to 1 meter as possible).
  5. Box2d is C++, so make sure to use C++ memory allocation and deallocations. If you are mixing Objective-C with C++ code, the file extension must be .mm (not .m).

Breakdown of the Code

You can find the complete code further down the screen. Here, I’m going to explain the code in pieces so that it is more readable and manageable.

@interface HelloWorld : CCLayer {
	b2World* world;
	GLESDebugDraw *m_debugDraw;
}

The block of code above should be in the header. We declare a b2World pointer and a GLESDebugDraw pointer. The b2World pointer is the physics world that will hold all the physics objects. GLESDebugDraw allows any Box2D bodies to be shown (i.e. outlined), instead of using a sprite. It is a useful verification and debugging tool. When you are ready for production, you want to get rid of DebugDraw and use your real art work.

// Define the gravity vector.
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
	
// Do we want to let bodies sleep?
// This will speed up the physics simulation
bool doSleep = true;
		
// Construct a world object, which will hold and simulate the rigid bodies.
world = new b2World(gravity, doSleep);
		
world->SetContinuousPhysics(true);

You will see the block of code above inside the init method. First, you define the gravity vector to point downward at -10.0. Recall that gravity is 9.8 meters per second squared, so we just use -10.0 to approximate gravity (negative because it is going down in the Y direction). Then, the Box2d world is created with the doSleep boolean variable is set to true, which means that bodies in the world are allowed to sleep when they come to rest. A sleeping body does not require simulation and thus does not require processing. This translates into better performance.

The Box2D manual states that SetContinuousPhysics(true) is more for testing purposes and is used for fast moving objects and continuous collision detection (CCD). The Box2D’s manual states the following regarding CCD.

By default, Box2D uses continuous collision detection (CCD) to prevent dynamic bodies from tunneling through static bodies. This is done by sweeping shapes from their old position to their new positions. The engine looks for new collisions during the sweep and computes the time of impact (TOI) for these collisions. Bodies are moved to their first TOI and then halted for the remainder of the time step.

Normally CCD is not used between dynamic bodies. This is done to keep performance reasonable. In some game scenarios you need dynamic bodies to use CCD. For example, you may want to shoot a high speed bullet at a stack of dynamic bricks. Without CCD, the bullet might tunnel through the bricks.

uint32 flags = 0;
flags += b2DebugDraw::e_shapeBit;
m_debugDraw->SetFlags(flags);				
		
// Define the ground body.
b2BodyDef groundBodyDef;
// Explicitly added here for clarity by me.
groundBodyDef.type = b2_staticBody;
groundBodyDef.position.Set(0, 0); // bottom-left corner
		
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world->CreateBody(&groundBodyDef);

Next, you set up the the GLESDebugDraw. You are interested in drawing shapes, so you set the bit to the e_shapeBit (some of the other available bits are e_jointBit, e_aabbBit, e_pairBit and e_centerOfMassBit — please check the reference for detailed information on them). You then define the body object for the ground and set the position. Remember that (0, 0) is the lower-left corner in Cocos2d and Box2D. You then call the factory method to create the body by calling the CreateBody function, passing in the reference to the b2BodyDef so that it can fill in the values. The ground is a static body (as opposed to a dynamic body). Remember, a static body do not collide with each other and are immovable. A body is static when it has zero mass. Bodies have zero mass by default, so it is static by default. The line “groundBodyDef.type = b2_staticBody;” is not in the code when you create the sample Box2D application from the template, but I explicitly added it here for clarity. After creating the body, you must create the fixture, which is done with the code below.

// Define the ground box shape.
b2PolygonShape groundBox;		
		
// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
		
// top
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), 
                 b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);
		
// left
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox,0);
		
// right
groundBox.SetAsEdge(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), 
                   b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);

The code above creates the fixtures to attach to the body. Recall that a fixture must be a child to a body and that the width and height are divided by PTM_RATIO to convert pixels to meters. We create a fixture for each side of the box (top, bottom, left and right) and attach it to the body. The second parameter to CreateFixture denotes the density (which is 0). Here, you passed the shape to the CreateFixture function, but there is another way to create a fixture. There is an overloaded CreateFixture function that takes a b2FixtureDef instead.

// Note: Not part of the sample code -- just here for illustration
b2FixtureDef groundFixtureDef;
groundFixtureDef.type = b2_staticBody;
groundFixtureDef.position.Set(0, 0);

// Then you pass the reference to groundFixtureDef to CreateFixture instead.
// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBox.CreateFixture(&groundFixtureDef);
...
// do similar for top, left and right

The rest of the code in init is normal Cocos2D code — it adds a sprite image, adds a label and then schedules a timer to call the method called tick. Now, for the rest of the code.

-(void) draw {
   glDisable(GL_TEXTURE_2D);
   glDisableClientState(GL_COLOR_ARRAY);
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
   world->DrawDebugData();
	
   // restore default GL states
   glEnable(GL_TEXTURE_2D);
   glEnableClientState(GL_COLOR_ARRAY);
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}

The draw method is where OpenGL calls take place. This is where you call the DrawDebugData() function (for GLESDebugDraw). OpenGL is a state based, so you need to disable certain states before DrawDebugData() is called and re-enable them after. You don’t have to know the details. This is the code provided by the template, so it is enough to copy/paste the code above and simply remember to disable and enable them as done above.

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
   //Add a new body/atlas sprite at the touched location
   for( UITouch *touch in touches ) {
      CGPoint location = [touch locationInView: [touch view]];
      location = [[CCDirector sharedDirector] convertToGL: location];
      [self addNewSpriteWithCoords: location];
   }
}

The method above is triggered when the screen is touched. When the screen is touched, it calls the addNewSpriteWithCoords method, passing in the location of the screen that was touched. The addNewSpriteWithCoords method creates a box at that location. The box that is created with be simulated by the Box2D physics engine (meaning, it will fall at 10.0 (or 9.8) meters or second squared.

-(void) addNewSpriteWithCoords:(CGPoint)p {
   CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y);
   CCSpriteBatchNode *batch = (CCSpriteBatchNode*) [self getChildByTag:kTagBatchNode];
 
   //We have a 64x64 sprite sheet with 4 different 32x32 images.  The following code is
   //just randomly picking one of the images
   int idx = (CCRANDOM_0_1() > .5 ? 0:1);
   int idy = (CCRANDOM_0_1() > .5 ? 0:1);
   CCSprite *sprite = [CCSprite spriteWithBatchNode:batch 
                                rect:CGRectMake(32 * idx,32 * idy,32,32)];
   [batch addChild:sprite];
 
   sprite.position = ccp( p.x, p.y);
 
   // Define the dynamic body.
   //Set up a 1m squared box in the physics world
   b2BodyDef bodyDef;
   bodyDef.type = b2_dynamicBody;
 
   bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
   bodyDef.userData = sprite;
   b2Body *body = world->CreateBody(&bodyDef);
 
   // Define another box shape for our dynamic body.
   b2PolygonShape dynamicBox;
   dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
 
   // Define the dynamic body fixture.
   b2FixtureDef fixtureDef;
   fixtureDef.shape = &dynamicBox;	
   fixtureDef.density = 1.0f;
   fixtureDef.friction = 0.3f;
   body->CreateFixture(&fixtureDef);
}

As mentioned, the method above is called when the screen is touched. First, the Cocos2D sprite is created at the location of the touch. Then, a Box2D body is created in order to simulate the physics for that object. It is defined as a dynamic body (b2_dynamicBody). We let Box2D know the position (in meters) and then create the body. A fixture is created with a shape, density and friction and attached to the body. The userData object is a void pointer that can point to any user defined object. Here we set it to the Cocos2d Sprite object for use later. Note that, here, we use the alternative way to create the fixture from b2FixtureDef as explained earlier.

-(void) tick: (ccTime) dt {
   //It is recommended that a fixed time step is used with Box2D for stability
   //of the simulation, however, we are using a variable time step here.
   //You need to make an informed choice, the following URL is useful
   //http://gafferongames.com/game-physics/fix-your-timestep/
 
   int32 velocityIterations = 8;
   int32 positionIterations = 1;
 
   // Instruct the world to perform a single step of simulation. It is
   // generally best to keep the time step and iterations fixed.
   world->Step(dt, velocityIterations, positionIterations);
 
 
   //Iterate over the bodies in the physics world
   for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) {
      if (b->GetUserData() != NULL) {
         //Synchronize the AtlasSprites position and rotation with the corresponding body
	 CCSprite *myActor = (CCSprite*)b->GetUserData();
	 myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, 
                                    b->GetPosition().y * PTM_RATIO);
	 myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
      }	
   }
}

The tick method above is the method that was scheduled in the init method. You will notice the world->Step() call. As noted in the comment, the Step() function is used to perform a single step of the simulation. You pass in the values for velocity iterations and position iterations as the parameters. These values are used by the physics engine to simulate the physics equations. The higher these numbers are, the better (more accurate) the simulations are, but the tradeoff is performance. So play around with these values to find the right value for your needs. We loop through the list of bodies in the physics world and then update the position and rotation of the sprites (which was stored in userData pointer) with the value that is calculated by Box2D.

- (void)accelerometer:(UIAccelerometer*)accelerometer 
         didAccelerate:(UIAcceleration*)acceleration {	
   
   static float prevX=0, prevY=0;
	
   //#define kFilterFactor 0.05f
   #define kFilterFactor 1.0f	// don't use filter. the code is here just as an example
	
   float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
   float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
	
   prevX = accelX;
   prevY = accelY;
	
   // accelerometer values are in "Portrait" mode. Change them to Landscape left
   // multiply the gravity by 10
   b2Vec2 gravity( -accelY * 10, accelX * 10);
	
   world->SetGravity( gravity );
}

Finally, the last block of code merely transforms the accelerometer readings into a Box2D gravity vector. Notice that X and Y values are switched due to the device being in landscape mode. As you tilt your phone, you will see the boxes shifting in the tilt direction.

The End

This was a simple exercise, but hopefully it was helpful in getting your head wrapped around the fundamentals. The key ideas to take away from this lesson are that 1. Box2D (physics engine) calculates the positions of the objects and you update the game objects with the values provided by Box2D, 2. physics objects are rigid bodies that can be either static or dynamic and 3. a body can be bound to one or more fixture(s) that define material properties such as the shape, density, friction and restitution.

In a future post, I am going to try doing something a bit more exciting than the sample application. So please stay tuned!

The Complete Code

#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"

// HelloWorld Layer
@interface HelloWorld : CCLayer
{
	b2World* world;
	GLESDebugDraw *m_debugDraw;
}
// returns a Scene that contains the HelloWorld as the only child
+(id) scene;

// adds a new sprite at a given coordinate
-(void) addNewSpriteWithCoords:(CGPoint)p;
@end
#import "HelloWorldScene.h"

//Pixel to metres ratio. Box2D uses metres as the unit for measurement.
//This ratio defines how many pixels correspond to 1 Box2D "metre"
//Box2D is optimized for objects of 1x1 metre therefore it makes sense
//to define the ratio so that your most common object type is 1x1 metre.
#define PTM_RATIO 32

// enums that will be used as tags
enum {
	kTagTileMap = 1,
	kTagBatchNode = 1,
	kTagAnimation1 = 1,
};

@implementation HelloWorld

+(id) scene {
	// 'scene' is an autorelease object.
	CCScene *scene = [CCScene node];
	HelloWorld *layer = [HelloWorld node];
	[scene addChild: layer];
	return scene;
}

-(id) init {
    if( (self=[super init])) {
        // enable touches
	self.isTouchEnabled = YES;
		
	// enable accelerometer
	self.isAccelerometerEnabled = YES;
		
	CGSize screenSize = [CCDirector sharedDirector].winSize;
	CCLOG(@"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height);
		
	// Define the gravity vector.
	b2Vec2 gravity;
	gravity.Set(0.0f, -10.0f);
	
	// Do we want to let bodies sleep?
	// This will speed up the physics simulation
	bool doSleep = true;
		
	// Construct a world object, which will hold and simulate the rigid bodies.
	world = new b2World(gravity, doSleep);
		
	world->SetContinuousPhysics(true);
		
	// Debug Draw functions
	m_debugDraw = new GLESDebugDraw( PTM_RATIO );
	world->SetDebugDraw(m_debugDraw);
		
	uint32 flags = 0;
	flags += b2DebugDraw::e_shapeBit;
	m_debugDraw->SetFlags(flags);		
		
		
	// Define the ground body.
	b2BodyDef groundBodyDef;
	groundBodyDef.position.Set(0, 0); // bottom-left corner
		
	// Call the body factory which allocates memory for the ground body
	// from a pool and creates the ground box shape (also from a pool).
	// The body is also added to the world.
	b2Body* groundBody = world->CreateBody(&groundBodyDef);
		
	// Define the ground box shape.
	b2PolygonShape groundBox;		
		
	// bottom
	groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
	groundBody->CreateFixture(&groundBox,0);
		
	// top
	groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), 
                       b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
	groundBody->CreateFixture(&groundBox,0);
		
	// left
	groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
	groundBody->CreateFixture(&groundBox,0);
		
	// right
	groundBox.SetAsEdge(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), 
                          b2Vec2(screenSize.width/PTM_RATIO,0));
	groundBody->CreateFixture(&groundBox,0);
		
		
	//Set up sprite
	CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:@"blocks.png" 
                                          capacity:150];
	[self addChild:batch z:0 tag:kTagBatchNode];
		
	[self addNewSpriteWithCoords:ccp(screenSize.width/2, screenSize.height/2)];
		
	CCLabelTTF *label = [CCLabelTTF labelWithString:@"Tap screen" 
                                          fontName:@"Marker Felt" fontSize:32];
	[self addChild:label z:0];
	[label setColor:ccc3(0,0,255)];
	label.position = ccp( screenSize.width/2, screenSize.height-50);
	
	[self schedule: @selector(tick:)];
    }
    return self;
}

-(void) draw {
   glDisable(GL_TEXTURE_2D);
   glDisableClientState(GL_COLOR_ARRAY);
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
   world->DrawDebugData();
	
   // restore default GL states
   glEnable(GL_TEXTURE_2D);
   glEnableClientState(GL_COLOR_ARRAY);
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}

-(void) addNewSpriteWithCoords:(CGPoint)p {
   CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y);
   CCSpriteBatchNode *batch = (CCSpriteBatchNode*) [self getChildByTag:kTagBatchNode];
	
   //We have a 64x64 sprite sheet with 4 different 32x32 images.  The following code is
   //just randomly picking one of the images
   int idx = (CCRANDOM_0_1() > .5 ? 0:1);
   int idy = (CCRANDOM_0_1() > .5 ? 0:1);
   CCSprite *sprite = [CCSprite spriteWithBatchNode:batch 
                                rect:CGRectMake(32 * idx,32 * idy,32,32)];
   [batch addChild:sprite];
	
   sprite.position = ccp( p.x, p.y);
	
   // Define the dynamic body.
   //Set up a 1m squared box in the physics world
   b2BodyDef bodyDef;
   bodyDef.type = b2_dynamicBody;

   bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
   bodyDef.userData = sprite;
   b2Body *body = world->CreateBody(&bodyDef);
	
   // Define another box shape for our dynamic body.
   b2PolygonShape dynamicBox;
   dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
	
   // Define the dynamic body fixture.
   b2FixtureDef fixtureDef;
   fixtureDef.shape = &dynamicBox;	
   fixtureDef.density = 1.0f;
   fixtureDef.friction = 0.3f;
   body->CreateFixture(&fixtureDef);
}



-(void) tick: (ccTime) dt {
   //It is recommended that a fixed time step is used with Box2D for stability
   //of the simulation, however, we are using a variable time step here.
   //You need to make an informed choice, the following URL is useful
   //http://gafferongames.com/game-physics/fix-your-timestep/
	
   int32 velocityIterations = 8;
   int32 positionIterations = 1;
	
   // Instruct the world to perform a single step of simulation. It is
   // generally best to keep the time step and iterations fixed.
   world->Step(dt, velocityIterations, positionIterations);

	
   //Iterate over the bodies in the physics world
   for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) {
      if (b->GetUserData() != NULL) {
         //Synchronize the AtlasSprites position and rotation with the corresponding body
	 CCSprite *myActor = (CCSprite*)b->GetUserData();
	 myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, 
                                    b->GetPosition().y * PTM_RATIO);
	 myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
      }	
   }
}

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
   //Add a new body/atlas sprite at the touched location
   for( UITouch *touch in touches ) {
      CGPoint location = [touch locationInView: [touch view]];
      location = [[CCDirector sharedDirector] convertToGL: location];
      [self addNewSpriteWithCoords: location];
   }
}

- (void)accelerometer:(UIAccelerometer*)accelerometer 
         didAccelerate:(UIAcceleration*)acceleration {	
   
   static float prevX=0, prevY=0;
	
   //#define kFilterFactor 0.05f
   #define kFilterFactor 1.0f	// don't use filter. the code is here just as an example
	
   float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
   float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
	
   prevX = accelX;
   prevY = accelY;
	
   // accelerometer values are in "Portrait" mode. Change them to Landscape left
   // multiply the gravity by 10
   b2Vec2 gravity( -accelY * 10, accelX * 10);
	
   world->SetGravity( gravity );
}

- (void) dealloc {
   // in case you have something to dealloc, do it in this method
   delete world;
   world = NULL;
	
   delete m_debugDraw;

   // don't forget to call "super dealloc"
   [super dealloc];
}
@end
Jan 032011
 

Happy New Year everyone! I started off 2011 with a visit to the doctor’s office. I may as well start the new year on a healthy foot! My New Years resolution is 1. to be healthy (and nag loved ones to lead a more healthy life) and 2. to release a hit iPhone game. I also had a chance to read through the new book on Cocos2d that I mentioned in my previous blog post.

I recently discovered a program that enables non-programmers to author iPhone games using a drag and drop interface, without nary a worry about programming. It will make about 60-70% of the easy stuff very easy to do, but the remaining 30-40% is probably painful to achieve without a lot of workarounds — workarounds that would have been unnecessary if you had programmed it instead. I will say that it is probably great for the simple stuff and for rapid prototyping.

On the other end spectrum are those who are a glutton for pain and insist on doing everything from scratch. If you are making a game on the iPhone, this usually means delving into the mysteries of OpenGL ES. My salute to those brave souls who travel the path less traveled by, but for mere mortals such as myself, I would opt for the middle-way and use a robust and mature library like Cocos2d.

Cocos2d is a wonderful 2d game library. The blood of OpenGL ES is flowing rigorously underneath the hood so you get the performance benefits of OpenGL ES while being shielded from having to touch it directly. To date, there are thousands of games made with Cocos2d, including many of the top free and paid games on the Apple App store.

It can be a daunting task to learn anything related to game programming if you have never attempted it before. It is as much a science as it is an art. And with an open source library such as Cocos2d, it is that much more important for a mentor to hold your hand and show you the way to the path of enlightenment. Notice I said to the “path” of enlightenment and not to enlightenment itself. That task belongs to only you! There are not too many books on the subject in the market, and the book Cocos2d for iPhone 0.99 Beginner’s Guide is a welcome addition to any aspiring iPhone game programmer’s library.

As the title of the book suggests, the author assumes that the reader is a beginning game developer (not necessarily a beginning iPhone developer). The book will take you through building 3 complete iPhone games, each of which is of a different genre. The first game is a puzzle, the second a vertical shooter, and the third is a physics based game using the Chipmunk physics engine. Readers will be able to take away useful information from each of these games, as different genre of games will require a different mentality and programming skill set. The author also lists some links to games that come with complete source code, which I found very useful as well.

It will give you a fairly complete overview of the Cocos2d engine. The chapters are organized in a logical manner, and each chapter builds on the previous chapters. I particularly liked the chapter about the particle system in Cocos2d. Readers will also appreciate the fact that the author introduces the tools that are commonly used by Cocos2d programmers. These tools will prove to be an invaluable time saver, and you will immediately recognize the benefits of using them.

The book is written in a fairly clear manner and does a good job of explaining the code. For example, just when you start to feel lost, you will be happy to run across sections titled “What Just Happened?” that will explain the code for you in plain English.

It did, however, seem slightly rushed. I also did not particularly care for the formatting of the source code in the book and wished more care was taken with them. It must be noted that I reviewed the PDF version of the book, so I cannot speak for the printed version. These imperfections only slightly mar the usefulness of the book. And those just starting out with Cocos2d will certainly learn a great deal from the book, which is the target demographic of the book (as it is obvious and evident from the title of the book).

 Posted by at 12:57 pm  Tagged with: