Wednesday, 15 February 2012

Box2d Demo


 I am currently doing all the motion in my game using actions - and using Chipmunk for collision detection. But for fun, I have started second guessing my choices, and have investigated Box2d.
I found this site really interesting, http://www.emanueleferonato.com/2009/04/06/two-ways-to-make-box2d-cars/?comments=true, and decided I would have a shot at converting the first example to Cocos2d. So I have modified the Box2dTest.h and Box2dTest.mm from the SVN repositry. If you want to have look, just copy and paste the following. Its not a perfect conversion, but might be of interest to someone.
If you touch the left side the car turns left. Left Top it goes left and forwards, left bottom it goes left and backwards.
Oh, and I couldn't be bothered subclassing sprite etc - so all the body bits are just properties of the motion layer - so really bad programming here :P
Box2dTest.h
//
// cocos2d
//

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

//CLASS INTERFACE
@interface AppController : NSObject <UIAlertViewDelegate, UITextFieldDelegate, UIApplicationDelegate>
{
 UIWindow *window;
}
@end

@interface Box2DTestLayer : Layer {
 b2World* world;
 GLESDebugDraw *m_debugDraw;
 b2Body *body;
 b2Body *leftWheel;
 b2Body *rightWheel;
 b2Body *leftRearWheel;
 b2Body *rightRearWheel;
 b2RevoluteJoint * leftJoint;
 b2RevoluteJoint * rightJoint;
 float engineSpeed;
 float steeringAngle;

}
-(void) addNewSpriteWithCoords:(CGPoint)p;
-(void) killOrthogonalVelocityForTarget:(b2Body *)targetBody;
@end
Box2dTest.mm
//
// Demo of calling integrating Box2D physics engine with cocos2d AtlasSprites
// a cocos2d example
// http://code.google.com/p/cocos2d-iphone
//
// by Steve Oldmeadow
//

#import "Box2dTest.h"

@implementation Box2DTestLayer

//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

enum {
 kTagTileMap = 1,
 kTagSpriteManager = 1,
 kTagAnimation1 = 1,
};

-(id) init
{
 if( (self=[super init])) {

  self.isTouchEnabled = YES;
  self.isAccelerometerEnabled = YES;

  CGSize screenSize = [Director 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, 0.0f);

  // car control
  engineSpeed = 0;
  steeringAngle = 0;

  // Do we want to let bodies sleep?
  bool doSleep = true;

  // Construct a world object, which will hold and simulate the rigid bodies.
  world = new b2World(gravity, doSleep);

  world->SetContinuousPhysics(true);

  m_debugDraw = new GLESDebugDraw( PTM_RATIO );
  world->SetDebugDraw(m_debugDraw);

  uint32 flags = 0;
  flags += b2DebugDraw::e_shapeBit;
  flags += b2DebugDraw::e_jointBit;
  flags += b2DebugDraw::e_aabbBit;
  flags += b2DebugDraw::e_pairBit;
  flags += b2DebugDraw::e_centerOfMassBit;
  m_debugDraw->SetFlags(flags);  

  //Set up sprite

  AtlasSpriteManager *mgr = [AtlasSpriteManager spriteManagerWithFile:@"blocks.png" capacity:150];
  [self addChild:mgr z:0 tag:kTagSpriteManager];

  [self addNewSpriteWithCoords:ccp(screenSize.width/2, screenSize.height/2)];

  Label *label = [Label 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) dealloc
{
 delete world;
 world = NULL;

 delete m_debugDraw;

 [super dealloc];
} 

-(void) draw
{
 [super draw];
 glEnableClientState(GL_VERTEX_ARRAY);
 world->DrawDebugData();
 glDisableClientState(GL_VERTEX_ARRAY);
}

-(void) addNewSpriteWithCoords:(CGPoint)p
{
 // define our body
 b2BodyDef bodyDef;
 bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
// bodyDef.userData = sprite;
 bodyDef.linearDamping = 1;
 bodyDef.angularDamping = 1;
 body = world->CreateBody(&bodyDef);
 body->SetMassFromShapes();

 // Front Wheels
 // Left
 b2BodyDef leftWheelDef;
 leftWheelDef.position.Set(p.x/PTM_RATIO-1.1f, p.y/PTM_RATIO-1.80f);
 leftWheel = world->CreateBody(&leftWheelDef);
 // Right
 b2BodyDef rightWheelDef;
 rightWheelDef.position.Set(p.x/PTM_RATIO+1.1f, p.y/PTM_RATIO-1.8f);
 rightWheel = world->CreateBody(&rightWheelDef);

 // Back Wheels
 // Left
 b2BodyDef leftRearWheelDef;
 leftRearWheelDef.position.Set(p.x/PTM_RATIO-1.1f, p.y/PTM_RATIO+1.8f);
 leftRearWheel = world->CreateBody(&leftRearWheelDef);
 // Right
 b2BodyDef rightRearWheelDef;
 rightRearWheelDef.position.Set(p.x/PTM_RATIO+1.1f, p.y/PTM_RATIO+1.8f);
 rightRearWheel = world->CreateBody(&rightRearWheelDef);

 // define our shapes
 b2PolygonShape boxDef;
 boxDef.SetAsBox(1.0f,2.0f);
 b2FixtureDef fixtureDef;
 fixtureDef.shape = &boxDef;
 fixtureDef.density = 1.0F;
 fixtureDef.friction = 0.3f;
 body->CreateFixture(&fixtureDef);
 body->SetMassFromShapes();

 //Left Front Wheel shape
 b2PolygonShape leftWheelShapeDef;
 leftWheelShapeDef.SetAsBox(0.2f,0.5f);
 b2FixtureDef fixtureDefLeftWheel;
 fixtureDefLeftWheel.shape = &leftWheelShapeDef;
 fixtureDefLeftWheel.density = 1.0F;
 fixtureDefLeftWheel.friction = 0.3f;
 leftWheel->CreateFixture(&fixtureDefLeftWheel);
 leftWheel->SetMassFromShapes();

 //Right Front Wheel shape
 b2PolygonShape rightWheelShapeDef;
 rightWheelShapeDef.SetAsBox(0.2f,0.5f);
 b2FixtureDef fixtureDefRightWheel;
 fixtureDefRightWheel.shape = &rightWheelShapeDef;
 fixtureDefRightWheel.density = 1.0F;
 fixtureDefRightWheel.friction = 0.3f;
 rightWheel->CreateFixture(&fixtureDefRightWheel);
 rightWheel->SetMassFromShapes();

 //Left Back Wheel shape
 b2PolygonShape leftRearWheelShapeDef;
 leftRearWheelShapeDef.SetAsBox(0.2f,0.5f);
 b2FixtureDef fixtureDefLeftRearWheel;
 fixtureDefLeftRearWheel.shape = &leftRearWheelShapeDef;
 fixtureDefLeftRearWheel.density = 1.0F;
 fixtureDefLeftRearWheel.friction = 0.3f;
 leftRearWheel->CreateFixture(&fixtureDefLeftRearWheel);
 leftRearWheel->SetMassFromShapes();

 //Right Back Wheel shape
 b2PolygonShape rightRearWheelShapeDef;
 rightRearWheelShapeDef.SetAsBox(0.2f,0.5f);
 b2FixtureDef fixtureDefRightRearWheel;
 fixtureDefRightRearWheel.shape = &rightRearWheelShapeDef;
 fixtureDefRightRearWheel.density = 1.0F;
 fixtureDefRightRearWheel.friction = 0.3f;
 rightRearWheel->CreateFixture(&fixtureDefRightRearWheel);
 rightRearWheel->SetMassFromShapes();

 b2RevoluteJointDef leftJointDef;
 leftJointDef.Initialize(body, leftWheel, leftWheel->GetWorldCenter());
 leftJointDef.enableMotor = true;
 leftJointDef.maxMotorTorque = 100;

 b2RevoluteJointDef rightJointDef;
 rightJointDef.Initialize(body, rightWheel, rightWheel->GetWorldCenter());
 rightJointDef.enableMotor = true;
 rightJointDef.maxMotorTorque = 100;

 leftJoint = (b2RevoluteJoint *) world->CreateJoint(&leftJointDef);
 rightJoint = (b2RevoluteJoint *) world->CreateJoint(&rightJointDef);

 b2Vec2 wheelAngle;
 wheelAngle.Set(1,0);

 // Join back wheels
 // Left
 b2PrismaticJointDef leftRearJointDef;
 leftRearJointDef.Initialize(body, leftRearWheel, leftRearWheel->GetWorldCenter(),wheelAngle);
 leftRearJointDef.enableLimit = true;
 leftRearJointDef.lowerTranslation = 0;
 leftRearJointDef.upperTranslation = 0;
 world->CreateJoint(&leftRearJointDef);
 // Right
 b2PrismaticJointDef rightRearJointDef;
 rightRearJointDef.Initialize(body, rightRearWheel, rightRearWheel->GetWorldCenter(),wheelAngle);
 rightRearJointDef.enableLimit = true;
 rightRearJointDef.lowerTranslation = 0;
 rightRearJointDef.upperTranslation = 0;
 world->CreateJoint(&rightRearJointDef);
}

-(void) killOrthogonalVelocityForTarget:(b2Body *)targetBody
{
 b2Vec2 localPoint;
 localPoint.Set(0,0);
 b2Vec2 velocity = targetBody->GetLinearVelocityFromLocalPoint(localPoint);

 b2Vec2 sidewaysAxis = targetBody->GetTransform().R.col2;
 sidewaysAxis *= b2Dot(velocity,sidewaysAxis);

 targetBody->SetLinearVelocity(sidewaysAxis);
 //targetBody.GetWorldPoint(localPoint));
}

-(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);

 [self killOrthogonalVelocityForTarget:leftWheel];
 [self killOrthogonalVelocityForTarget:rightWheel];
 [self killOrthogonalVelocityForTarget:leftRearWheel];
 [self killOrthogonalVelocityForTarget:rightRearWheel];

 //Driving
 b2Vec2 localPoint;
 localPoint.Set(0,0);
 b2Vec2 ldirection = leftWheel->GetTransform().R.col2;
 ldirection *= engineSpeed;
 b2Vec2 rdirection = rightWheel->GetTransform().R.col2;
 rdirection *= engineSpeed;
 leftWheel->ApplyForce(ldirection, leftWheel->GetPosition());
 rightWheel->ApplyForce(rdirection, rightWheel->GetPosition());
 //Steering
 float mspeed = steeringAngle - leftJoint->GetJointAngle();
 leftJoint->SetMotorSpeed(mspeed * 1.5F);
 mspeed = steeringAngle - rightJoint->GetJointAngle();
 rightJoint->SetMotorSpeed(mspeed * 1.5F);

 //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
   AtlasSprite* myActor = (AtlasSprite*)b->GetUserData();
   myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
   myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
  }
 }
}

- (BOOL)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
 NSLog(@"TOUCHES STARTED");
 for( UITouch *touch in touches ) {
  CGPoint location = [touch locationInView: [touch view]];

  location = [[Director sharedDirector] convertCoordinate: location];

  if (location.x > 160.0F)
   steeringAngle = 1.03F;
  else
   steeringAngle = -1.03F;
  if (location.y > 160.0F)
   engineSpeed = -40;
  else
   engineSpeed = +40;

 }
 return kEventHandled;
}

@end

// CLASS IMPLEMENTATIONS
@implementation AppController

- (void) applicationDidFinishLaunching:(UIApplication*)application
{
 // Init the window
 window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

 // cocos2d will inherit these values
 [window setUserInteractionEnabled:YES];
 [window setMultipleTouchEnabled:YES];

 // must be called before any othe call to the director
// [Director useFastDirector];

 // AnimationInterval doesn't work with FastDirector, yet
// [[Director sharedDirector] setAnimationInterval:1.0/60];
 [[Director sharedDirector] setDisplayFPS:YES];
 [[Director sharedDirector] setDeviceOrientation:CCDeviceOrientationLandscapeLeft];

 // create an openGL view inside a window
 [[Director sharedDirector] attachInView:window];

 // And you can later, once the openGLView was created
 // you can change it's properties
 [[[Director sharedDirector] openGLView] setMultipleTouchEnabled:YES];

 // Default texture format for PNG/BMP/TIFF/JPEG/GIF images
 // It can be RGBA8888, RGBA4444, RGB5_A1, RGB565
 // You can change anytime.
 [Texture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888]; 

 // add layer
 Scene *scene = [Scene node];
 id box2dLayer = [[Box2DTestLayer alloc] init];
 [scene addChild:box2dLayer z:0];
// glClearColor(1.0f,1.0f,1.0f,1.0f);

 [window makeKeyAndVisible];

 [[Director sharedDirector] runWithScene: scene];
}

- (void) dealloc
{
 [window release];
 [super dealloc];
}

// getting a call, pause the game
-(void) applicationWillResignActive:(UIApplication *)application
{
 [[Director sharedDirector] pause];
}

// call got rejected
-(void) applicationDidBecomeActive:(UIApplication *)application
{
 [[Director sharedDirector] resume];
}

// purge memroy
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
 [[TextureMgr sharedTextureMgr] removeAllTextures];
}

// next delta time will be zero
-(void) applicationSignificantTimeChange:(UIApplication *)application
{
 [[Director sharedDirector] setNextDeltaTimeZero:YES];
}

@end

0 comments:

Post a Comment

 

Copyright @ 2013 PakTechClub.