How To Build a Monkey Jump Game Using Cocos2d 2.X, PhysicsEditor & TexturePacker – Part 2
In part 2 of this Monkey Jump tutorial series, you will add the hero to the game, make him move and jump, and start adding some gameplay. By .
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How To Build a Monkey Jump Game Using Cocos2d 2.X, PhysicsEditor & TexturePacker – Part 2
30 mins
Push It!
Let's improve the gameplay and allow the monkey to push objects to the left and right - and give him the ability to put his hands above his head to shield him from dropping objects.
Do you remember how you added the sensors to the left and right sides of the monkey in Part One? They come into play now! The key to the sensors was setting up the Id parameter in PhysicsEditor. You are going to retrieve this value now!
But before you do that, you need to add a few instance variables to keep count of the left and right sensors, as well as the number of contacts made with the monkey's head. Add these variables to Monkey.h:
int numPushLeftContacts;
int numPushRightContacts;
int numHeadContacts;
Next, the beginContactWith* and endContactWith* selectors have a contact parameter you can use to determine which part of the monkey has contact with an object – the Id value you added in PhysicsEditor is stored as user data in each fixture. So replace the existing object contact handlers for Monkey.mm with the following:
-(void) beginContactWithObject:(GB2Contact*)contact
{
NSString *fixtureId = (NSString *)contact.ownFixture->GetUserData();
if([fixtureId isEqualToString:@"push_left"])
{
numPushLeftContacts++;
}
else if([fixtureId isEqualToString:@"push_right"])
{
numPushRightContacts++;
}
else if([fixtureId isEqualToString:@"head"])
{
numHeadContacts++;
}
else
{
// count others as floor contacts
numFloorContacts++;
}
}
-(void) endContactWithObject:(GB2Contact*)contact
{
NSString *fixtureId = (NSString *)contact.ownFixture->GetUserData();
if([fixtureId isEqualToString:@"push_left"])
{
numPushLeftContacts--;
}
else if([fixtureId isEqualToString:@"push_right"])
{
numPushRightContacts--;
}
else if([fixtureId isEqualToString:@"head"])
{
numHeadContacts--;
}
else
{
// count others as floor contacts
numFloorContacts--;
}
}
As you see from the new code, you retrieve the Id, which You'll call fixtureId here, by accessing the contact parameter's fixture and then accessing the fixture's user data via the GetUserData method.
Now that we're tracking the contacts, you can update the monkey's animation frames to handle additional events.
Here's the decision table for the various animations:
Using the above table, you modify section #6 of updateCCFromPhysics in Monkey.mm as follows:
// 6 - Update animation phase
const float standingLimit = 0.1;
NSString *frameName = nil;
if((vX > -standingLimit) && (vX < standingLimit))
{
if(numHeadContacts > 0)
{
// Standing, object above head
frameName = [NSString stringWithFormat:@"monkey/arms_up.png"];
}
else
{
// Just standing
frameName = [NSString stringWithFormat:@"monkey/idle/2.png"];
}
}
else
{
if(numFloorContacts == 0)
{
// Jumping, in air
frameName = [NSString stringWithFormat:@"monkey/jump/%@.png", dir];
}
else
{
// Determine if monkey is pushing an item
bool isPushing = (isLeft && (numPushLeftContacts > 0))
|| (!isLeft && (numPushRightContacts > 0));
// On the floor
NSString *action = isPushing ? @"push" : @"walk";
frameName = [NSString stringWithFormat:@"monkey/%@/%@_%d.png", action, dir, animPhase];
}
}
Compile and test. Perfect! The monkey now behaves just as you want.
It Takes a Strong Monkey ...
Playing a little bit more though, I think the monkey should be a bit stronger under certain conditions. Currently, he's too weak to break free when an object is above his head. Let's give him some extra strength when he's trapped like that and wants to jump.
This is the current line from the jump selector in Monkey.mm that makes the monkey jump:
[self applyLinearImpulse:b2Vec2(0,[self mass]*JUMP_IMPULSE)
point:[self worldCenter]];
Replace it with:
float impulseFactor = 1.0;
// if there is something above monkey's head make the push stronger
if(numHeadContacts > 0)
{
impulseFactor = 2.5;
}
[self applyLinearImpulse:b2Vec2(0,[self mass]*JUMP_IMPULSE*impulseFactor)
point:[self worldCenter]];
That's your monkey on steroids! He now uses a 2.5-times stronger impulse when there's an object resting above him – this should allow him to break free of most of the objects.
Let's also change the walking impulse in case the monkey needs to push an object to the side to break free. Go to updateCCFromPhysics and cut the following lines from section #6:
// Determine if monkey is pushing an item
bool isPushing = (isLeft && (numPushLeftContacts > 0))
|| (!isLeft && (numPushRightContacts > 0));
Now paste that code into section #4 and modify it as follows:
// 4 - Determine direction of the monkey
bool isLeft = (direction < 0);
// Determine if monkey is pushing an item
bool isPushing = (isLeft && (numPushLeftContacts > 0))
|| (!isLeft && (numPushRightContacts > 0));
if((isLeft && (vX > -MAX_VX)) || ((!isLeft && (vX < MAX_VX))))
{
// apply the directional impulse
float impulse = clamp(-[self mass]*direction*WALK_FACTOR,
-MAX_WALK_IMPULSE,
MAX_WALK_IMPULSE);
if(isPushing)
{
impulse *= 2.5;
}
[self applyLinearImpulse:-b2Vec2(impulse,0) point:[self worldCenter]];
}
Compile and test – that's much better. But there's still a problem: when the monkey slightly grazes an object with his head, the new jump power makes him go through the roof!
You need to clamp his maximum speed inside the updateCCFromPhysics method. Add this to the end of section #3 of the updateCCFromPhysics method:
const float maxVelocity = 5.0;
float v = velocity.Length();
if(v > maxVelocity)
{
[self setLinearVelocity:maxVelocity/v*velocity];
}
Notice that in the code above you are directly modifying values controlled by the Box2d engine, thus affecting the overall behavior of the physics engine. You should try to avoid doing this kind of manipulation.
Compile and test. I like the monkey's behavior now. He reacts quickly, and is strong enough to push objects but does not become uncontrollable.
Where To Go From Here?
If you don't have it already, here is all of the source code for this tutorial series.
You've now reached the end of Part Two of the MonkeyJump tutorial! The project in its current form is available in the source code zip in the folder called 5-MonkeyJumpAndRun.
Stay tuned for the final part of the series, where You'll add some performance improvements, add a HUD layer to the game, and yes - kill the monkey! :]
In the meantime, if you have any questions or comments, please join the forum discussion below!