The Chipmunk SpaceManager

Cocos2d and Chipmunk

Cocos2d-iphone and Chipmunk

A while ago when I (MobileBros) was just beginning to use Cocos2d-iphone, I also started playing around with Chipmunk; at the time this was the only physics library that came with Cocos2d and I wasn’t really aware of anything else out there. Very quickly I discovered that I was writing a lot of repetitive code in order to use Chipmunk and what was more was that it was in C, making me have consider two languages now! Sound familar?

Chipmunk:
Chipmunk is actually very simple in concept. There is a chipmunk “space”, you add shapes/bodies/constraints to the space, you ask the space to simulate a specific amount of time. Thats it! The last step performs all the magic that is rigid-body physics.

Cocos2d Integration – Basic Approach:
Cocos2d is very good at a lot of things, its a very good at displaying sprites, particles, actions, touches, and a number of other things. Chipmunk is very good a simulating rigid-body physics. The common approach to “glueing” both components together usually involves running chipmunk and telling it to update Cocos2d nodes (setting position and rotation); pretty much a basic model-view approach. Modifying the constraints, and modifying collision behavior in the chipmunk system would result in different behavior; your game logic.

The SpaceManager attempts to step in as this glue and automate much of the process.

What is SpaceManager?

There are actually two distinct parts of the Chipmunk-SpaceManager project. There is the core SpaceManager class and then there are a number of “helper” classes targeting Cocos2d-iphone and used for representing shapes and constraints with CCNode types. In fact most people do not realize that these two parts are independent of each other.

Core:
The goal of the core SpaceManager class was to provide an encapsulation around the basic tasks we (at least) found ourselves performing over and over. Theses tasks included:

  • making basic shapes (rects, circles, polys)
  • removing shapes (especially a mechanism for during collisions)
  • advancing time within the chipmunk space
  • negligible API difference in Static and Active shapes
  • updating our views with our “model” (Chipmunk –> Cocos2d)
  • updating our model with our controller/view (Cocos2d –> Chipmunk)
  • non invasive to chipmunk (you can combine native chipmunk calls with spacemanager calls)
  • window containment walls (sounds dumb, but it’s tedious)

Our aim was not exactly to wrap exising objects and functionality (In fact slembcke just released an obj-c wrapper suite). Instead we wanted something to manage common tasks/patterns.

Cocos2d Helpers:
The goal of the “helper” classes was to provide a simple solution to associating a shape/constraint with a corresponding CCNode type. By doing this we could also accomplish a number of other common tasks including: automatic cleanup of shape/body from chipmunk upon node deletion, as well as being able to control the position and rotation of shapes thru CCAction's or even just calling setPosition or setRotation on the node.

cpShapeNode and cpConstraintNode are somewhat special in that they will draw their corresponding shape/constraint respectfully. For instance you just created a cpConstraintNode with a spring constraint, by adding this node type to a CCLayer or other CCNode it will correctly draw a spring constraint between the attached bodies (it uses OpenGL to draw a zig-zag line).

A base class called cpCCNode is provided for anyone wishing to derive their own class implementation. It provides:

  • shape property: your associated shape
  • integrationDt: if other than zero, any call to setPosition (not coming from chipmunk) will result in velocity being calculated
  • spaceManager: a reference to the owning spaceManager
  • autoFreeShape: cleanup the shape, needs the spaceManager property set
  • applyImpulse: apply an impulse to the shape’s body
  • applyForce: apply a continuous force to the shape’s body
  • resetForces: reset any continuous forces to zero

Give me an Example!

Lets assume you are working on a CCLayer that you’ve just subclassed and want to integrate Chipmunk with. This layer will act as your space and any CCNodes added will represent your shapes and constraints.

First off, lets create our space and add a containment rect (so other shapes can’t go offscreen).

smgr = [[SpaceManager alloc] init];       //SpaceManager* smgr declared in header file
[smgr addWindowContainmentWithFriction:0.8 elasticity:0.7 inset:cpvzero];

Well it doesn’t look like much, but chipmunk was just initialized and a cpSpace was created and set up with default values and gravity. We then created a containment rect, thats somewhat bouncy with the friction coefficient of concrete, a concrete room if you will.

Our First Shape:
Well thats all well and good, but lets add a ball next; lets use a cpShapeNode (a “helper” class) for this example because it only uses OpenGL primative calls to draw itself.

cpShape *ball = [smgr addCircleAt:cpv(240,160) mass:5.0 radius:15];
 
ballNode = [cpShapeNode nodeWithShape:ball];       //cpShapeNode *ballNode declared in header file
ballNode.color = ccBLUE;
 
ballNode.autoFreeShape = YES;
ballNode.spaceManager = smgr;
 
[self addChild:ballNode];

We just asked the SpaceManager to create a circle shape for us, placed in the middle of the screen with a mass of 5 and a radius of 15; it gave us back a cpShape* which we then attached it to a cpShapeNode, colored it blue, and added to self (our CCLayer). Also important here is that we told the ballNode to auto-free it’s shape (when ballNode is released) and also who our SpaceManager instance was. This frees you from worrying about clean-up later.

Crank it up:
At this point, things are starting to sound interesting, however if you ran the code right now, you’d wonder why there is a blue circle just sitting in the middle of the screen. Oh yeah! We need to tell chipmunk to start simulating our space.

[smgr start:1.0/60.0];

Woo-Hoo! We did it, a blue ball should fall from the center of the screen and bounce up and down a few times on the bottom of the screen. Well ok, thats not very interesting is it.

Touches:
Perhaps we can make it do something in our touch methods. Don’t forget to set up your layer as a touch delegate…

-(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent*)event
{
   CGPoint pt = [self convertTouchToNodeSpace:touch];
 
   [ballNode applyImpulse:ccpMult(ccpSub(pt, ballNode.position), 3)];
 
   return YES;
}

This will give the ball a jolt in the direction you touch, stronger the further away you touch. Now if you only had a target to hit…. you’d have a game!

A Game Idea…
$1,000,000 Game Idea: A ball and a target; touch to launch the ball at the target; the less touches it takes to hit the target, the more points you get!

Ok so lets get a target going, perhaps a nice red square?

cpShape *target = [smgr addRectAt:cpv(440,160) mass:STATIC_MASS width:20 height:20 rotation:0];
 
targetNode = [cpShapeNode nodeWithShape:target];       //cpShapeNode *targetNode declared in header file
targetNode.color = ccRED;
 
targetNode.autoFreeShape = YES;
targetNode.spaceManager = smgr;
 
[self addChild:targetNode];

Nothing too dramatic here, EXCEPT whats this STATIC_MASS thing? Well in chipmunk terms STATIC_MASS == INFINITY, the former just seemed a little more definitive sounding. In chipmunk land there are shapes that are “active” and then there are shapes that are “static”, static shapes are never supposed to move; they are immovable as far as chipmunk sees things. Well what better use than for our target rectangle shape? Passing a STATIC_MASS for mass will make the SpaceManager perform the necessities it takes to set one up, everything else will look the same as when we created our ball above.

Collision Callbacks:
We now have a ball, touch logic, and a target. All we need now is to know when the target has been hit. First off, we need to define a method to handle this.

-(BOOL) handleCollision:(CollisionMoment)moment arbiter:(cpArbiter*)arb space:(cpSpace*)space
{
   if (moment == COLLISION_BEGIN)
   {
      CCLabel *label = [CCLabel labelWithString:@"You Win!" fontName:@"Helvetica" fontSize:32];
      label.position = ccp(240,160);
      label.color = ccBLACK;
      [self addChild:label];
}
 
//other moments: COLLISION_PRESOLVE, COLLISION_POSTSOLVE, COLLISION_SEPARATE
 
return YES;
}

If you’re familar with chipmunk callback functions, you’ll notice that this is slightly similar but you are now passed a CollisionMoment variable as well. This enum tells you what collision moment we are at, in this case we only care about when the objects first begin touching.

Now don’t forget…. we need to register this callback with our SpaceManager instance:

ballNode.shape->collision_type = 1;
targetNode.shape->collision_type = 2;
 
[smgr addCollisionCallbackBetweenType:1
                            otherType:2
                               target:self
                             selector:@selector(handleCollision:arbiter:space:)];

Thats it! If we just keep track of how many touches, perhaps display them too our game is all done. If anyone is interested in this example, the complete source is given here: SpaceManager Example

The SpaceManager can do a whole lot more than discussed here including: scheduling shapes for deletion, fragmenting shapes, morphing shapes between active and static, detecting persistent contacts, etc. The project web page can be found at http://code.google.com/p/chipmunk-spacemanager/ and a more in-depth example is included with the source code.

20 Responses to “The Chipmunk SpaceManager”


  • Great stuff MobileBros! I created a similar approach, but you really went all out encapsulating the Chipmunk functionality. Good job!

  • Great Job!,thanks !!!

  • Thanks for this very cool. Based on your example, I’m trying to detect mouse clicks inside boxes. Even when clicking right in the boxes I’m not getting a point intersection and I don’t know why. Other than that it’s very cool:

    -(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent*)event
    {
    	CGPoint pt = [self convertTouchToNodeSpace:touch];
     
    	CGPoint location = [touch locationInView:[touch view]];
    	location = [[CCDirector sharedDirector] convertToGL:location];
     
    	for (cpShapeNode *shn in boxArray) {
    		NSLog(@"rect2: %@", NSStringFromCGRect([shn boundingBox]));
     
    		NSLog(@"lcoation x:%f y:%f", location.x, location.y);
    		NSLog(@"point x:%f y:%f", shn.position.x, shn.position.y);
    		if (CGRectContainsPoint([shn boundingBox],location)) {
    			NSLog(@"collision");
    			[smgr removeAndFreeShape:shn.shape];
    			[self removeChild:shn cleanup:YES];
    			NSLog(@"point contained");
    		}
    	}
     
    	[ballNode applyImpulse:ccpMult(ccpSub(pt, ballNode.position), 3)];
     
    	[touchLabel setString:[NSString stringWithFormat:@"%d", ++touchCounter]];
     
    	 return YES;
    }
  • Ah I’m glad you pointed this out, in fact cpShapeNode makes no use of boundingRect, though it probably should.

    However, there is a better, more efficient way to do what you’re doing:

    cpShape *shape = [smgr getShapeAt:pt];
     
    if (shape)
    {
       cpShapeNode *shn = (cpShapeNode*)shape->data;
       [smgr removeAndFreeShape:shape];
       [smgr removeChild cleanup:YES];
    }

    That should do it. By the way, if you set the spaceManager property and autoFreeShape property (to YES) on the cpShapeNode, then you only need to call removeChild and don’t need to worry about calling removeAndFreeShape.

  • Thank you so much for the informative and perfect reply. Also, sorry, did not realize we could use code tags here.

  • No problem davecom, I edited your reply to use the code markup. Thank you Rolandas, Zenkimoto, and Sky for your supportive comments. :)

  • Very cool stuff :)
    Thank you very much for this, helps me a lot.

    Keep up the good work !

  • Quick FYI, if anybody chooses to use the code above, it’s important to also check shape is not nil. Thanks again guys for developing this framework.

    #edit [MobileBros] Thanks davecom, the code had the wrong check, it was edited to be correct

  • BTW – I’ve been learning spacemanager and posting on the developer’s forum. The developer has been super responsive, which is awesome. Also, there are a few spacemanager questions on stackoverflow.com

  • Thanks if your ever in Charlotte i will buy you a beer for this!

  • Thanks, I’ll hold you to it ;)

  • Hey,
    I managed to implement the game, in my application. but im having an issue with it, If I tap around a lot and make the ball go at a very high speed.. the ball moves out of the screen bounds and disappears. Also if a put a breakpoint in the collision handler function, the ball disappears.. has anyone had a similar issue?
    Any ideas how to fix this?
    Thanks in advance
    S

  • It’s a known limitation of chipmunk; chipmunk does not employ continuous collision detection, therefore if the dt is too large or velocity is too high things may have their new positions calculated past the point of a collision, in your case you probably just have a thin bounds…. I say you have two options either increase the width of your walls (which only solves the one problem) or increase the number of steps the SpaceManager performs (default is 2).

    smgr.steps = 10;

    Good luck, and I would tend to ask these types of questions in the forum (also can ask at mobile-bros.com forum too)

  • Hi,

    A big thanks for this wrapper! I am trying something wacky…what I want to be able to do is to do soft-body physics with your wrapper. So basically what I want is to create a soft-body composed of N number of smaller bodies attached with some constraint(s). I may have a couple of these soft-bodies floating in the containment rect, so I also want to add collision handler between them as well.

    Do you think it’s doable? If so do you have any example handy?

    Thanks much in advance!

  • Sure it’s possible, even though its a “wrapper” somewhat you still have access to the chipmunk-c api and can be mixed freely. So yes you’d be doing exactly what you described, creating some shape/bodies and attaching constraints. Sorry no example for your exact situation… but I think Riq actually posted something on the forum side a week or so ago talking about squishy bodies like the one your describing.

  • Recently i was developing a small game like skywire (a tram slide along the static track) by using the chipmunk spacemanager , i used two bodies with circle

    shape which act as two wheels ,and a pin joint to connect the two bodies , then there is the static track between two wheels , then i hang the tram on the

    lower wheels , but the problem is the equipment shakes heavily when i give the two wheels the initial speed(the same for two ) , i think the structure i chose must be wrong ,
    and i think maybe the gear would help ,but how to use the gear ?
    here are the skywire for free :
    http://www.miniclip.com/games/skywire/en/
    thanks!

  • how many kinds of attributes does the body has? and how do they work?

  • how to add two or more shapes for one cpsprite in spacemanager?
    thx!

Leave a Reply




Social Widgets powered by AB-WebLog.com.