Animations, Actions And Particle SystemsCreating Realistic iPhone Games With Cocos2D

Advertisement

Animation makes games real. Movement adds excitement to a game and makes the characters more realistic. In this article, we’ll look at the Cocos2D library and how it supports programmatic animations in iPhone games.

This article is part of a series that teaches you how to create iPhone games based on the open-source game Seven Bridges. Make sure to check out the first article in the series, “Designing an Open-Source iPhone Game” and look at the source code in the Seven Bridges GitHub repository.

This article walks you through the basics of animation. We’ll look at actions in Cocos2D and see how to combine them to create complex and custom effects.

Simple Actions

All of the basic building blocks in a Cocos2D game, such as images and layers, derive from CCNode. Nodes interact with actions, which extend CCAction to create movement on the screen.

The basic Cocos2D actions can:

We’ll start by looking at the simple action CCBlink. Seven Bridges doesn’t use the blink action, but it’s one of the easiest actions to understand.

Before we can start to use any action, we’ll need a node. We’ll start with PlayerNode from Seven Bridges.

The player node is a simple one. It shows the player sprite, The player icon, and it handles the movement and animation of the player around the screen. PlayerNode isn’t actually a CCNode, but it contains a CCSprite, which represents a single image and extends CCNode. Separating the player object from the actual player image makes the player object a little more flexible.

Our player node needs a color and a LayerManager. LayerManager is a helper object in Seven Bridges that lets game nodes interact with the world around them. You don’t need to code your objects with a LayerManager, but it gives you a simple place to add some standard code.

Let’s create a new player and place the player on the screen.

PlayerNode* player = [[PlayerNode alloc] initWithColor:cBlack layerMgr:_layerMgr];
player.player.position = ccp(100, 100);

PlayerNode contains the sprite pointer and uses the LayerManager to add the sprite to the scene. It makes the sprite available as a property named player, which has a property named position. The full code for creating LayerManager is in LevelLayer.

The blink action can run directly on the player sprite, because sprites are also CCNode objects that implement the runAction method. It works like this:

CCBlink* blink = [CCBlink actionWithDuration:10 blinks:5];
[player.player runAction:blink];

Most actions define a static actionWithDuration method, which creates a new action object. CCBlink takes a duration and the number of times it should blink. We can apply whatever actions we’d like to the node just by passing them to the runAction method.

Running Multiple Actions

The latest version of Seven Bridges introduces teleporters, Teleporter icon. When players run into teleporters, they should look like they’re jumping in. To create that effect, we’ll combine three actions: rotate, fade and scale. Each of these actions takes a little time to run, but we can make them all run simultaneously. Put them together and we’ll see players spinning and disappearing as they shrink (again, see LevelLayer.mm).

[player.player runAction:[CCRotateBy actionWithDuration:0.5 angle:360]];
[player.player runAction:[CCFadeTo actionWithDuration:0.5 opacity:0.0]];
[player.player runAction:[CCScaleTo actionWithDuration:0.5 scale:0.25]];

CCRotateBy rotates the player from the current position by a specified number of degrees. In this case, we want a full circle, so we rotate by 360 degrees.

CCFadeTo changes the player’s opacity to the specified value. We want players to disappear completely, so we fade to 0.0.

CCScaleTo grows or shrinks the player according to the specified scaling factor. We want players to shrink as they enter the teleporter, so we scale them down to one quarter of the default size.

These actions take a duration, just like CCBlink. By specifying the duration as the same for each action, we cause all three of them to run at the same time. Playing them all together looks like this:


Seven Bridges – Teleport jump from Zack Grossbart on Vimeo.

Combining Actions In Sequences

Rotating and fading is cool, but let’s take it up a notch by actually changing the dimensions of the sprite, instead of just the size. The “Houses and Colors” map introduces houses, House icon, that you have to visit. When you visit a house, we turn it gray with a little flourish.

Houses are rendered by an object similar to PlayerNode, named HouseNode. Each is a helper object that contains a sprite and that handles the interactions. PlayerNode exposes the player sprite in a property named player, and HouseNode exposes the house sprite in a property named house.

The house-visiting animation flips the house horizontally while turning it gray. To flip the house back and forth, we run two actions sequentially. The first action flips the house to the right and leaves it backwards. The second action flips it back to the left.

The spinning, shrinking, fading player used three animations that ran at the same time. The house needs more control. To space them out, we’ll use a new object named CCSequence. Sequences string together actions that run one after another.

We’ll start by creating our two actions (HouseNode.mm):

float scale = 1.0;
CCEaseExponentialIn* flipHalf = [CCEaseExponentialIn 
    actionWithAction:[CCActionTween actionWithDuration:0.25 key:@"scaleX" from:-scale to:0.0]];
    
CCEaseExponentialOut* flipRemainingHalf = [CCEaseExponentialOut 
    actionWithAction:[CCActionTween actionWithDuration:0.25 key:@"scaleX" from:0.0 to:scale]];

Cocos2D provides a series of special actions that make easing and tweening possible. They all derive from CCEase. These actions provide simple access to some of the basic movement animations that make objects bounce and slide into each other with a more natural movement. These same techniques are used on the Web for JavaScript animations.

CCEaseExponentialIn and CCEaseExponentialOut provide natural movement in and out of various positions using an exponential curve. This animation combines that with CCTween, which can apply changes to any property of a node. We change the scaleX property to start the rotation.

Confused yet?

There’s a lot of math here, but the basic concepts are pretty simple. The first animation causes the house to get smaller horizontally along the x axis. This squishes the house together until it disappears. The second animation pulls the house apart from zero to the full width. When you put them together, the house looks like it’s rotating.

Let’s create our house and run the animations together with a sequence like this (HouseNode.mm):

HouseNode *houseNode = [[HouseNode alloc] initWithColorAndCoins:cBlack layerMgr:layerMgr coins:0];

CCSequence* seq = [CCSequence actions:flipHalf, flipRemainingHalf, nil];
[house runAction:seq];

CCSequence takes a series of actions and runs them one after another. The last argument is an Objective-C construct called a selector; it defines a method to call when the actions are complete. It is optional, and we don’t need any notification, so we just pass it nil.

Combining Actions With Custom Code

This sequence will flip the house, but we also want to turn it gray: Gray house icon. Cocos2D provides special actions for tinting, but we want a high-quality image for the gray house, so we’ll use a PNG file. None of the default actions replace a sprite’s image, so we need a little custom code. Cocos2D provides the ultimate flexibility with the CCCallFunc action, which sits inside a sequence and calls any code you want.

The house turns gray when the visit ends, so we’ll create a new function named visitEnded and define it on HouseNode, like so (HouseNode.mm):

-(void)visitEnded {
    CCSpriteFrameCache* cache = [CCSpriteFrameCache sharedSpriteFrameCache];
    CCSpriteFrame* frame;
    frame = [cache spriteFrameByName:@"house_gray.png"];
    [self.house setDisplayFrame:frame];
}

We already have a gray house icon, so the visitEnded function gets that icon from the sprite cache and sets it into our house sprite. Now we just need to tweak our sequence to call visitEnded in the middle of the animation. This code is getting a little complicated, so we’ll add it to a new method on HouseNode and call it visit.

-(void) visit {
    float scale = 1.0;
    CCEaseExponentialIn* flipHalf = [CCEaseExponentialIn 
        actionWithAction:[CCActionTween actionWithDuration:0.25 key:@"scaleX" from:-scale to:0.0]];
        
    CCEaseExponentialOut* flipRemainingHalf = [CCEaseExponentialOut 
        actionWithAction:[CCActionTween actionWithDuration:0.25 key:@"scaleX" from:0.0 to:scale]];
        
    CCSequence* seq = [CCSequence actions:flipHalf,
                      [CCCallFunc actionWithTarget:self selector:@selector(visitEnded)],
                      flipRemainingHalf, nil];
                      
    [self.house runAction:seq];
}

This new sequence will squish the house down, call the visitEnded method with CCCallFunc to turn the house gray, and stretch the house back out.


Seven Bridges – House Visit from Zack Grossbart on Vimeo.

Cocos2D provides a series of actions for calling custom code, such as CCCallFuncN and CCCallBlock. Each is a variation of calling custom code during sequences. Combining out-of-the-box animations with custom code creates an ideal environment for complex animations.

Combining Multiple Animations Into Particle Systems

We’ve looked at simple animations and looked at some more complex interactions. Now let’s get fancy with particles. Cocos2D provides a set of more complex animations, which all start with CCParticleSystem.

Particle systems aren’t actions. They use actions to create flexible canned animations. They’re awesome, complex and poorly documented. Particle systems work well for mimicking natural phenomena such as rain and smoke.

When you beat a level of Seven Bridges, we give you a little reward: star-shaped confetti that spins and floats across the screen. We add whimsy to those stars with some randomness, and pull everything together with a particle system named CCParticleRain.


Seven Bridges – Stars from Zack Grossbart on Vimeo.

So… um… there’s no way to ease into this one — here’s a really big chunk of code (LevelLayer.mm):

-(void) showConfetti: (float) x y:(float) y {
    self.emitter = [[CCParticleRain alloc] init];
    [self.emitter setScaleX:0.5];
    [self.emitter setScaleY:0.5];
    
    [self.emitter resetSystem];
    self.emitter.texture = [[CCTextureCache sharedTextureCache] addImage:@"confetti.png"];
    
    self.emitter.duration = 1.5;
    
    // Set the gravity effect for the stars to control how
    // fast they fall down.
    self.emitter.gravity = ccp(self.player.player.position.x, 90);
    
    // Specify the angle of the stars as they extend from the
    // starting point.
    self.emitter.angle = 90;
    self.emitter.angleVar = 360;
    
    // The speed the particles will use when floating
    self.emitter.speed = 160;
    self.emitter.speedVar = 20;
    
    // The radial variable
    self.emitter.radialAccel = -120;
    self.emitter.radialAccelVar = 120;
    
    // The tangential variable
    self.emitter.tangentialAccel = 30;
    self.emitter.tangentialAccelVar = 60;
    
    // How long each star should last before fading out
    self.emitter.life = 1;
    self.emitter.lifeVar = 4;
    
    // How much each star should spin
    self.emitter.startSpin = 15;
    self.emitter.startSpinVar = 5;
    self.emitter.endSpin = 360;
    self.emitter.endSpinVar = 180;
    
    // The color of the stars as RGB values.  Each color uses
    // a variable value for where the stars should start and
    // what color they should use when they're done.
    ccColor4F startColor = {171.0f, 26.0f, 37.0f, 1.0f};
    self.emitter.startColor = startColor;
    ccColor4F startColorVar = {245.0f, 255.f, 72.0f, 1.0f};
    self.emitter.startColorVar = startColorVar;
    ccColor4F endColor = {255.0f, 223.0f, 85.0f, 1.0f};
    self.emitter.endColor = endColor;
    ccColor4F endColorVar = {255.0f, 131.0f, 62.0f, 1.0f};
    self.emitter.endColorVar = endColorVar;
    
    // The size of each of the stars
    self.emitter.startSize = 50.0f;
    self.emitter.startSizeVar = 20.0f;
    self.emitter.endSize = kParticleStartSizeEqualToEndSize;
    
    // The rate at which new stars will be created
    self.emitter.totalParticles = 250;
    self.emitter.emissionRate = self.emitter.totalParticles/self.emitter.life;
    
    // The position to start the stars
    self.emitter.posVar = ccp(x + 20, y - 20);
    self.emitter.position = ccp(x,y);
    
    // We have a simple white background, so we don't want to
    // do additive blending.
    self.emitter.blendAdditive = NO;
    
    // Now we're ready to run the particle emitter
    [self addChild: self.emitter z:10];
    self.emitter.autoRemoveOnFinish = YES;
    
    // We call the doWon function when the particle system completes
    // so that we can take the player to the You Won screen.
    [self scheduleOnce:@selector(doWon) delay:3];
}

This code comes from LevelLayer, which controls the player’s interactions with the play screen. When the player wins the level, we call the showConfetti method and pass in the player’s current location.

The particle system starts with a single image, Star confetti icon, creates hundreds of instances of that image, and moves those instances across the screen while changing their size, color, rotation and speed.

Most of this code just sets up CCParticleRain and tells it how to behave. Particle systems are very flexible and support a dizzying array of configuration properties. These properties aren’t well documented; I found most of them by looking in Cocos2D’s source code.

Particle systems achieve a natural look by having random variability added to them. You control the range of the randomness with var properties. For example, we want the stars to start out between 20 and 50 times their regular size, so we specify a startSize property of 50.0f and a startSizeVar property of 20.0f. This means that each of the stars will start somewhere between these two values.

Create Your Own Animations

Now that you’ve seen some of the basic concepts in Cocos2D animations, you can create your own. An easy place to start is by changing Seven Bridges. Just download the source code and start tweaking it.

Add a blinking animation to make the player character blink while moving. Take a look at PlayerNode to see the sequence for moving the player across the screen. Play with the particle emitter to change the way the stars work.

In this article, we’ve reviewed programmatic animations in games, but Cocos2D also supports sprite-based animations. These animations play a series of images to create the illusion of movement, somewhat like a flip book. Seven Bridges uses that style of animation when moving the player around the screen.

Player movement animation

Ray Wenderlich has an awesome tutorial on sprite-based animation, titled “How to Use Animations and Sprite Sheets in Cocos2D.” The article is excellent, but it mentions a product called Zwoptex, and I strongly recommend TexturePacker instead.

(al)

↑ Back to top

Zack Grossbart is an engineer, designer, and author. He's a founding member of the Spiffy UI project, the architect of the WordPress Editorial Calendar, and a Consulting Engineer with NetIQ. Zack began loading DOS from a floppy disk when he was five years old. He first worked professionally with computers when he was 15 and started his first software company when he was 16. Zack lives in Cambridge, Massachusetts with his wife and daughter.

  1. 1

    thank you

    0
  2. 2

    Good job with your tutorial. I used the part of the particle system, worked pretty good! Thank you.

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top