How To Build a Monkey Jump Game Using Cocos2d 2.X, PhysicsEditor & TexturePacker – Part 3

In part third and final part of the Monkey Jump tutorial series, you will add the HUD layer, some performance improvements, and yes – kill the monkey! :] By .

Leave a rating/review
Save for later
Share

Create this vertical-scrolling platformer with Cocos2d!

Create this vertical-scrolling platformer with Cocos2D!

Welcome to the final part of the Monkey Jump tutorial! In this series, you are creating a fun vertical-scrolling platformer with Cocos2d 2.X, TexturePacker, and PhysicsEditor.

In Part One of the tutorial series, you introduced the MonkeyJump game design, created the sprite sheets and shapes you needed, and began coding the game.

In Part Two, you added your hero to the game, made him move and jump, and added some gameplay.

In this third and final part of the series, you will add some performance improvements, add a HUD layer, and yes – kill the monkey! :]

You’ll be starting with the project where you left off last time. If you don’t have it already, grab the source code for this tutorial series and open up 5-MonkeyJumpAndRun.

All right, time to stop monkeying around and wrap up this tutorial! :]

Too Many Objects!

If you play the game for a while, you’ll see that it gets slower and slower, until it becomes completely unplayable.

There’s a reason for this – as objects continue to fall from the sky, one after another, they bump into the objects already lying around. All of these collisions have to be handled by Box2d. If there are n objects, there are n*(n-1) possible collisions to handle. Box2d uses hashes to make things faster, but you can imagine how dramatically the number of collisions increases as the number of objects increases.

If you watch a statue drop, you’ll see that nearly the entire stack below moves and bounces from impulses passed from the statue down through the stack from one object to another.

To improve the situation, you’re going to convert objects which are some distance below the monkey into static objects. These static objects will still let other objects pile up above them, but they’ll no longer react to the impact. As a result, a falling statue will only affect the top of the stack instead of the complete pile.

Box2d allows objects to go to sleep when they aren’t touched by other objects for some time. you will use this feature to improve performance.

GB2Engine has an iterate method that can be used to iterate all objects with a block. you will use it to create a routine that checks the y-coordinates of all objects and puts to sleep any that are a certain distance below the monkey.

Add the following code to the end of the update selector in GameLayer.mm:

    // 10 - Iterate over objects and turn objects into static objects
    // if they are some distance below the monkey
    float pruneDistance = 240/PTM_RATIO;
    float prune = [monkey physicsPosition].y - pruneDistance;    
    [[GB2Engine sharedInstance] iterateObjectsWithBlock: ^(GB2Node* n) 
     {
         if([n isKindOfClass:[Object class]])
         {
             Object *o = (Object*)n;
             float y = [o physicsPosition].y;
             if(y < prune)
             {
                 // set object to static
                 // if it is below the monkey
                 [o setBodyType:b2_staticBody];
             }
         }
     }
     ];

Build and run. Note that there are still some situations where things won't work as expected. For example, if a group of objects pile up like a tower, and the monkey climbs onto the pile, it might happen that a dropping object reaches the prune distance and is converted into a static object in mid-air.

The solution is quite simple: only convert objects to static if their speed is low. Change the second if condition in the above code to:

    if((y < prune) && 
      ([o linearVelocity].LengthSquared() < 0.1))    
    {...}

Build and run. Looks good, doesn't it?

Caught in a Trap

There's still an issue, though - the monkey might still get caught under a pile of objects. Items pile up around him, and he's not strong enough to break free if there are too many objects above him.

There are several ways to deal with this situation. One is to simply let him die if he's stuck. Another solution is to "teleport" the monkey above the objects and let him go on playing. That sounds fun, let's do that!

To make this work, you have to be sure that the monkey teleports above all the objects. Otherwise, his position might place him directly inside another object and he'll never break free!

Go into GameLayer.h and add a member variable:

    float highestObjectY;   // y position of the highest object

And make it a property by adding the following line above the "scene" method declaration:

    @property (nonatomic, readonly) float highestObjectY;

Now switch to GameLayer.mm and synthesize the object by adding this line just below the @implementation line:

    @synthesize highestObjectY;

Then, replace section #10 in update with the following:

    // 10 - Iterate over objects and turn objects into static objects
    // if they are some distance below the monkey
    float pruneDistance = 240/PTM_RATIO;
    float prune = [monkey physicsPosition].y - pruneDistance;
    highestObjectY = 0.0f;
    [[GB2Engine sharedInstance] iterateObjectsWithBlock: ^(GB2Node* n) 
     {
         if([n isKindOfClass:[Object class]])
         {
             Object *o = (Object*)n;
             float y = [o physicsPosition].y;
             // record the highest object
             if((y > highestObjectY) && ([o active]))
             {
                 highestObjectY = y;
             }   
             if((y < prune) && 
                ([o linearVelocity].LengthSquared() < 0.1))
             {
                 // set object to static
                 // if it is below the monkey
                 [o setBodyType:b2_staticBody];
             }
         }
     }
     ];

The new code determines the highest object location by resetting the highestObjectY with every new check. Note that it only checks active objects. Otherwise, the highest object will always be the object that is waiting to drop.

Switch to Monkey.h and add a new member:

    int stuckWatchDogFrames; // counter to detect if monkey is stuck

Now let's teleport the monkey above the highest object's position if he's been stuck with an object above his head for a certain amount of time. Add the following to the end of the updateCCFromPhysics selector:

    // 8 - Check if monkey is stuck
    if(numHeadContacts > 0)
    {
        stuckWatchDogFrames--;
        if(stuckWatchDogFrames == 0)
        {
            // teleport the monkey above the highest object
            [self setPhysicsPosition:b2Vec2([self physicsPosition].x, 
                                            gameLayer.highestObjectY+2.0)];                
        }
    }
    else
    {
        // restart watchdog
        stuckWatchDogFrames = 120; // 2 seconds at 60fps
    }

Build and run. That's much better! Now, if the monkey is caught in a trap and he can't push his way out, he will be magically freed.

There are other ways to detect if the monkey is caught. For example, you could check how high the objects are piled, or if the monkey's speed is low for a certain amount of time. Feel free to try out other detection methods to see which one works best for you.