modeling box2d bodies in mekanimo

mekanimo can be used to draw compound shapes, motors, links, rails, etc., and  set their properties.  One can then export the model to c++ code.

some tips for this when modeling:

  • first remove the box container
  • zoom in to about a level of 5, assuming cocos2d PTM ratio of about 32
  • navigate to the origin to draw your model
  • first place a rectangle, set a sprite, and trace it to create the body/bodies.

A quick way to resize the texture to fit your model is to place the created model in your scene (just copy & paste the generated code), then turn on debug draw node:

Box2dDebugDraw *b2node = [Box2dDebugDraw nodeWithWorld:world];

[self addChild:b2node z:999];

Box2dDebugDraw.mm

@implementation Box2dDebugDraw

+(id) nodeWithWorld:(b2World*)world
{
	return [[[self alloc] initWithWorld:world] autorelease];
}

-(id) initWithWorld:(b2World*)w
{
	if ((self=[super init])) {
		_world = w;

		uint32 flags = 0;
		flags += b2DebugDraw::e_shapeBit;
		flags += b2DebugDraw::e_jointBit;
		_debugDraw = new GLESDebugDraw( kPhysicsPTMRatio );
		_debugDraw->SetFlags(flags);

		_world->SetDebugDraw(_debugDraw);
	}

	return self;
}

- (void) dealloc
{
	if( _debugDraw ) {
		delete _debugDraw;
		+debugDraw = NULL;
	}

	[super dealloc];
}

-(void) draw
{
	// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	// Needed states:  GL_VERTEX_ARRAY, 
	// Unneeded states: GL_TEXTURE_2D, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	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);	
}
@end

After this, run the simulator & take a screenshot.  resize the original sprite to this new size then just add it to the spritesheet in zwoptex.

Advertisements

cocos2d 0.99.5 multi-touch

After updating from 0.99.4 to 0.99.5 to fix some tilemap jitter issues when multiple layers exist in the tilemap, multi-touch suddenly stopped functioning.

Adding [glView setMultipleTouchEnabled:YES]; to the AppDelegate fixes this issue.

AI follow path

This is for my iPhone racing game. The joystick controls the wheels with steeringAngle for the player, but the game also has AI where other cars are following “waypoints” which are set as an object layer on tilemap using Tiled. I needed to know when to turn the wheels left/right based on the current location of the car (playerPosition) and the car’s box2d body + attached wheel’s angle – the angle property of the car object.

Snippet as follows:

UpdateWorld.mm
- (void) followToCheckpoint {
    float pdcurrent = CC_RADIANS_TO_DEGREES([car angle]);

    while (pdcurrent > 360) {
        pdcurrent -= 360;
    }
    while(pdcurrent < 0) { 		
        pdcurrent += 360; 	
    } 	
    CGPoint playerPosition = ccp(car.body->GetPosition().x *PTM_RATIO, car.body->GetPosition().y * PTM_RATIO);

    double angle = atan2(playerPosition.y - checkpointPos.y, playerPosition.x - checkpointPos.x) * (180 / M_PI) + 180;
    double pdopt = angle + 90;

    if ( pdopt > 360) {
	pdopt -= 360;
    }
    if(pdcurrent > 180) {
	if(pdopt > pdcurrent || pdopt < pdcurrent - 180) {
 	    [car SetSteeringAngle:-.4];
	}
	else {
	    [car SetSteeringAngle:.4];
	}
    }
    if(pdcurrent < 180) { 		
        if(pdopt > pdcurrent && pdopt < pdcurrent + 180) {
	    [car SetSteeringAngle:-.4];
        }
	else {
	    [car SetSteeringAngle:.4];
	}
    }
}

This will let the AI cars navigate from checkpoint to checkpoint.

One thing you may want to do along with this is slightly modify the checkpoint positions to different places for each car.  We do this with arc4random after getting the original position for the current destination checkpoint from the tilemap.

AIPlayer.mm

- (CGPoint) getCheckpointFromMap:(int)checkPointNum {
    CCTMXObjectGroup *objects = gameController.map.checkPoints;
    NSMutableDictionary * objPoint;
    CGPoint cpPos;

    int x,y;
    int w,h;
    for (objPoint in [objects objects]) {
        if ([[objPoint valueForKey:@"Checkpoint"] intValue] == nextCheckpoint ) {
	    x = [[objPoint valueForKey:@"x"] intValue];
	    y = [[objPoint valueForKey:@"y"] intValue];
	    w = [[objPoint valueForKey:@"width"] intValue];
	    h = [[objPoint valueForKey:@"height"] intValue];
	    int randX = arc4random() % PTM_RATIO *2 - PTM_RATIO;
	    int randY = arc4random() % PTM_RATIO *2 - PTM_RATIO;
  	    // the target checkpoint may be shifted by 1 tile maximum
	    cpPos = ccp(x+w/2 + randX,y+h/2 + randY);
        }
    }
    return cpPos;
}

In this way each checkpoint for each car is shifted a little bit from it’s original placement in the tilemap.  We should also assign slightly random speeds to the AI cars.  With this, the cars will not all be headed to the same destinations at corners or other places on the track.