Home > Game Demo Tutorial Series > Game Demo Tutorial #3: Collisions & Particle Systems

Game Demo Tutorial #3: Collisions & Particle Systems

April 26th, 2009

In this entry I am going to cover using Chipmunk's collision pair function. This function is used to set up a collision test between two objects. If Chipmunk detects that two predefined objects have collided, the callback function that was passed to the collision pair function gets called. To show this we are going to extend the code from the last tutorial, adding a rock and a particle system.

The first thing we are going to do is add rock.png to our resources. Rock.png can be found in the source code for this entry. Next, we're going to add a method called makeSpaceRock to our GameLayer class within GameScene.m. Here is the code you need to add:

 
-(void) makeSpaceRockX: (float) x y:(float)y
{
	Sprite *rock = [[Sprite spriteWithFile:@"rock.png"] retain];
	rock.position = cpv(x,y);
	[self addChild: rock];
 
	cpVect verts[] = {
		cpv(-54,-43),
		cpv(-54, 43),
		cpv(54, 43),
		cpv(54,-43),
	};
 
	cpBody *rockBody = cpBodyNew(200.0f, INFINITY);
	rockBody->p = cpv(x, y);
	rockBody->v = cpv(0, 0);
 
	cpSpaceAddBody(space, rockBody);
 
	cpShape * rockShape = cpPolyShapeNew(rockBody, 4, verts, cpvzero);
	rockShape->e = 0.9f; rockShape->u = 0.9f;
	rockShape->data = rock;
	rockShape->collision_type = 0; //New!
	cpSpaceAddShape(space, rockShape);
 
	cpSpaceAddCollisionPairFunc(space, 0, 1, &bulletCollision, self); //new!
 
}
 

As you can see this method contains to lines of code we haven't seen before. We are setting a property called collision_type on rockShape and then calling cpSpaceAddCollisionPairFunc. collision_type is how we identify a particular object for the collision pair function. In order for everything to work properly you'll need to start numbering your collision_types from 0. rockShape will be numbered with a 0 and our bullets will be numbered with a 1. cpSpaceAddCollisionPairFunc takes the following arguments: the current simulation space, object A (as specified by collision_type), object B, C callback function to be called when a collision is detected, and then a data parameter. The last parameter is optional and could be left as null, but you can pass self in to get access to your GameLayer object during runtime.

Next we need to modify our method that creates bullets, makeBulletX. Here is the line you need to add:

 
laserShape->data = laser;
	laserShape->collision_type = 1; //this is new!
	cpSpaceAddShape(space, laserShape);
 

Now we need to see up our callback function, which we declared as bulletCollision in cpSpaceAddCollisionPairFunc. This C function will be located in GameScene.m, but outside of our classes, just like the function eachShape(void *ptr, void* unused). After the eachShape function add the following code:

 
static int bulletCollision(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data)
{
	NSLog(@"Collision Detected");
	return 0;
}
 

Now we need to add a rock to the screen to test out our collision. In the init method of GameLayer add the following line:

 
[self makeSpaceRockX:200 y:200];
 

If you build and run the application you should now see a rock on screen. You should be able to shoot that rock with laser bullets and have the collision show up in the console. Hopefully everything worked for you. If not you can always grab the fully working source code at the end of this entry.

Next up we are going to flesh out our bullet collisions a little bit more. We want the rock to disappear when a bullet collides with it and we want an explosion to occur. First we want to add the following method to our GameLayer class:

 
-(void) createExplosionX: (float) x y: (float) y
{
	ParticleSystem *emitter = [ParticleExplosion node];
	emitter.position = cpv(x,y);
	[self addChild: emitter];
}
 

Now we want to go back to our collision callback function (bulletCollision) and modify its code to this:

 
GameLayer *game = (GameLayer*) data;
	[game createExplosionX:200 y:200];
	return 0;
 

Now if you run the application you should be able to shoot the rock and generate an explosion. This is great, but as you can see the explosion doesn't really look that great. It runs a little slow and the colors are wacky. To fix this we're going to create our own particle system. Right click on the Classes folder in X-Code and select Add->New File. Choose NSObject subclass and name it RockExplosion. Change RockExplosion.h to this:

 
#import "PointParticleSystem.h"
#import "QuadParticleSystem.h"
 
@interface RockExplosion : PointParticleSystem
{
}
@end
 

As you can see we are going to be extending Cocos2D's PointParticleSystem class.

In the implementation file add the following code:

 
#import "RockExplosion.h"
#import "TextureMgr.h"
#import "Director.h"
 
@implementation RockExplosion
-(id) init
{
	return [self initWithTotalParticles:2];
}
 
-(id) initWithTotalParticles:(int)p
{
	if( !(self=[super initWithTotalParticles:p]) )
		return nil;
 
	// duration
	duration = 0.1f;
 
	// gravity
	gravity.x = 10;
	gravity.y = 10;
 
	// angle
	angle = 90;
	angleVar = 360;
 
	// speed of particles
	speed = 200;
	speedVar = 40;
 
	// radial
	radialAccel = 0;
	radialAccelVar = 0;
 
	// tagential
	tangentialAccel = 0;
	tangentialAccelVar = 0;
 
	// emitter position
	position.x = 160;
	position.y = 240;
	posVar.x = 0;
	posVar.y = 0;
 
	// life of particles
	life = 10.0f;
	lifeVar = 2;
 
	// size, in pixels
	startSize = 40.0f;
	startSizeVar = 20.0f;
 
	// emits per second
	emissionRate = totalParticles/duration;
 
	// color of particles
	startColor.r = 0.99f;
	startColor.g = 0.99f;
	startColor.b = 0.99f;
	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 = 0.0f;
	endColorVar.a = 0.0f;
 
	self.texture = [[TextureMgr sharedTextureMgr] addImage: @"rock.png"];
 
	// additive
	blendAdditive = NO;
 
	return self;
}
 
@end
 

Now we need to go back and modify our createExplosion method in GameLayer. The ParticleSystem should now be created from RockExplosion instead of ParticleExplosion. The line of code should be:

 
ParticleSystem *emitter = [RockExplosion node];
 

Also, you will need to go into GameScene.h and add #import RockExplosion.h.

Great! Now we have a nice little explosion when the lasers hit our rock. Let's go back and actually get rid of the space rock when a laser hits it. In GameScene.m, change the code inside the bulletCollision function to this:

 
	GameLayer *game = (GameLayer*) data;
	a->body->p = cpv(800,800);
	b->body->p = cpv(800,800);
	[game createExplosionX:200 y:200];
	return 0;
 

This code will move the space rock and bullet that hit it off screen, leaving only the exploding rock chunks. Compile and run your code, looks cool, huh?

SOURCE CODE: Game Demo Tutorial Entry 3 Source Code

rjett Game Demo Tutorial Series , , ,

  1. April 29th, 2009 at 08:51 | #1

    Very nice tutorial. It is good to see a good introduction to physics using chipmunk.

    Thanks.

  2. May 2nd, 2009 at 14:02 | #2

    Hi again,

    A couple of small fixes.

    In GameScene.h, add another two prototypes:

    - (void)makeSpaceRockX:(float)x y:(float)y;
    - (void)createExplosionX:(float)x y:(float)y;

    and import:

    #import “RockExplosion.h”

    RockExplosion.h should read:

    #import “ParticleSystems.h”
    @interface RockExplosion : ParticleExplosion {

    In RockExplosion.m, the size declarations should look like:

    // Size, in pixels
    size = 40.0f;
    sizeVar = 20.0f;

    Cheers,
    James

  3. Austin
    May 19th, 2009 at 20:27 | #3

    Does anyone know how to fire the bullet from a button instead of tapping the screen? I have the button on the screen but I’m having trouble with the next part.

  4. June 19th, 2009 at 07:49 | #4

    Thanks- these are great tutorials- served as a brilliant introduction into the world of cocos and physics! i have a question though- i’ve modified the code so the ship itself is also physics enabled sprite, like the bullet and i’m trying to write it so the ship will move to where you click. Whats the best way to acheive this- is there some kind of moveTo function for chipmunk in the same way you have to standard cocos2d sprites? at the moment i set the velocity of the object then kick off a timer to check if the ship is within +/- 3 pixels of where i asked, then stop, but it doesn’t seem like the right way to do it?

    Many thanks, Shaw.

  5. QW
    July 8th, 2009 at 15:37 | #5

    This is good, thanks for the work. one thing in the last part. why there is “->” in a objective-c m file? it is C++?

    Again, very good and looking forward for more.

  6. July 17th, 2009 at 12:42 | #6

    I create Sprite monstro and add on Space:

    Sprite *monstro = [[Sprite spriteWithFile:@"m5.png"] retain];

    monstro.position = cpv(x,y);
    [self addChild: monstro];

    cpBody *monstroBody = cpBodyNew(200.0f, INFINITY);
    monstroBody ->p = cpv(x, y);

    cpBodySlew(monstroBody, cpv(160,240));
    cpSpaceAddBody(space, rockBody);

    cpShape * monstroShape = cpCircleShapeNew(monstroBody, 10.0, cpvzero);

    monstroShape ->e = 0.9f; monstroShape->u = 0.9f;
    monstroShape ->data = monstro;
    monstroShape ->collision_type = 0; //New!

    cpSpaceAddShape(space, monstroShape );

    cpSpaceAddCollisionPairFunc(space, 0, 1, &bulletCollision, self); //new!

    Animation good. When
    When an object (monstro) moves outside the screen, again move to the screen:
    monstroBody->p = cpv(x,y);
    monstroBody->v = cpv(x,y);
    But the object moves fast, no means by function cpBodySlew.
    How correct to use the function cpBodySlew.
    Example please.
    Sorry for the language.

  7. imrank1
    July 19th, 2009 at 14:19 | #7

    Hey, this is a great tutorial. I am trying to do somthing similar where I have a character positioned at the bottom of the screen ( like where you have the ship ). However the character does not move. He is anchored in that position. Whenever the user touches the screen. I want to the sprite to rotate to that position and fire off a bullet in that direction. My sprite rotates correctly so that character looks like he rotates to the correct position however my code to apply a velocity vector to the bullets is not working. Do you know a good way to do this?

    Here is my touchesBegan method :

    - (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {

    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView: [touch view]];

    point = [[Director sharedDirector] convertCoordinate: point];
    NSLog(@”Clicked x=%f y=%f”, point.x, point.y);
    //x1 and y1 represent the place where the soldier is on the screen
    double x1 = 150 ;
    double y1 = 50;
    double y2 = point.y;
    double x2 = point.x;

    double soldierAngle ;
    if ( y2 < 50 && x2 <150)
    {
    //prevent from rotating past -90 degrees
    soldierAngle = -90;

    }
    else if ( y2 150)
    {
    // prevent from rotating past 90 degrees
    soldierAngle = 90;

    }
    else if ( x2 150 )
    {
    //if user touched less than 180
    soldierAngle = atan ( (x2-x1)/(y2-y1)) * 180/PI;

    }

    NSLog ( @”Soldier Angle is %f”, soldierAngle ) ;

    [soldier runAction:[RotateTo actionWithDuration:0.0
    angle:soldierAngle]];

    for(Bullet *b in bullets)
    {
    if([b ready])
    {

    [b fireFromTo:soldier.position.x y:soldier.position.y a:(CC_DEGREES_TO_RADIANS(-soldierAngle))];
    [b setReady: NO];
    break;
    }
    }

    return kEventHandled;
    }

    And here is my fireBullets Method :

    -(void)fireFromTo: (float)x y:(float)y a:(float)a
    {
    bulletBody->p = cpv(x,y);
    bulletBody->v = cpv(cos(a)*320,sin(a)*480);

    }

    Thanks for the help.

  8. July 22nd, 2009 at 21:29 | #8

    Great run through … looking forward to future articles. May I suggest things like enemies, power-ups, and score systems?

  9. July 31st, 2009 at 02:58 | #9

    Im using cocos2d 0.8 and i get a build error on the RockExplosion.m – it seems position.x and position.y no longer exist! Commenting them out fixed it but i’m curious as to what they were supposed to do?

    Also, the position of the emitter is hardcoded, so i changed that to use the position of the bullet instead in bulletCollision:

    GameLayer *game = (GameLayer*) data;
    [game createExplosionX:a->body->p.x y:a->body->p.y];
    a->body->p = cpv(800,800);
    b->body->p = cpv(800,800);
    return 0;

    Wil you be writing any more tutorials, you seem to be good at them!

  10. Mark
    September 2nd, 2009 at 18:05 | #10

    Hi,

    Thank you very much for taking the time to write this tutorial. I learned a lot from this. I wanted to see what this would be like in landscape view, so I simply added this line to the AppDelegate:

    [[Director sharedDirector] setLandscape:YES];

    and added the drawshapes functions to the ‘eachShape’ function GameNode.m to see the chipmunk bodies:

    When the view is set to landscape, I would have expected the chipmunk bodies to have rotated as well, but they haven’t. I am a complete noob, and was wondering if you could tell me if there is an easy way to synchronize the sprites with the chipmunk bodies when the view is rotated? Otherwise, would it be possible for you to explain how to accomplish this. I’ve looked around for several hours now and cannot find much helpful information on this. Maybe I’m looking in the wrong places or missing something really simple?

    Here are two photos showing the issue.

    This is showing the code as you have it, just with the addition of drawing the chipmunk bodies. All is well here.
    http://i753.photobucket.com/albums/xx175/effervescent_photos/Picture3.png

    This is showing the code as you have it, with the view set to landscape, and drawing the chipmunk bodies. The chipmunk bodies and the sprites are out of sync, rotated by 90 degrees, it looks like.
    http://i753.photobucket.com/albums/xx175/effervescent_photos/Picture5.png

    Thanks!

  11. rjett
    September 6th, 2009 at 00:43 | #11

    Thanks. I’m pretty busy as of late, but I may try to get something up soon! :)

  12. rjett
    September 6th, 2009 at 00:46 | #12

    The -> is a part of C, which technically is part of Objective-C. It is used to access a field in a struct. Chipmunk, one of the physics engines that comes packaged with Cocos2d, is written in C.

  13. rjett
    September 6th, 2009 at 00:49 | #13

    Yeah.. just use the body, like cpBody->p = cpv(newX, newY);

  14. rjett
    September 6th, 2009 at 00:59 | #14

    I’m not sure. You might need to look in to the willRotateToInterfaceOrientation function. Abstractly, you should be able to listen for a change in orientation and then alert your app. You app can then rotate the bodies. You could use body->a for this I think. I’m not positive though. I haven’t touched this stuff in a few months I’ve been rather busy.

  1. No trackbacks yet.