Cocos2d Game Development Blueprints
上QQ阅读APP看书,第一时间看更新

Particle systems

I always say that video games are comparable to films, differences aside, because they tell stories, have an argument, different scenes, a soundtrack, and special effects. Yes, you read correctly, you can add special effects, or particle systems as they're commonly known in computer games, to your games.

In computer graphics, this technique is commonly used because it simulates several natural and meteorological phenomena such as snow, sun, rain, fire, meteors, and smoke. It also simulates other special effects such as spirals, explosions, fireworks, and other lighting effects by using a large amount of small images. If you think about natural phenomena, like rain for example, it is composed of hundreds of water droplets and so that's what we need to replicate if we want to simulate a particle system in Cocos2d.

You might be thinking that this task must be hard and offers little possibilities, but in this section, you will learn to create and customize your own particle effects easily.

CCParticleSystem

The class responsible for creating and managing particle effects is CCParticleSystem, a subclass of CCParticleSystemBase, which inherits from CCNode. It offers some features that its parent class doesn't, such as:

  • Floating-point numbers in particle sizes
  • Subrectangles
  • Batched rendering to improve performance
  • Rotation
  • Scalation

This class allows you to configure several attributes to achieve the effect you're looking for, but before customizing a particle system, let's create a simple one.

We will need a private CCParticleSystem instance variable, so in GameScene.m, add the following line after CGSize _screenSize;:

    // Declare a private CCParticleSystem instance variable
    CCParticleSystem *_fire;

Note

Note how we use the underscore character to identify private variables. On the other hand, instance variables can also be declared as private by specifying the @private modifier.

Then add the following lines to the init method, just after [self configureParallaxEffect];:

    // Init the fire particle
    _fire = [CCParticleFire node];
    
    // Place the fire
    _fire.position = CGPointMake(_scientist.position.x, _scientist.position.y);

    // Add the particle system to the scene
    [self addChild:_fire z:1];

As you can see, creating a particle system is as easy as creating a common sprite. We declared a CCParticleSystem instance and then initialized it by sending the node message to CCParticleFire so we have a fire particle. Then we place the fire in the middle of the scientist's back and we add it to the scene with a z-order of 1 to be above the parallax node. Pretty short and simple, isn't it? Run the project and let's see what happens.

Where is the fire? Don't worry, I introduced this issue deliberately to expose a particularity of CCParticleSystem. If you take a look at Target Output at the bottom of the Xcode screen, you will see the following log:

2014-05-29 23:12:18.760 ExplosionsAndUFOs[13561:60b] -[CCFileUtils fullPathForFilename:contentScale:] : cocos2d: Warning: File not found: fire.png
2014-05-29 23:12:18.761 ExplosionsAndUFOs[13561:60b] cocos2d: Couldn't find file:fire.png

2014-05-29 23:14:17.359 ExplosionsAndUFOs[13561:60b] cocos2d: animation stopped
2014-05-29 23:14:17.385 ExplosionsAndUFOs[13561:60b] cocos2d: animation started with frame interval: 4.00
2014-05-29 23:14:17.405 ExplosionsAndUFOs[13561:60b] cocos2d: animation stopped

Okay it is important to notice that our particle system is looking for a file named fire.png and is not finding it; that's why there is no visual representation. This means that we must provide the texture to be used to represent the particles.

You can solve this issue by following these steps:

  1. In the project navigator, select the Resources group.
  2. Right-click and select Add Files to "ExplosionsAndUFOs"….
  3. Select the fire.png file (which I downloaded from http://misteraibo.deviantart.com/art/Fire-311421529) in the Resources folder you unzipped and click on Add.

Run your game and you will see something like the following screenshot:

CCParticleSystem

You just created your first particle system by adding the fire.png file into your project. It's the default file the particle system will look for, but you can choose a different file to create the effect. The next line shows how to apply a texture created with a custom file to our fire particle system:

_fire.texture = [CCTexture textureWithFile:@"flame.png"];

If you think about it, this feature allows you to create a large variety of particle systems. What should happen if you use the image of a bubble as a particle? Let's check it.

Again in the project navigator:

  1. Right-click on the Resources group and select Add Files to "ExplosionsAndUFOs"….
  2. Select bubble.png in the Resources folder and click on Add.

Then go back to GameScene.m and add the following line in the init method, before [self addChild:_fire z:1];:

    // Create particle from a texture
    _fire.texture = [CCTexture textureWithFile:@"bubble.png"];

Run the project and you will see what happens:

CCParticleSystem

I just wanted to show you how powerful particle systems are in Cocos2d, but it doesn't stop there. Before proceeding, undo the last changes:

  1. Delete bubble.png from the project, making sure that you choose the Move to Trash option.
  2. Delete the line _fire.texture = [CCTexture textureWithFile:@"bubble.png"];.

CCParticleSystem modes

There are two modes of particle systems that depend on which way they emit particles:

  • Gravity Mode
  • Radius Mode

You can set the mode by assigning CCParticleSystemModeGravity or CCParticleSystemModeRadius to the emitterMode property of the particle system. These modes can be customized thanks to a broad set of properties, some of which are exclusive to each mode; that's why you need to pay attention or you will encounter NSInternalInconsistencyException.

CCParticleSystemModeGravity

This is the default mode and it creates particles that flow from a source point or that converge at a target point. You can set up a gravity particle, setting values for the following attributes:

  • gravity: This indicates the acceleration of the particles in the x and y axes
  • speed: This indicates the speed that each particle will have
  • speedVar: This is the speed variance that each particle will have
  • tangentialAccel: This indicates the velocity of the particles moving in a curved path
  • tangentialAccelVar: This is the variance of the tangential acceleration
  • radialAccel: This indicates the acceleration of a particle that moves at a constant speed along a circular path
  • radialAccelVar: This is the variance of the radial acceleration

Better than reading, let's play a little with these attributes. Add the following lines to the init method, just before return self;:

    _fire.emitterMode = CCParticleSystemModeGravity;
    _fire.gravity = CGPointMake(0, -160);
    _fire.speed = 50.0;
    _fire.speedVar = 200.0;
    _fire.tangentialAccel = 70.0;
    _fire.tangentialAccelVar = 150.0;
    _fire.radialAccel = 80.0;
    _fire.radialAccelVar = 30.0;

Run the game and discover how these values affect the fire's behavior. I recommend you try your own setups to obtain your desired results.

CCParticleSystemModeRadius

This mode creates particles that move around a central point. The particular attributes for this mode are:

  • startRadius: This determines the starting radius of the particles. In other words, the distance to the node's position.
  • startRadiusVar: This is the starting radius variance of the particles.
  • endRadius: This determines the final radius of the particles. It can be made equal to the starting radius by specifying CCParticleSystemStartRadiusEqualToEndRadius.
  • endRadiusVar: This is the end radius variance of the particles.
  • rotatePerSecond: This indicates the number of degrees the particles will rotate around the source.
  • rotatePerSecondVar: This is the variance in degrees for each rotation per second.

Let's play a little with these attributes too. Replace the lines added in the gravity mode section with these ones:

    _fire.emitterMode = CCParticleSystemModeRadius;
    _fire.startRadius = 200.0;
    _fire.startRadiusVar = 5.0;
    _fire.endRadius = 30.0;
    _fire.endRadiusVar = 3.0;
    _fire.rotatePerSecond = 100.0;
    _fire.rotatePerSecondVar = 12.0;

You can see the results in the following screenshots:

CCParticleSystemModeRadius

Common properties

Besides these specific attributes, CCParticleSystem objects have some common attributes among which I would like to stress the most interesting that you may want to know about:

  • totalParticles: This is the maximum number of particles that will be on the screen at the same time.
  • life/lifeVar: This is the time that each particle will take to move from the start to the end point. If this value is low enough, there will be more and more particles on the screen, but this will never exceed the totalParticles value.
  • emissionRate: This indicates the number of particles that will be created per second. Usually, this value corresponds to totalParticles/life.
  • startSpin/startSpinVar: This determines the initial spin value of each particle.
  • endSpin/endSpinVar: This determines the final spin value of each particle.
  • startSize/startSizeVar: This indicates the initial size of each particle.
  • endSize/endSizeVar: This indicates the final size of each particle. You can keep the initial size at the end by using the CCParticleSystemStartSizeEqualToEndSize constant.
  • startColor/startColorVar: This sets up the initial color of the particles. For example, you can tint your particles in green using _fire.startColor = [CCColor greenColor].
  • endColor/endColorVar: This sets the final color of each particle.
  • texture: This specifies the texture used to render each particle.
  • angle/angleVar: This determines the direction the particles will follow once emitted, where 0 means right, 90 means up, 180 means left, and 270 means down.
  • duration: This is the time the emitter will run, not to be confused with the life attribute, where -1 and CCParticleSystemDurationInfinity means forever.
  • posVar: This is the position variance of the emitter.
  • sourcePosition: This indicates the offset position of the particles from the emitter.

However, you must be wondering how these properties affect our project visually. Replace the lines added during the Radius Mode section with these ones:

    _fire.totalParticles = 200;
    _fire.life = 3.3;
    _fire.lifeVar = 0.5;
    _fire.emissionRate = _fire.totalParticles/_fire.life;
    _fire.startSpin = 13.0;
    _fire.startSpinVar = 0.5;
    _fire.endSpin = 50.0;
    _fire.endSpinVar = 0.3;
    _fire.startSize = 60.0;
    _fire.startSizeVar = 5.0;
    _fire.endSize = 10.0;
    _fire.endSizeVar = 2.0;
    _fire.startColor = [CCColor greenColor];
    _fire.startColorVar = [CCColor blueColor];
    _fire.endColor = [CCColor redColor];
    _fire.endColorVar = [CCColor purpleColor];
    _fire.angle = 270.0;
    _fire.duration = CCParticleSystemDurationInfinity;
    _fire.posVar = CGPointMake(10, 10);
    _fire.sourcePosition = CGPointMake(0, 50);

Run the project again and look at the results.

Do you realize the potential these particles have thanks to these attributes? There are only a few changes required to achieve the result we are looking for in the backpack-reactor.

Replace the following properties from the previous block with the following ones:

    // Configure the particle system
    _fire.startSize = 50.0;
    _fire.startSizeVar = 1.0;
    _fire.posVar = CGPointMake(10, 0);
    _fire.sourcePosition = CGPointMake(0, -10);

Add these new ones:

    _fire.speed = 50.0;
    _fire.speedVar = 1.0;

By modifying startSize and its variance, the position variance, and the source position, we have customized the particle node to behave as we want. Also, by setting its speed and speed variance, we made it look like a reactor, but we want the fire to follow the scientist so add these lines at the end of the update method:

    // Make the fire follow the scientist
    _fire.position = CGPointMake(_scientist.position.x, _scientist.position.y);

Run the game and you will see the brand new invention of Dr. Fringe in action, the super BPR-200!

Common properties

Particle Designer

It's important that as game developers we know as many tools as possible so in this section, I would like to introduce you to a very useful one: Particle Designer (http://71squared.com/en/particledesigner).

This editor, which has a trial and a paid version, allows us to customize our particle systems in real time without needing to run the Xcode project each time to see the resultant effect.

Particle Designer

Particle Designer has a main view where you can see your particles (in the current version, you can work with multiple particle systems) in different screen modes: from iPhone SD Portrait to iPad HD Landscape and from Android Normal Portrait to Android X-Large Landscape. There you can change the stage color too, which will help you to reproduce your game's colors so you can achieve the proper effect.

On the left, you have your particle systems where you can edit its particlePositionType attribute (grouped or relative) and select other visual properties of the editor to show or hide the particle system.

On the right side, you will find four panel settings: Emitter Settings, Particle Settings, Color Settings, and Texture Settings. In these panels, you will be able to edit all the attributes of the particles while you see how this affects the visual result.

There is an interesting button with a cloud shape at the top-right corner of the editor. Clicking this shows the particles other users have shared, which you can export or just take a look at to get ideas.

Once you have set up your particles, if you have a licensed version, you can export them in the PEX, LAP, Plist, or JSON format. We won't cover how to integrate these files into Cocos2d in this book, but you will find a lot of documentation on the official website at http://71squared.com/particledesigner.