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 Bridges1. Make sure to check out the first article in the series, “Designing an Open-Source iPhone Game2” and look at the source code in the Seven Bridges GitHub repository3.

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 CCNode4. Nodes interact with actions, which extend CCAction5 to create movement on the screen.

The basic Cocos2D actions can:

We’ll start by looking at the simple action CCBlink12. 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 PlayerNode4813 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 CCSprite14, 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 LayerManager15. 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 LevelLayer4616.

The blink action can run directly on the player sprite, because sprites are also CCNode objects that implement the runAction17 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: rotate189, fade1910 and scale20. 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.mm4521).

[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 jump22 from Zack Grossbart433623 on Vimeo443724.

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 HouseNode25. 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 CCSequence26. Sequences string together actions that run one after another.

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

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 tweening28 possible. They all derive from CCEase29. 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 animations30.

CCEaseExponentialIn and CCEaseExponentialOut provide natural movement in and out of various positions using an exponential curve. This animation combines that with CCTween31, 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.mm3432):

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 CCCallFunc33 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.mm3432):

-(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 Visit35 from Zack Grossbart433623 on Vimeo443724.

Cocos2D provides a series of actions for calling custom code, such as CCCallFuncN38 and CCCallBlock39. 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 CCParticleSystem40.

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 CCParticleRain41.


Seven Bridges – Stars42 from Zack Grossbart433623 on Vimeo443724.

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

-(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 LevelLayer4616, 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 code47 and start tweaking it.

Add a blinking animation to make the player character blink while moving. Take a look at PlayerNode4813 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 book49. 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 Cocos2D50.” The article is excellent, but it mentions a product called Zwoptex, and I strongly recommend TexturePacker51 instead.

(al)

Footnotes

  1. 1 http://zgrossbart.github.com/bridges/
  2. 2 http://www.smashingmagazine.com/2013/02/13/designing-open-source-iphone-game
  3. 3 https://github.com/zgrossbart/bridges
  4. 4 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_node.html
  5. 5 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_action.html
  6. 6 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_hide.html
  7. 7 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_show.html
  8. 8 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_flip_y.html
  9. 9 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_rotate_by.html
  10. 10 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_fade_to.html
  11. 11 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_tint_to.html
  12. 12 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_blink.html
  13. 13 https://github.com/zgrossbart/bridges/blob/master/bridges2/PlayerNode.mm
  14. 14 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_sprite.html
  15. 15 https://github.com/zgrossbart/bridges/blob/master/bridges2/LayerMgr.mm
  16. 16 https://github.com/zgrossbart/bridges/blob/master/bridges2/LevelLayer.mm
  17. 17 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_node.html#a5d6d868cc1a0ba72820a055b05661aa5
  18. 18 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_rotate_by.html
  19. 19 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_fade_to.html
  20. 20 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_scale_to.html
  21. 21 https://github.com/zgrossbart/bridges/blob/master/bridges2/LevelLayer.mm
  22. 22 http://vimeo.com/60320015
  23. 23 http://vimeo.com/user1004495
  24. 24 http://vimeo.com
  25. 25 https://github.com/zgrossbart/bridges/blob/master/bridges2/HouseNode.mm
  26. 26 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_sequence.html
  27. 27 https://github.com/zgrossbart/bridges/blob/master/bridges2/HouseNode.mm
  28. 28 http://en.wikipedia.org/wiki/Inbetweening
  29. 29 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_action_ease.html
  30. 30 http://www.smashingmagazine.com/2011/10/04/quick-look-math-animations-javascript/
  31. 31 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_action_tween.html
  32. 32 https://github.com/zgrossbart/bridges/blob/master/bridges2/HouseNode.mm
  33. 33 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_call_func.html
  34. 34 https://github.com/zgrossbart/bridges/blob/master/bridges2/HouseNode.mm
  35. 35 http://vimeo.com/60319771
  36. 36 http://vimeo.com/user1004495
  37. 37 http://vimeo.com
  38. 38 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_call_func_n.html
  39. 39 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_call_block.html
  40. 40 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_particle_system.html
  41. 41 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_particle_rain.html
  42. 42 http://vimeo.com/60320148
  43. 43 http://vimeo.com/user1004495
  44. 44 http://vimeo.com
  45. 45 https://github.com/zgrossbart/bridges/blob/master/bridges2/LevelLayer.mm
  46. 46 https://github.com/zgrossbart/bridges/blob/master/bridges2/LevelLayer.mm
  47. 47 https://github.com/zgrossbart/bridges
  48. 48 https://github.com/zgrossbart/bridges/blob/master/bridges2/PlayerNode.mm
  49. 49 http://en.wikipedia.org/wiki/Flip_book
  50. 50 http://www.raywenderlich.com/1271/how-to-use-animations-and-sprite-sheets-in-cocos2d
  51. 51 http://www.codeandweb.com/texturepacker

↑ Back to topShare on Twitter

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.

Advertising

Note: Our rating-system has caused errors, so it's disabled at the moment. It will be back the moment the problem has been resolved. We're very sorry. Happy Holidays!

  1. 1
  2. 2

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

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