Cocos2D Particle Engine w/Different Sprites 6

Most of my development work is centered around educational games. I am co-founder of Third Rail Games, LLC. A while back I wanted to modify the particle system in Cocos2D so that it would put out streams of letters in a given sequence instead of the one sprite.

Here’s what I ended up with:

Here’s the code.

I’ll explain the simple modification required to create a particle system that puts out a string of letters, and you’ll see that the same logic could be used to create a particle system that pulls from a sprite sheet.

Here’s the code from the class. I’ll explain what each element does.

JGCCTextParticleSystem.h

#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CCBMFontConfiguration+atlasName.h"

@interface JGCCParticleSystemText : CCParticleSystemQuad {
    CCBMFontConfiguration *fntConfig;
    NSString *particleString;
}

-(id)initWithFntFile:(NSString *)fntFile andPlist:(NSString *)plistFile andString:(NSString *)str;
-(CGRect)getLetterRect:(int)position;
-(void)setParticleString:(NSString *)ps;
-(NSString *)particleString;

@end

JGCCTextParticleSystem.m

#import "JGCCParticleSystemText.h"

@implementation JGCCParticleSystemText

-(NSString *)particleString {
    return particleString;
}

-(void)setParticleString:(NSString *)ps {
    particleString = ;
    [self initTexCoordsWithRect:CGRectZero];
}

-(id)initWithFntFile:(NSString *)fntFile andPlist:(NSString *)plistFile andString:(NSString *)str {
    if (self = ([super initWithFile:plistFile])) {
        fntConfig = [[CCBMFontConfiguration configurationWithFNTFile:fntFile] retain];
        self.texture = [[CCTexture2D alloc] initWithImage:[UIImage imageNamed:[fntConfig atlasName]] resolutionType:kCCResolutionUnknown];
        [self setParticleString:str];
    }
    return self;
}

-(CGRect)getLetterRect:(int)position {
    NSLog(@"position %d", position);
    position = position % [particleString length];
    unichar c = [particleString characterAtIndex:position];
    ccBMFontDef fontDef = fntConfig->BMFontArray_;
    CGRect rect = fontDef.rect;
    return rect;
}

// pointRect is in Points coordinates.
-(void) initTexCoordsWithRect:(CGRect)pointRect
{
    if (!particleString) {
        return;
    }

    for(NSUInteger i=0; i<totalParticles; i++) {
        
        CGRect rect = [self getLetterRect:i];
        NSLog(@"%f, %f, %f, %f, %d", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, i);
        GLfloat wide = [texture_ pixelsWide];
        GLfloat high = [texture_ pixelsHigh];
        
        GLfloat left = rect.origin.x / wide;
        GLfloat bottom = rect.origin.y / high;
        GLfloat right = left + rect.size.width / wide;
        GLfloat top = bottom + rect.size.height / high;
        
        CC_SWAP( top, bottom);
        
        // bottom-left vertex:
        quads_[i].bl.texCoords.u = left;
        quads_[i].bl.texCoords.v = bottom;
        // bottom-right vertex:
        quads_[i].br.texCoords.u = right;
        quads_[i].br.texCoords.v = bottom;
        // top-left vertex:
        quads_[i].tl.texCoords.u = left;
        quads_[i].tl.texCoords.v = top;
        // top-right vertex:
        quads_[i].tr.texCoords.u = right;
        quads_[i].tr.texCoords.v = top;
    }
}

@end

This new class is a subclass of the CCParticleSystemQuad class in Cocos2d. Besides the cocos2d and foundation classes, we’re importing a class extension file, CCBMFontConfiguration+atlasName.h. This extension exposes the altasName property of the Bitmap font classes we’ll use. It allows us to get the name of the texture file from a bitmap font class. I used Glyph Designer to create my bitmap font file, but you can use any bitmap font generator tool for this.

We’re adding two instance variables. The first stores the bitmap font configuration information which we’ll use to lookup the rect for each letter. The second stores our string that will be used as the list of letters to use for the particle generator. The particle generator will create the particles in the order of the letters in the string.

If you look at the implementation file, the first two methods are the getter and setter for the particleString property. The reason I created my own is that I need the custom setter to call the initTexCoordsWithRect: method every time the string is changed.

Next is our initialization method, we pass in all the information we’ll need to set up the particle generator. The font config file contains all the information about each letter in the bitmap, we’ll use this to lookup the rect for each letter. The plist file is the same that one would use to set up a CCParticleSystemQuad in Cocos2d. Finally, we pass in the string that we’ll use to create the letter sequence.

First the method sets the fntConfig variable using the input file. We then use this object and the class extension method to get the name of the atlasName property (name of the texture file) and set it to the texture of the particle generator. Then we set the particleString, which also calls the initTexCoordsWithRect: method.

The next method is the getLetterRect:(int)position method. We use the position of the particle in the particles array to lookup what letter it should be. This method seems to work, even though it’s not obvious why a particles position in the array should stay constant. I’ll have to investigate this further, but for now it works fine.

The particle system just starts the string over after having output all the letters. We use a modulo to get the position in the string if the array position is greater than the string length. Using the position we get the character (unichar) from the string. The CCBMFontConfiguration object has a struct that contains most of the information about the bitmap font, including the rect. The unichar character also serves as the index for the associated data in the struct that contains our rect info. We use this class and struct to lookup the rect.

The last method is the initTexCoordsWithRect. This method exists in the CCParticleSystemQuad class and normally it’s job is to set the texture coordinates for all the particles. Usually it’s the same rect for every particle and so the system passes in the rect. But, in our case we’ll just pass in CGRectZero and return a different rect for each particle.

The first thing we need to do is test whether we’ve set the particleString class. If this is nil it will crash, so we just abandon the method at that point. If the string is set, then we can proceed.

Most of this code is from the CCParticleSystem class. Cocos2d uses a structure called quads_ to hold the texture information it passes to Cocos2d. We’re assigning this data structure the values from the getLetterRect method. This will use a different section of our texture for each particle.

You can see that you could use the same logic with a texture atlas to create a particle system that uses different images. You’d just need to set the texture to your sprite sheet and change the getLetterRect method to return a rect from that sprite sheet.

Here’s a sample with all the code. It’s using Cocos2d 2.0, so you may need to modify one of the calls in order for it to work with Cocos2D 1.0, the line that sets the texture in the init method.

Change this:

[[CCTexture2D alloc] initWithImage:[UIImage imageNamed:[fntConfig atlasName]] resolutionType:kCCResolutionUnknown];

To this:

[[CCTexture2D alloc] initWithImage:[UIImage imageNamed:[fntConfig atlasName]]];

This isn’t a very robust class as I just took it this far, it probably needs some more testing, but I figured I’d put it out there for any that may benefit from it. Enjoy.

6 thoughts on “Cocos2D Particle Engine w/Different Sprites

  1. Pingback: List of Open Source Cocos2d Projects and Code Snippets | iUridium

  2. Pingback: List of Open Source Cocos2d Projects, Extensions and Code Snippets « AVATAR.Dev - iOS Developer Tips, Tricks and Tutorials.

  3. Reply Saj Mar 13,2012 5:15 am

    Can you register collision on this particle like registering it in the bounding box of the particle so we don’t kill the cpu? Thanks.

    • Reply admin Apr 21,2012 6:38 am

      I’m not sure what you are asking, but I’m thinking that adding collision detection would increase, not decrease, the cpu requirement . . . am I missing something?

  4. Reply uwidme Jun 14,2012 2:30 am

    I have cocos2d version ‘2.0 rc2’.
    In my version does not work.
    And this code is a free license?

  5. Reply akuba Jul 11,2013 10:03 pm

    where i can download ready particles files?

Leave a Reply