Pages

Thursday, April 4, 2013

xCode Car Race with Box2d

Finally i have created Objective C based car race from the Javascript reference link
http://www.codekites.com/make-racing-car-box2d-javascript/ by - Silver Moon

My output will be like this

Right One is Accelerator and Left one is Break with reverser :)

So here are the steps. 
Steps:

1. You have to install cocos2d in Mac. You can take help from below link to install Box2d in xcode.
    
     http://www.raywenderlich.com/25736/how-to-make-a-simple-iphone-game-with-cocos2d-2-x-tutorial

2. Coding Part :

Create default box2d project

3. Go to HelloWorldLayer.h and add following variables


@property (nonatomic) float max_steer_angle;
@property (nonatomic) float steering_angle;
@property (nonatomic) float steer_speed;
@property (nonatomic) float defaultCarSpeed;


4. Now go to HelloWorldLayer.mm and delete code inside init method and put the code as below
Also Import 

#import "Car.h"
#import "ClickableSprite.h"




@interface HelloWorldLayer()

@property (nonatomic) Car *car;//Add this 

Now - 



-(id) init
{
if( (self=[super init])) {
// enable events
self.car = [[Car alloc] init];
        self.startTrack = NO;
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
        max_steer_angle = M_PI/3;
        steer_speed = 1;
        steering_angle = 0;//-max_steer_angle;
        
CGSize s = [CCDirector sharedDirector].winSize;
[self createMenu];
// init physics
[self initPhysics];
        CGPoint p = CGPointMake(s.width/2-20, 230);
        [self addCarBody:p];
        
[self scheduleUpdate];
}
return self;
}


5. Now add following code in createMenu function 

//Accelerator
    ClickableSprite *accelerator = [ClickableSprite spriteWithFile:@"accelerator.png" rect:CGRectMake(0, 0, 73, 140)];
    accelerator.target = self;
    accelerator.selector = @selector(accelerateCar);
    accelerator.selectorEnd = @selector(accelerateCarStop);
    accelerator.clickableSize = CGSizeMake(73, 140);
    accelerator.touchPriority = 2;
    accelerator.position = ccp(size.width-200, 100);
    [self addChild:accelerator z:1];
    
    //Reverse
    ClickableSprite *reversor = [ClickableSprite spriteWithFile:@"accelerator.png" rect:CGRectMake(0, 0, 73, 140)];
    reversor.target = self;
    reversor.selector = @selector(reverseCar);
    reversor.selectorEnd = @selector(reverseCarStop);
    reversor.clickableSize = CGSizeMake(73, 140);
    reversor.touchPriority = 2;
    reversor.position = ccp(100, 90);
    [self addChild:reversor z:1];

6. Now add some following functions

-(void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
    if (acceleration.y > 0) {
        [self moveLeft:acceleration.y];
    } else if (acceleration.y < 0) {
        [self moveRight:acceleration.y];
    }
}

-(void) accelerateCar
{
    self.car.gear = 1;
    [self.car start_engine];
}
-(void) accelerateCarStop
{
    self.car.gear = 0;
    [self.car stop_engine];
}

-(void) reverseCar
{
    self.car.gear = -1;
    [self.car start_engine];
}
-(void) reverseCarStop
{
    self.car.gear = 0;
    [self.car stop_engine];
}

7. Now change the gravity in initPhysics function

gravity.Set(0.0f, 0.0f);


8. Now add updateCar in draw function

-(void) draw
{

    [self updateCar];
}



9. add updateCar function

-(void) updateCar
{
    //NSLog(@" - > : %c", self.startTrack);
    if(self.startTrack == YES)
    {
        world->Step(1/60, 8, 3);
        
        world->ClearForces();
        
        //Driving
        b2Vec2 direction = self.car.left_wheel->GetTransform().q.GetYAxis();
        direction.operator*=(self.car.engine_speed);
        self.car.left_wheel->ApplyForce(direction, self.car.left_wheel->GetPosition());
        
        direction = self.car.right_wheel->GetTransform().q.GetYAxis();
        direction.operator*=(self.car.engine_speed);
        self.car.right_wheel->ApplyForce(direction, self.car.right_wheel->GetPosition());
        
        //Steering
        float angle = steering_angle - self.car.leftWheelJoint->GetJointAngle();
        self.car.leftWheelJoint->SetMotorSpeed(angle*steer_speed);
        
        angle = steering_angle - self.car.rightWheelJoint->GetJointAngle();
        self.car.rightWheelJoint->SetMotorSpeed(angle*steer_speed);
    }
}


10. Now add following functions


-(void) addCarBody:(CGPoint)p
{
    CCNode *parent = [self getChildByTag:kTagParentNode];
    //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);
    PhysicsSprite *sprite = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx,32 * idy,32,32)];
    [parent 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.linearDamping = 10.0;
    bodyDef.angularDamping = 10.0;
    bodyDef.gravityScale = 1.0;
    b2Body *body = world->CreateBody(&bodyDef);
    // Define another box shape for our dynamic body.
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox(1.0f, 2.0f);//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.0f;
    fixtureDef.restitution = 0.2f;
    body->CreateFixture(&fixtureDef);
    
    [sprite setPhysicsBody:body];
    
    //Left Wheel Start Here
    PhysicsSprite *left_wheel = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx, 32 * idy, 32, 32)];
[parent addChild:left_wheel];
    p.x -= 30;
    p.y += 40;
    b2Body *leftwheelB = [self createBox:p wd:0.2f ht:0.35f];
    [left_wheel setPhysicsBody:leftwheelB];
    
    left_wheel.position = ccp(p.x, p.y);
    
    b2RevoluteJointDef joint_def_left;
    joint_def_left.Initialize(body, leftwheelB, leftwheelB->GetWorldCenter());
    joint_def_left.enableMotor = YES;
    joint_def_left.maxMotorTorque = 100000;
    
    joint_def_left.enableLimit = YES;
    joint_def_left.lowerAngle = -1*M_PI/3;
    joint_def_left.upperAngle = M_PI/3;
    
    self.car.leftWheelJoint = (b2RevoluteJoint*)world->CreateJoint(&joint_def_left);
    
    //Right Wheel Start Here
    PhysicsSprite *right_wheel = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx, 32 * idy, 32, 32)];
    [parent addChild:right_wheel];
    p.x += 60;//35 before and 35 more so 70
    b2Body *rightwheelB = [self createBox:p wd:0.2f ht:0.35f];
    [right_wheel setPhysicsBody:rightwheelB];
    right_wheel.position = ccp(p.x, p.y);  
        
    b2RevoluteJointDef joint_def_right;
    joint_def_right.Initialize(body, rightwheelB, rightwheelB->GetWorldCenter());
    joint_def_right.enableMotor = YES;
    joint_def_right.maxMotorTorque = 100000;
    
    joint_def_right.enableLimit = YES;
    joint_def_right.lowerAngle = -1*M_PI/3;
    joint_def_right.upperAngle = M_PI/3;
    
    self.car.rightWheelJoint = (b2RevoluteJoint *)world->CreateJoint(&joint_def_right);
    
    //Left Bottom Wheel Start Here
    PhysicsSprite *left_rear_wheel = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx, 32 * idy, 32, 32)];
[parent addChild:left_rear_wheel];
    p.x -= 60;
    p.y -= 80;
    b2Body *leftRearWheelB = [self createBox:p wd:0.2f ht:0.35f];
    [left_rear_wheel setPhysicsBody:leftRearWheelB];
    left_rear_wheel.position = ccp(p.x, p.y);
    
    b2PrismaticJointDef joint_def_left_rear;
    joint_def_left_rear.Initialize(body, leftRearWheelB, leftRearWheelB->GetWorldCenter(), b2Vec2(1,0));
    
    joint_def_left_rear.enableLimit = YES;
    joint_def_left_rear.lowerTranslation = 0;
    joint_def_left_rear.upperTranslation = 0;
    
    world->CreateJoint(&joint_def_left_rear);
    
    //Right Bottom Wheel Start Here
    PhysicsSprite *right_rear_wheel = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx, 32 * idy, 32, 32)];
[parent addChild:right_rear_wheel];
    p.x += 60;//35 before and 35 more so 70
    //p.y += 130;// same as above 65 before and 65 now so 0
    b2Body *rightRearWheelB = [self createBox:p wd:0.2f ht:0.35f];
    [right_rear_wheel setPhysicsBody:rightRearWheelB];
    right_rear_wheel.position = ccp(p.x, p.y);
    
    b2PrismaticJointDef joint_def_right_rear;
    joint_def_right_rear.Initialize(body, rightRearWheelB, rightRearWheelB->GetWorldCenter(), b2Vec2(1,0));
    
    joint_def_right_rear.enableLimit = YES;
    joint_def_right_rear.lowerTranslation = 0;
    joint_def_right_rear.upperTranslation = 0;
    
    self.car.carBody = body;
    self.car.left_wheel = leftwheelB;
    self.car.right_wheel = rightwheelB;
    self.car.left_rear_wheel = leftRearWheelB;
    self.car.right_rear_wheel = rightRearWheelB;
    
    world->CreateJoint(&joint_def_right_rear);
      
    [self.car stop_engine];
    self.startTrack = YES;
}

-(b2Body *) createBox :(CGPoint)p wd:(float)wd ht:(float)ht
{
    b2BodyDef wheelDef;
    wheelDef.type = b2_dynamicBody;
    wheelDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
    b2Body *wheelbody = world->CreateBody(&wheelDef);
    // Define another box shape for our dynamic body.
    b2PolygonShape dynamicBoxWheel;
    dynamicBoxWheel.SetAsBox(wd, ht);//These are mid points for our 1m box
    // Define the dynamic body fixture.
    b2FixtureDef fixtureDefWheel;
    fixtureDefWheel.shape = &dynamicBoxWheel;
    fixtureDefWheel.density = 2.0f;
    fixtureDefWheel.friction = 0.3f;
    wheelbody->CreateFixture(&fixtureDefWheel);
    
    return wheelbody;
}


11. Now Move Right and Left

-(void) moveRight
{
    steering_angle = -max_steer_angle;
    steer_speed = 1;
}
-(void) moveLeft
{
    steering_angle = max_steer_angle;
    steer_speed = 1;
}

12. Now Add Car.h and Car.mm
Car.h Code: 
#import <Foundation/Foundation.h>
#import "PhysicsSprite.h"

@interface Car : PhysicsSprite
@property (nonatomic) float top_engine_speed;
@property (nonatomic) bool engineOn;
@property (nonatomic) float gear;
@property (nonatomic) float engine_speed;
@property (nonatomic, assign) b2Body *left_wheel;
@property (nonatomic, assign) b2Body *right_wheel;
@property (nonatomic, assign) b2Body *left_rear_wheel;
@property (nonatomic, assign) b2Body *right_rear_wheel;
@property (nonatomic) b2RevoluteJoint *leftWheelJoint;
@property (nonatomic) b2RevoluteJoint *rightWheelJoint;
@property (nonatomic, assign) b2Body *carBody;
-(id) init;
-(void) start_engine;
-(void) stop_engine;
@end

Car.mm Code: 
#import "Car.h"

@implementation Car
@synthesize top_engine_speed, engineOn, gear, engine_speed;
@synthesize left_wheel, right_wheel, left_rear_wheel, right_rear_wheel, carBody, leftWheelJoint, rightWheelJoint;
-(id) init
{
    top_engine_speed = 240.0;
    engineOn = NO;
    gear = 1.0;
    return self;
}

-(void) start_engine
{
    engineOn = YES;
    engine_speed = gear * top_engine_speed;
}
-(void) stop_engine
{
    engineOn = NO;
    engine_speed = 0;
}

@end

13. Now Add ClickableSprite.h and ClickableSprite.mm
ClickableSprite.h Code: 

#import "CCSprite.h"

@interface ClickableSprite : CCSprite <CCTargetedTouchDelegate>

@property (nonatomic, assign) id<NSObject> target;
@property (nonatomic) SEL selector;
@property (nonatomic) SEL selectorEnd;
@property (nonatomic) CGSize clickableSize;
@property (nonatomic) int touchPriority;

@end

ClickableSprite.mm Code:
#import "ClickableSprite.h"

@implementation ClickableSprite

- (BOOL)containsTouchLocation:(UITouch *)touch {
    CGPoint p = [self convertTouchToNodeSpaceAR:touch];
    CGSize size = self.contentSize;
    if(!CGSizeEqualToSize(self.clickableSize, CGSizeZero))
        size = self.clickableSize;
    CGRect r = CGRectMake(-size.width*0.5, -size.height*0.5, size.width, size.height);
    return CGRectContainsPoint(r, p);
}

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    NSLog(@"Tound Began");
    [self.target performSelector:self.selector withObject:self];
    return [self containsTouchLocation:touch];
}

- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
    if(![self containsTouchLocation:touch])
        return;
    NSLog(@"Tound End");
    [self.target performSelector:self.selectorEnd withObject:self];
}

- (void)onEnter {
    [super onEnter];
    CCDirector *director = [CCDirector sharedDirector];
    [[director touchDispatcher] addTargetedDelegate:self priority:self.touchPriority swallowsTouches:YES];
}

- (void)onExit {
    CCDirector *director = [CCDirector sharedDirector];
    [[director touchDispatcher] removeDelegate:self];
    [super onExit];
}
@end


You can Contact me also to get these files. If any issue. I will love to help.