Menu Search
Jump to the content X X
Smashing Conf New York

You know, we use ad-blockers as well. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf New York, dedicated to smart front-end techniques and design patterns.

Animations, Actions And Particle Systems Creating Realistic iPhone Games With Cocos2D

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.

Further Reading on SmashingMag: Link4

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 Link

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

The basic Cocos2D actions can:

  • hide and show,
  • flip and rotate,
  • fade and tint,
  • and a lot more.

We’ll start by looking at the simple action CCBlink10. 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 PlayerNode4311 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 CCSprite12, 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 LayerManager13. 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 LevelLayer4114.

The blink action can run directly on the player sprite, because sprites are also CCNode objects that implement the runAction15 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 Link

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.mm4016).

[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 jump17 from Zack Grossbart383118 on Vimeo393219.

Combining Actions In Sequences Link

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

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

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 tweening23 possible. They all derive from CCEase24. 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 animations25.

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

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 Link

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 CCCallFunc28 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.mm2927):

-(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 Visit30 from Zack Grossbart383118 on Vimeo393219.

Cocos2D provides a series of actions for calling custom code, such as CCCallFuncN33 and CCCallBlock34. 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 Link

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

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


Seven Bridges – Stars37 from Zack Grossbart383118 on Vimeo393219.

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

-(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 LevelLayer4114, 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 Link

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 code42 and start tweaking it.

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

(al)

Footnotes Link

  1. 1 http://zgrossbart.github.com/bridges/
  2. 2 https://www.smashingmagazine.com/2013/02/13/designing-open-source-iphone-game
  3. 3 https://github.com/zgrossbart/bridges
  4. 4 https://www.smashingmagazine.com/2012/10/design-your-own-mobile-game/#further-reading-on-smashingmag
  5. 5 https://www.smashingmagazine.com/2016/11/how-to-build-a-spritekit-game-in-swift-3-part-1/
  6. 6 https://www.smashingmagazine.com/2012/05/develop-a-one-of-a-kind-cssjs-based-game-portfolio/
  7. 7 https://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/
  8. 8 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_node.html
  9. 9 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_action.html
  10. 10 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_blink.html
  11. 11 https://github.com/zgrossbart/bridges/blob/master/bridges2/PlayerNode.mm
  12. 12 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_sprite.html
  13. 13 https://github.com/zgrossbart/bridges/blob/master/bridges2/LayerMgr.mm
  14. 14 https://github.com/zgrossbart/bridges/blob/master/bridges2/LevelLayer.mm
  15. 15 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_node.html#a5d6d868cc1a0ba72820a055b05661aa5
  16. 16 https://github.com/zgrossbart/bridges/blob/master/bridges2/LevelLayer.mm
  17. 17 https://vimeo.com/60320015
  18. 18 https://vimeo.com/user1004495
  19. 19 https://vimeo.com
  20. 20 https://github.com/zgrossbart/bridges/blob/master/bridges2/HouseNode.mm
  21. 21 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_sequence.html
  22. 22 https://github.com/zgrossbart/bridges/blob/master/bridges2/HouseNode.mm
  23. 23 http://en.wikipedia.org/wiki/Inbetweening
  24. 24 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_action_ease.html
  25. 25 https://www.smashingmagazine.com/2011/10/04/quick-look-math-animations-javascript/
  26. 26 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_action_tween.html
  27. 27 https://github.com/zgrossbart/bridges/blob/master/bridges2/HouseNode.mm
  28. 28 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_call_func.html
  29. 29 https://github.com/zgrossbart/bridges/blob/master/bridges2/HouseNode.mm
  30. 30 https://vimeo.com/60319771
  31. 31 https://vimeo.com/user1004495
  32. 32 https://vimeo.com
  33. 33 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_call_func_n.html
  34. 34 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_call_block.html
  35. 35 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_particle_system.html
  36. 36 http://www.cocos2d-iphone.org/api-ref/2.1.0/interface_c_c_particle_rain.html
  37. 37 https://vimeo.com/60320148
  38. 38 https://vimeo.com/user1004495
  39. 39 https://vimeo.com
  40. 40 https://github.com/zgrossbart/bridges/blob/master/bridges2/LevelLayer.mm
  41. 41 https://github.com/zgrossbart/bridges/blob/master/bridges2/LevelLayer.mm
  42. 42 https://github.com/zgrossbart/bridges
  43. 43 https://github.com/zgrossbart/bridges/blob/master/bridges2/PlayerNode.mm
  44. 44 http://en.wikipedia.org/wiki/Flip_book
  45. 45 http://www.raywenderlich.com/1271/how-to-use-animations-and-sprite-sheets-in-cocos2d
  46. 46 http://www.codeandweb.com/texturepacker

↑ Back to top Tweet itShare on Facebook

Zack Grossbart is an engineer, designer, and author. He's an Architecting Engineer and Human Factors Specialist at Micro Focus where he focuses on enterprise identity and security. Zack began loading DOS from a floppy disk when he was five years old. He's been getting paid to code since 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

↑ Back to top