Chapter    9

Particle Effects

To create special visual effects, game programmers often make use of particle systems. Particle systems work by emitting vast numbers of tiny particles and rendering them efficiently—much more efficiently than if they were sprites. This allows you to simulate effects such as rain, fire, snow, explosions, vapor trails, and many more.

Particle systems are driven by a great number of properties. By great I mean about 30 properties, which all influence not only the appearance and behavior of individual particles but the whole particle effect. The particle effect is the totality of all particles working together to create a particular visual outcome. One particle alone does not make a fire effect; ten still don’t get close enough. You would want several dozens, if not hundreds, of particles to work together in just the right way to create the fire effect.

Creating convincing particle effects is a trial-and-error process. Trying all the various properties in source code and tweaking a particle system by compiling the game, seeing what it looks like, and then making changes and repeating this process is cumbersom to say the least. That’s where a particle design tool comes in handy, and I know just the right one: it’s called Particle Designer, and I explain how it works in this chapter.

Example Particle Effects

Cocos2d comes with a number of built-in particle effects that give you a good idea of the kinds of effects they’ll produce. You can use them as placeholders in your game or subclass and modify the examples defined in the CCParticleExamples.m file if you just want to apply some minor tweaks. The good thing about them is that you don’t need any outside help; you create the example particle effects as if they were simple CCNode objects. As a matter of fact, they are actually derived from CCNode.

I created a project called ParticleEffects01 that shows all the cocos2d example particle effects. You can cycle through the examples by quickly tapping the screen, and you can also drag and move them with your finger. Many particle effects look totally different as soon as they start moving, as you can see in Figure 9-1. What seems like just a huge blob of particles may well work as an engine exhaust effect if the particle effect is moving.

9781430244165_Fig09-01.jpg

Figure 9-1 .  The same particle effect from top to bottom: motionless, moving slowly, moving fast

There’s only one type of effect that can’t be moved once started—one-time effects like the CCParticleExplosion shown in Figure 9-2. What’s special about this effect is that it emits all its particles at once and stops emitting new particles instantly. All other particle effects run continuously, always creating new particles while those that have exceeded their lifetime are removed. The challenge in that situation is to balance the total number of particles that are on the screen.

9781430244165_Fig09-02.jpg

Figure 9-2 .  The CCParticleExplosion is an example effect provided by cocos2d

Listing 9-1 shows the relevant methods used in the ParticleEffects01 example project. By using the current particleType variable in the switch statement, the corresponding built-in particle effect is created. Note that a CCParticleSystem pointer is used to store the particles, so you need to use the addChild code only once at the end of the runEffect method. Every example particle effect is derived from CCParticleSystem.

Listing 9-1.  Using the Built-in Effects

-(void) runEffect
{
    // remove any previous particle FX
    [self removeChildByTag:1 cleanup:YES];
 
    CCParticleSystem* system;
 
    switch (particleType)
    {
        case ParticleTypeExplosion:
           system = [CCParticleExplosion node];
                         break;
        case ParticleTypeFire:
           system = [CCParticleFire node];
           break;
        case ParticleTypeFireworks:
           system = [CCParticleFireworks node];
           break;
        case ParticleTypeFlower:
           system = [CCParticleFlower node];
           break;
        case ParticleTypeGalaxy:
           system = [CCParticleGalaxy node];
           break;
        case ParticleTypeMeteor:
           system = [CCParticleMeteor node];
           break;
        case ParticleTypeRain:
           system = [CCParticleRain node];
           break;
        case ParticleTypeSmoke:
           system = [CCParticleSmoke node];
           break;
        case ParticleTypeSnow:
           system = [CCParticleSnow node];
           break;
        case ParticleTypeSpiral:
           system = [CCParticleSpiral node];
           break;
        case ParticleTypeSun:
           system = [CCParticleSun node];
           break;
 
        default:
           // do nothing
           break;
    }
 
    CGSize winSize = [CCDirector sharedDirector].winSize;
    system.position = CGPointMake(winSize.width / 2, winSize.height / 2);
 
    [self addChild: system z:1 tag:1];
 
    [label setString:NSStringFromClass([system class])];
}
 
-(void) setNextParticleType
{
    particleType++;
    if (particleType == ParticleTypes_MAX)
    {
     particleType = 0;
    }
}

Note  The NSStringFromClass method is very helpful in this example for printing out the name of the class without having to enter dozens of matching strings. It’s one of the cool runtime features of the Objective-C language that you’re able to get a class’s name as a string. Try to do that in C++, and you’ll be biting your toenails. The Objective-C Runtime Programming Guide is a good starting point if you’d like to dive into this advanced topic or if you just want to learn how Objective-C works on a lower level: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html.

For game-play code, the NSStringFromClass and related methods hardly play any role, but they’re very helpful debugging and logging tools. You can find a complete list and description of these methods in Apple’s Foundation Function Reference: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html.

If you use one of these example effects in your own project, you might be shocked to see ugly, square pixels. Figure 9-3 shows this effect very clearly. This occurs because all the built-in particle effects try to load a specific texture named fire.png , which is distributed with cocos2d-iphone in the Resources/Images folder. You can still create very good particle effects even without a texture, provided that the particle sizes remain fairly small. But to see the built-in particle effects as they were intended, you need to add the fire.png image to your Xcode project.

9781430244165_Fig09-03.jpg

Figure 9-3 .  If your example particle effects, like this CCParticleFireworks, display huge, square particles, you forgot to add the fire.png image to your Xcode project

Creating a Particle Effect the Hard Way

You can easily create your own subclass of the CCParticleSystem class. What’s not so easy is creating a convincing particle effect with it, let alone one that comes close to what you originally envisioned. The following is the list of properties grouped by function that determine the particle system’s look and behavior:

  • emitterMode = gravity
    • sourcePosition
    • gravity
    • radialAccel, radialAccelVar
    • speed, speedVar
    • tangentialAccel, tangentialAccelVar
  • emitterMode = radius
    • startRadius, startRadiusVar, endRadius, endRadiusVar
    • rotatePerSecond, rotatePerSecondVar
  • duration
  • posVar
  • positionType
  • startSize, startSizeVar, endSize, endSizeVar
  • angle, angleVar
  • life, lifeVar
  • emissionRate
  • startColor, startColorVar, endColor, endColorVar
  • blendFunc, blendAdditive
  • texture

As you can imagine, there’s a lot to tweak here, and that’s the main problem of achieving what you want: you don’t see the effect of your changes until you rebuild and run your project. When you read about Particle Designer later in this chapter, you’ll learn how much it streamlines the creation of new particle effects.

For now you’ll do it the hard way first and start at the beginning to gain an understanding about how the cocos2d particle system works. To program a particle effect from scratch, you first learn how to subclass the CCParticleSystem class and how to initialize it. A detailed description of the particle system’s properties follows.

Subclassing CCParticleSystem

To create your own particle effect without Particle Designer, you should subclass from CCParticleSystemQuad. The quad particle system is not only faster than CCParticleSystem, it can also be batched. Using the CCParticleBatchNode class allows you to have the same effect multiple times on the screen while only using one draw call.

For the customized particle effect in the ParticleEffects01 project, I’ve created the ParticleEffectSelfMade class. The interface of this class is in Listing 9-2.

Listing 9-2.  Subclassing from the Optimal Particle System Class

#import < Foundation/Foundation.h>
#import "cocos2d.h"
 
@interface ParticleEffectSelfMade : CCParticleSystemQuad
{
}
@end

Now you’ll look at the implementation of the self-made particle effect, which uses all the available properties. I’ll attempt to explain each of them, but it’s much better to see it for yourself and experiment with the parameters, so I encourage you to tweak the properties in this project. In the ParticleEffects01 project (Listing 9-3), you’ll also find comments describing each parameter in brief.

Listing 9-3.  Manually Setting a Particle System’s Properties

#import "ParticleEffectSelfMade.h"
 
@implementation ParticleEffectSelfMade
-(id) init
{
    return [self initWithTotalParticles:250];
}
 
-(id) initWithTotalParticles:(int)numParticles
{
    self = [super initWithTotalParticles:numParticles];
    if (self)
    {
     self.duration = kCCParticleDurationInfinity;
     self.emitterMode = kCCParticleModeGravity;
     // some properties must only be used with a specific emitterMode!
     if (self.emitterMode == kCCParticleModeGravity)
     {
        self.sourcePosition = CGPointMake(−15, 0);
        self.gravity = CGPointMake(−50, -90);
        self.radialAccel = −90;
        self.radialAccelVar = 20;
        self.tangentialAccel = 120;
        self.tangentialAccelVar = 10;
        self.speed = 15;
        self.speedVar = 4;
     }
     else if (self.emitterMode == kCCParticleModeRadius)
     {
        self.startRadius = 100;
        self.startRadiusVar = 0;
        self.endRadius = 10;
        self.endRadiusVar = 0;
        self.rotatePerSecond = −180;
        self.rotatePerSecondVar = 0;
     }
 
     self.position = CGPointZero;
     self.posVar = CGPointZero;
     self.positionType = kCCPositionTypeFree;
 
     self.startSize = 40.0f;
     self.startSizeVar = 0.0f;
     self.endSize = kCCParticleStartSizeEqualToEndSize;
     self.endSizeVar = 0;
 
     self.angle = 0;
     self.angleVar = 0;
 
     self.life = 5.0f;
     self.lifeVar = 1.0f;
 
     self.emissionRate = 30;
     self.totalParticles = 250;
 
     startColor.r = 1.0f;
     startColor.g = 0.25f;
     startColor.b = 0.12f;
     startColor.a = 1.0f;
     startColorVar.r = 0.0f;
     startColorVar.g = 0.0f;
     startColorVar.b = 0.0f;
     startColorVar.a = 0.0f;
     endColor.r = 0.0f;
     endColor.g = 0.0f;
     endColor.b = 0.0f;
     endColor.a = 1.0f;
     endColorVar.r = 0.0f;
     endColorVar.g = 0.0f;
     endColorVar.b = 1.0f;
     endColorVar.a = 0.0f;
 
     self.blendFunc = (ccBlendFunc){GL_SRC_ALPHA, GL_DST_ALPHA};
     // or use this shortcut to set the blend func to: GL_SRC_ALPHA, GL_ONE
     //self.blendAdditive = YES;
 
     self.texture = [[CCTextureCache sharedTextureCache] addImage:@"fire.png"];
    }
    return self;
}
@end

CCParticleSystem Properties

In Listing 9-3 you will have noticed how verbose the code is simply because so many particle system properties can be initialized. And most of them need to be set to acceptable values in order to display a reasonably meaningful particle effect onscreen. Some properties are even mutually exclusive and can’t be used together. It’s time to take a close look at what these particle system properties actually do.

Variance Properties

You’ll notice that many properties have companion properties suffixed with Var. These are variance properties, and they determine the range of fuzziness allowed for the corresponding property. Take, for example, the properties life = 5 and lifeVar = 1. These values mean that on average each particle will live for five seconds. The variance allows a range of 5–1 to 5 + 1. So, each particle gets a random lifetime between four to six seconds.

If you don’t want any variation, set the Var variable to 0. Variation is what gives particle effects their organic, fuzzy behavior and appearance. But variation can also be confusing when you design a new effect, so unless you have some experience, I recommend starting with a particle effect that has little or no variance.

Number of Particles

It’s time for you to get acquainted with particles by starting with the total number of particles in the particle effect, controlled by the totalParticles property. The totalParticles variable is usually set by the initWithTotalParticles method but can be changed later. The number of particles has a direct impact both on the look of the effect and on performance.

-(id) init
{
    return [self initWithTotalParticles:250];
}

Use too few particles and you won’t get a nice glow, but it may be sufficient to sprinkle a few stars around the player’s head when he runs into a wall. Use too many particles and it might not be what you want either, because many particles are rendered on top of one another and possibly blended, so you basically end up with a white blob. Furthermore, using too many particles easily kills your framerate. There’s a reason why the Particle Designer tool won’t let you create effects with more than 2,000 particles.

Tip  In general, you should aim to achieve the desired effect with the smallest number of particles. Particle size also plays an important role—the smaller the size of individual particles, the better the performance will be. Especially with particle effects, it’s important to test on older devices because they can have a severely negative impact on performance.

Emitter Duration

The duration property determines how long particles will be emitted. If set to 2, it will create new particles for two seconds and then stop. It’s that simple:

self.duration = 2.0f;

If you’d like the particle effect node to be automatically removed from its parent node once the particle system has stopped emitting particles and the last particles have vanished, set the autoRemoveOnFinish property to YES:

self.autoRemoveOnFinish = YES;

The autoRemoveOnFinish property is a convenience feature that’s meaningful only if used in conjunction with particle systems that don’t run infinitely, like one-time explosion effects. Cocos2d defines a constant kCCParticleDurationInfinity (equals: -1) for infinitely running particle effects.

self.duration = kCCParticleDurationInfinity;

The majority of particle effects are infinitely running, and you can only stop them by removing them from the node hierarchy. Infinitely running particle effects ignore the autoRemoveOnFinish property.

Emitter Modes

There are two emitter modes: gravity and radius, controlled by the emitterMode property. These two modes create fundamentally different effects even if most of the parameters are the same, as you can see when you compare Figure 9-4 with Figure 9-5. Both modes use several exclusive properties (see Listing 9-3) that must not be set if they are not supported by the current mode; otherwise, you’ll receive a runtime exception from cocos2d like this:

ParticleEffects[6332:207] *** Terminating app due to uncaught exception
     'NSInternalInconsistencyException', reason: 'Particle Mode should be Radius'

9781430244165_Fig09-04.jpg

Figure 9-4 .  The ParticleEffectSelfMade from the ParticleEffects01 project in gravity mode

9781430244165_Fig09-05.jpg

Figure 9-5 .  The very same effect using radius mode looks completely different

Emitter Mode: Gravity

Gravity mode lets particles fly toward or away from a center point. Its strength is that it allows very dynamic, organic effects. You set gravity mode with this line:

self.emitterMode = kCCParticleModeGravity;

Gravity mode uses the following exclusive properties, which can be used only when emitterMode is set to kCCParticleModeGravity:

self.sourcePosition = CGPointMake(−15, 0);
self.gravity = CGPointMake(−50, -90);
self.radialAccel = −90;
self.radialAccelVar = 20;
self.tangentialAccel = 120;
self.tangentialAccelVar = 10;
self.speed = 15;
self.speedVar = 4;

The sourcePosition determines the offset as a CGPoint from the node’s position where new particles appear. The name is a bit misleading in that the actual center of gravity is the node’s position, and sourcePosition is an offset to that center of gravity. The gravity property then determines the speed with which particles accelerate in the x and y directions. In this case, the negative values indicate that the gravitational force will accelerate particles toward the left (−50) and downward (−90). But this acceleration is relative to the particle node’s position. Because particles start out at a sourcePosition offset of −15 (slightly offset to the left), they perform a counterclockwise movement around the particle node’s position. You can see this effect in Figure 9-4, and tweaking the values in the ParticleEffects01 project helps to understand how sourcePosition and gravity affect the movement of particles.

For the center of gravity to have any impact, the gravity of the particles shouldn’t be too high, and the sourcePosition should not be offset too far. The previous values give you a good working example that you can tweak.

The radialAccel property defines how fast particles accelerate the farther they move away from the emitter. This parameter can also be negative, which makes particles slow down as they move away. The tangentialAccel property is similar in that it lets particles rotate around the emitter and speed up as they move away. Negative values let the particles spin clockwise, and positive values spin them counterclockwise.

The speed property should be fairly obvious—it’s simply the speed of the particles. It has no particular unit of measurement. Figure 9-4 shows an example particle effect using gravity mode. Particles are attracted by the particle node’s position and start out slightly to the left of the particle node’s position, so they perform a counterclockwise radial movement.

Emitter Mode: Radius

Radius mode causes particles to rotate in a circle. It also allows you to create spiral effects with particles either rushing inward or rotating outward. You set radius mode with this line:

self.emitterMode = kCCParticleModeRadius;

Like gravity mode, radius mode has exclusive properties. The following properties can be used only when emitterMode is set to kCCParticleModeRadius:

self.startRadius = 100;
self.startRadiusVar = 0;
self.endRadius = 10;
self.endRadiusVar = 0;
self.rotatePerSecond = −180;
self.rotatePerSecondVar = 0;

The startRadius property determines how far away from the particle effect node’s position the particles will be emitted. Likewise, endRadius determines the distance from the node’s position the particles will rotate toward. If you want to achieve a perfect circle effect, you can set endRadius to the same as the startRadius using this constant:

self.endRadius = kCCParticleStartRadiusEqualToEndRadius;

Using the rotatePerSecond property, you can influence the direction the particles move and the speed with which they move, and thus the number of times they rotate around if startRadius and endRadius are different.

The same particle effect that was shown in Figure 9-4 using gravity mode is shown in Figure 9-5 using radius mode, and you’ll notice how different it looks, despite all other properties—except for the exclusive ones—being the same. To test this, uncomment the following line in the ParticleEffects01 project:

//self.emitterMode = kCCParticleModeRadius;

Particle Position

By moving the node, you also move the effect. But the effect also has a posVar property that determines the variance in the position where new particles will be created. By default, both are at the center of the node:

self.position = CGPointZero;
self.posVar = CGPointZero;

A very important aspect of particle positions is whether existing particles should move relative to the node’s movement or whether they should not be influenced at all by the node’s position. For example, if you have a particle effect that creates stars around your player-character’s head, you’d want the stars to follow the player as he moves around. You can achieve this effect by setting this property:

self.positionType = kCCPositionTypeGrouped;

On the other hand, if you want to set your player on fire and you want the particles to create a trail-like effect as the player moves around, you should set the positionType property like this:

self.positionType = kCCPositionTypeFree;

The free movement is best used with steam, fire, engine exhaust smoke, and similar effects that move around with the object they are attached to and should give the impression of not being connected to the object that emits these particles.

Particle Size

The size of particles is given in pixels using the startSize and endSize properties, which determine the size of the particles when they’re emitted and how big they are when they’re removed. The size of the particle gradually scales from startSize to endSize.

self.startSize = 40.0f;
self.startSizeVar = 0.0f;
self.endSize = kCCParticleStartSizeEqualToEndSize;
self.endSizeVar = 0;

You can use the constant kCCParticleStartSizeEqualToEndSize to ensure that the particle size does not change during a particle’s lifetime.

Particle Direction

You set the direction in which particles are initially emitted with the angle propert, measured in degrees from 0 to 360. A value of 0 means that particles will be emitted upward, but this is true only for the gravity emitterMode. In radius emitterMode, the angle property determines where on the startRadius the particles will be emitted; higher values will move the emission point counterclockwise along the radius.

self.angle = 0;
self.angleVar = 0;

Particle Lifetime

A particle’s lifetime determines how many seconds it will take to transition from start to end, where the particle will simply fade out and disappear. The life property sets the lifetime of individual particles. Keep in mind that the longer particles live, the more particles will be onscreen at any given time. If the total number of particles is reached, no new particles will be spawned until some existing particles have died.

self.life = 5.0f;
self.lifeVar = 1.0f;

The emissionRate property directly influences how many particles are created per second. Together with the totalParticles property, it has a big impact on what the particle effect looks like.

self.emissionRate = 30;
self.totalParticles = 250;

In general, you will want to balance the emissionRate so that it matches the particle lifetime with the totalParticles allowed in the particle effect. You can do so by dividing totalParticles by life and set the result as the emissionRate:

self.emissionRate = self.totalParticles / self.life;

Tip  By tweaking particle lifetime, the total number of particles allowed in the system, and the emissionRate, you can create burst effects by allowing the stream of particles to be frequently interrupted just because the number of particles onscreen is limited and new particles are emitted relatively quickly. On the other hand, if you notice undesirable gaps in your particle stream, you need to either increase the number of allowed particles or preferably reduce the lifetime and/or emission rate. In that case, you should use emissionRate = totalParticles / life.

Particle Color

Each particle can transition from a starting color to an end color, creating the vibrant colors particle effects are known for. You should at least set the startColor in a particle effect; otherwise, the particles may not be visible at all because the default color is black. The colors are of type ccColor4F, a struct with four floating-point members: r, g, b, and a, corresponding to the colors red, green, and blue, as well as the alpha channel, which determines the color’s opacity. The value range for each of these members goes from 0 to 1, with 1 being the full color.

If you want a completely white particle color, you’d set all four r, g, b, and a members to 1. If you want a red color, you only need to set the r and a values to 1.0f. If you want blue, then set b and a to 1.0f. Note that the a value is the alpha transparency of the color. If you leave it at the default value of 0.0f, the color will be completely translucent and thus not visible.

// startColor is mostly red and fully opaque
startColor.r = 1.0f;
startColor.g = 0.25f;
startColor.b = 0.12f;
startColor.a = 1.0f;
// startColor has no variance (plus/minus 0.0f)
startColorVar.r = 0.0f;
startColorVar.g = 0.0f;
startColorVar.b = 0.0f;
startColorVar.a = 0.0f;
// endColor is a fully opaque black color
endColor.r = 0.0f;
endColor.g = 0.0f;
endColor.b = 0.0f;
endColor.a = 1.0f;
// endColorVar specifies a full variance range for color blue
// the end of lifetime color of a particle will be randomly between black and blue
endColorVar.r = 0.0f;
endColorVar.g = 0.0f;
endColorVar.b = 1.0f;
endColorVar.a = 0.0f;

Particle Blend Mode

Blending refers to the computation a particle’s pixels go through before being displayed onscreen. The property blendFunc takes a ccBlendFunc struct as input, which provides the source and destination blend modes:

self.blendFunc = (ccBlendFunc){GL_SRC_ALPHA, GL_DST_ALPHA};

Blending works by taking the red, green, blue, and alpha of the source image (the particle) and mixing it with the colors of any images that are already onscreen when the particle is rendered. In effect, the particle blends in a certain way with its background, and blendFunc determines how much and what colors of the source image are blended with how much and with which colors of the background.

The formula to determine the final pixel color onscreen is as follows:

(source color * source blend function) + (destination color * destination blend function)

Let’s assume your example source pixel has the RGB values (0.1, 0.2, 0.3), and the destination pixel has the RGB values (0.4, 0.5, 0.6). Both color values are multiplied by the blend function, the simplest being GL_ONE, which equals 1.0. That means the resulting pixel’s color will be as follows:

(0.1 * 1 + 0.4 * 1, 0.2 * 1 + 0.5 * 1, 0.3 * 1 + 0.6 * 1) = (0.5, 0.7, 0.9)

The blendFunc property has a very profound effect on how particles are displayed. By using a combination of the following blend modes for both source and target, you can create rather bizarre effects or simply cause the effect to render as black squares. There’s lots of room for experimentation.

  • GL_ZERO
  • GL_ONE
  • GL_SRC_COLOR
  • GL_ONE_MINUS_SRC_COLOR
  • GL_SRC_ALPHA
  • GL_ONE_MINUS_SRC_ALPHA
  • GL_DST_ALPHA
  • GL_ONE_MINUS_DST_ALPHA

You’ll find more information on the OpenGL blend modes and details about the blend calculations in the OpenGL ES documentation at www.khronos.org/opengles/documentation/opengles1_0/html/glBlendFunc.html.

Tip  Because it’s hard to imagine which blend functions will create which results with varying images, I’d like to point you to an article that describes the most common blend operations with example images: www.machwerx.com/2009/02/11/glblendfunc/.

Even more interesting is the Visual glBlendFunc tool developed by Anders Riggelsen: www.andersriggelsen.dk/OpenGL/. With any HTML5-compatible browser, you can play with various images and blend functions to see the results instantly.
Note that you can also modify the blendFunc property of other cocos2d nodes, namely, all nodes that conform to the CCBlendProtocol such as the classes CCSprite, CCSpriteBatchNode, CCLabelTTF, CCLayerColor, and CCLayerGradient.

The source and target blend modes GL_SRC_ALPHA and GL_ONE are frequently combined to create additive blending, resulting in very bright or even white colors where many particles are drawn on top of each other:

self.blendFunc = (ccBlendFunc){GL_SRC_ALPHA, GL_ONE};

Alternatively, you can simply set the blendAdditive property to YES, which is the same as setting blendFunc to GL_SRC_ALPHA and GL_ONE:

self.blendAdditive = YES;

The normal blend mode is set using GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA, which creates transparent particles:

self.blendFunc = (ccBlendFunc){GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};

Particle Texture

Without a texture, all particles would be flat, colored squares, as in Figure 9-3. To use a texture for a particle effect, provide one using the CCTextureCache method addImage, which returns the CCTexture2D for the given image file:

self.texture = [[CCTextureCache sharedTextureCache] addImage:@"fire.png"];

Particle textures look best if they’re cloudlike and roughly spherical. It’s often detrimental to the particle effect if the texture has high-contrast areas, resembling a particular shape or form, like the redcross.png from the shoot-’em-up game. This makes it easier to see individual particles because they don’t blend too well with each other. Some effects can use this to their advantage, like the aforementioned stars circling the player’s head. In addition, cartoonlike images or gradients work best, whereas photorealistic images tend to create an undefinable pixel mess. To demonstrate this, Figure 9-6 shows three different textures applied to the same particle effect.

9781430244165_Fig09-06.jpg

Figure 9-6 .  The same particle effect using different textures. Photorealistic images are not ideal

The most important aspect of particle textures is that the image must be at most 64x64 pixels in size. The smaller the texture size, the better it is for the performance of the particle effect.

Particle Designer

Particle Designer is an interactive tool for creating particle effects for cocos2d and iOS OpenGL applications. You can download the trial version at http://particledesigner.71squared.com.

This is an invaluable tool that will save you a lot of time creating particle effects. Its power is that you can immediately see what happens onscreen when you change a particle effect’s properties, and you can share your creations with others and get inspiration from other developers’ particle effects.

Introducing Particle Designer

By default, Particle Designer’s user interface shows a visual list of particle effects. To edit a particular effect, select it and switch to the Emitter Config view (Figure 9-7) by either double-clicking it or clicking the Emitter Config button in the upper right-hand corner.

9781430244165_Fig09-07.jpg

Figure 9-7 .  Particle Designer is full of properties you can tweak interactively. A separate window (Figure 9-8) shows the effect as you edit it

You should recognize these parameters from the description of the self-made particle effect properties. Only a few properties aren’t available and can’t be edited in Particle Designer. One is the positionType, and another is the endRadiusVar property for the radius emitter mode. The latter means you can’t create particle effects that rotate outward in radius mode. But you can always load a Particle Designer effect and then tweak it in code by overriding certain properties, like setting the positionType to kCCPositionTypeFree, as you’ll do later in Listing 9-4. This is just a minor nuisance compared to actually seeing onscreen how your effect changes as you move the sliders.

The only unusual control is the Particle Texture. There’s no button to load an image, and double-clicking the field doesn’t do anything either. The trick here is that the Particle Texture box only accepts images that are dragged and dropped onto it. Just drag any image from Finder over to this box, and the box will turn green, signaling that it will accept that image. Once you drop the image, it will be used by the particle effect.

Caution  Particle Designer will warn you if you drop an image that’s larger than 512x512 pixels. It will use the image anyway but will scale the image down to 512x512 pixels, regardless of its original aspect ratio. Do keep in mind that texture sizes of over 64x64 pixels are probably overkill unless you have special requirements. A particle effect’s performance is mainly influenced by the size of the texture and the number of particles.

The Particle Designer preview window in Figure 9-8 looks just like the iPhone Simulator. It can also be set to the iPad screen size, and you can change the orientation by clicking the iPad/iPhone and Orientation buttons in Particle Designer’s menu bar, to the right of the Load, Save, and Save As buttons. By clicking and dragging inside the preview window, you can move the particle effect around, which helps you see how the effect might look on moving objects.

Notice that the Background Color settings aren’t part of the actual effect. They will change the background color of the preview window. This is useful if your game has bright colors and you want to design a dark or dim effect and still be able to see what you’re doing.

9781430244165_Fig09-08.jpg

Figure 9-8 .  By clicking and dragging inside Particle Designer’s preview window, you can even move the particle effect around to see what it might look like on moving objects

If you lack inspiration, you can always make use of the Ramdomize button. You can also ponder about the meaning of the word ramdomize, which is how it’s spelled in Particle Designer. The Urban Dictionary tells me that ramdom is a cooler form of random. So, I’m guessing the developers just thought their randomizer to be extra cool. Well, it’s definitely inspiring even though it doesn’t randomize all the available properties. For example, Ramdomize will never change the emitter type, the emitter location, and many emitter type–specific parameters.

Once you find your inspiration, you’ll want to slide the sliders and watch what happens in the preview window. Take your time and tweak an effect until you like it. Careful, though, because it’s a very captivating, even mesmerizing, activity, and you’ll easily find yourself making new particle effects just for the fun of it.

Caution  Be careful when designing particle effects! First, keep in mind that your game has to calculate and render a lot of other things, too. If the effect you’re currently designing runs at 60 FPS in Particle Designer’s preview window, that doesn’t mean it won’t kill your framerate when you use it in your game. Always test new particle effects in your game and keep an eye on the framerate. Also, make sure to run these tests on a device—in particular on older devices! Your game’s performance in the iPhone/iPad Simulator is often misleading and thus must be regarded as completely irrelevant. The same goes for the Particle Designer preview window.

Using Particle Designer Effects

I’m assuming that, hours later, you’ve made the perfect particle effect and now you’d like to use that in cocos2d. I made mine, and the first step is to save the particle effect. When you click the Save or Save As button in Particle Designer, you get the dialog shown in Figure 9-9.

9781430244165_Fig09-09.jpg

Figure 9-9 .  Saving a particle effect from Particle Designer requires you set the file format to cocos2d. Embedding the texture into the plist file is optional

For the saved particle effect to be usable by cocos2d, you must set the File Format setting to cocos2d (plist). You can also check the Embed texture box, which will save the texture into the plist file. The benefit is that you only have to add the plist file to your Xcode project; the downside is that you then can’t change the effect’s texture without loading the particle effect back into Particle Designer.

After saving the effect, you have to add the effect plist and, if you didn’t embed the texture, the effect’s PNG file to your Xcode project’s Resources group. In the ParticleEffects01 project, I’ve added both variants, one effect with a separate PNG texture and another effect that has the texture embedded in the plist file.

Listing 9-4 shows how I modified the runEffect method to load the Particle Designer effects.

Listing 9-4.   Using a Particle Effect Created with Particle Designer

-(void) runEffect
{
    . . .
 
    switch (particleType)
    {
     . . .
 
        case ParticleTypeDesignedFX:
           system = [CCParticleSystemQuad particleWithFile:@"fx1.plist"];
           break;
        case ParticleTypeDesignedFX2:
           system = [CCParticleSystemQuad particleWithFile:@"fx2.plist"];
           system.positionType = kCCPositionTypeFree;
           break;
        case ParticleTypeSelfMade:
           system = [ParticleEffectSelfMade node];
           break;
 
     default:
     // do nothing
           break;
    }
 
    . . .
}

You initialize a CCParticleSystem with a Particle Designer effect by using the particleWithFile method and providing the particle effect’s plist file as parameter. In this case, I chose the CCParticleSystemQuad since it is faster than CCParticleSystem and can be batched with CCParticleBatchNode. Most importantly, Particle Designer effects require the quad particle system because CCParticleSystem isn’t compatible with effects created with Particle Designer.

Sharing Particle Effects

What’s very cool about Particle Designer is that you can share your creations with other Particle Designer users. From the Particle Designer menu, simply choose Share and then Share Emitter to open a dialog that lets you enter a title and description for your particle effect, as shown in Figure 9-10.

9781430244165_Fig09-10.jpg

Figure 9-10 .  By submitting a Particle Effect to the Online Library, you can share your creations with other users

Caution  As the message in Figure 9-8 indicates, you should be careful to only upload artwork for which you have the rights to share and distribute or for which you are the copyright holder. Otherwise, you risk violating someone’s copyright or may be violating a nondisclosure or other agreement if you work under contract.

In Figure 9-11, you can see the effect I just submitted in the lower right-hand corner.

9781430244165_Fig09-11.jpg

Figure 9-11 .  The submitted effect quickly appears in the Online Library. Apparently my description was too long to fit in the display area

Shared particle effects may not always be perfect for your requirements, but they often provide good starting points for your own effects. They help you achieve the desired effect much faster and, at the very least, they can be an inspiration. I encourage you to scroll through the list of effects and try as many as you can to gain a good sense of what’s possible, what looks good, and what just doesn’t work.

Shoot ’em Up with Particle Effects

I’d love to see some of these effects in the game! Let’s take the shoot-’em-up game to a new level. You’ll find the results in this chapter’s ShootEmUp04 project, along with added sound effects, as well as in Figure 9-12.

9781430244165_Fig09-12.jpg

Figure 9-12 .  I killed the boss, and I can’t see a darn thing. But those particles are so beautiful!

In the Enemy class, the gotHit method is the perfect place to add destructive particle explosions, as Listing 9-5 shows. I decided the boss monster should have its own particle effect, mostly because it’s so big. And purple.

Listing 9-5.  Adding an Explosion Effect to the Shoot ’em Up Game

-(void) gotHit
{
    hitPoints--;
    if (hitPoints < = 0)
    {
     self.visible = NO;
 
     // Play a particle effect when the enemy was destroyed
     CCParticleSystem* system;
 
     if (type == EnemyTypeBoss)
     {
           system = [CCParticleSystemQuad particleWithFile:@"fx-explosion2.plist"];
     [[SimpleAudioEngine sharedEngine] playEffect:@"explo1.wav"
                                         pitch:1.0f
                                         pan:0.0f
                                         gain:1.0f];
     }
     else
     {
           system = [CCParticleSystemQuad particleWithFile:@"fx-explosion.plist"];
     [[SimpleAudioEngine sharedEngine] playEffect:@"explo2.wav"
                                         pitch:1.0f
                                         pan:0.0f
                                         gain:1.0f];
     }
 
     // Set some parameters that can't be set in Particle Designer
           system.positionType = kCCPositionTypeFree;
           system.autoRemoveOnFinish = YES;
           system.position = self.position;
 
     [[GameLayer sharedGameLayer] addChild:system];
    }
    else
    {
     [[SimpleAudioEngine sharedEngine] playEffect:@"hit1.wav"
                                         pitch:1.0f
                                         pan:0.0f
                                         gain:1.0f];
    }
}

You have to add the particle effects fx-explosion.plist and fx-explosion2.plist as resources to the Xcode project. The particle system is initialized as before. Since the particle effect should and must be independent from the enemy that creates it, a few preparations are necessary. First, set the autoRemoveOnFinish flag to YES so that the effect automatically removes itself. This works because both explosions run only for a short time. The effect also needs the current position of the enemy so that it’s displayed at the correct position.

I add the particle effect to the GameLayer because the enemy itself can’t display the particle effect. To start, it’s invisible and so will be any child nodes added to it. It also might be respawned very soon, which would interfere with the particle effect’s position. But most importantly, all Enemy objects are added to a CCSpriteBatchNode, which does not allow you to add anything but CCSprite objects. If the particle effect were added to the Enemy object, a runtime exception would be inevitable.

For playing sound effects, the SimpleAudioEngine class usually suffices. To use it, you must import the SimpleAudioEngine.h header file in every file you’re using the SimpleAudioEngine class because cocos2d doesn’t treat the CocosDenshion audio engine as an integral part of the engine.

As you play the game with the new particle effects, you may notice that the first time one of these effects displays, the game play stops for a short moment. That’s because cocos2d is loading the particle effect’s texture—a rather slow process, whether the texture is embedded into the plist, as in this case, or provided as a separate texture. To avoid that, I’m preloading the particle effects and sound files in the GameLayer:

-(id) init
{
    if ((self = [super init]))
    {
     . . .
 
     // To preload the textures, play each effect once off-screen
     [CCParticleSystemQuad particleWithFile:@"fx-explosion.plist"];
     [CCParticleSystemQuad particleWithFile:@"fx-explosion2.plist"];
 
     // Preload sound effects
     [[SimpleAudioEngine sharedEngine] preloadEffect:@"explo1.wav"];
     [[SimpleAudioEngine sharedEngine] preloadEffect:@"explo2.wav"];
     [[SimpleAudioEngine sharedEngine] preloadEffect:@"shoot1.wav"];
     [[SimpleAudioEngine sharedEngine] preloadEffect:@"shoot2.wav"];
     [[SimpleAudioEngine sharedEngine] preloadEffect:@"hit1.wav"];
    }
    return self;
}

The init method now creates each particle system for each particle effect once, but it doesn’t add it as child anywhere. This causes the texture to be cached by cocos2d. If you chose not to embed the texture inside the particle effect plist file, you can preload the particle effect textures simply by calling the CCTextureCache addImage method:

[[CCTextureCache sharedTextureCache] addImage:particleFile];

Summary

This chapter was truly a visual joy ride! The stock effects provided by cocos2d give a good indication of what the particle effect is able to deliver. They’re quick and easy to use.

But it was also excruciating to create a particle effect in source code. There are so many properties to tweak; some are exclusive to specific emitter modes; some have misleading names, and they aren’t exactly straightforward to figure out. With the explanation for each property, however, you should now have a good understanding how a particle effect is put together and what the most important parameters are.

We then saw how particle effects can shine with Particle Designer. This tool is extremely helpful—and lots of fun—to work with. Suddenly, when you can move sliders and see the results immediately onscreen, it changes your whole view on particle effects, and even more so since you can share your creations with others and experiment with other designers’ effects.

Finally, the shoot-‘em-up game got a makeover and now plays particle explosions and sounds when bullets are fired and enemies are destroyed. This makes for a much more lively game.

In the next chapter I set the shoot-’em-up game aside for a bit to tell you all you need to know about tilemaps.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.164.164