I hope that after reading this post, you might have a better understanding of cocos2d coordinates and how to relate them to chipmunk coordinates (I know I did after writing it!).
So, in order to do that, let's make a small example:
Let's build a sprite out of several sprites and let's call it the "camioncito" (little truck :-P), here's a picture of what would be the final sprite:

The camioncito is built out of 4 sub-sprites: the black box, the brown box and two wheels. The black box is 32x36 (width x height), the brown box is 52x7 and the wheels have a radius of 7 pixels. So, in order to lay them out to form the camioncito as pictured above, I'm thinking they should be in the following positions:
- black box: (0,0)
- brown box: (0, -21.5)
- left wheel: (-25, -21.5)
- right wheel: (25, -21.5)
Why those coordinates? Think of it as if initially, you would place them in the origin. The brown box center should be half the black box below (given that the black box is in the origin) and half it's height: -16 - 3.5 = -21.5. Each wheel's center should be 25 pixels from the origin and at the same height of the center of the brown box.
So, here's the code for that:
add_child Sprite.new("white.png").tap { |sp|
sp.position = [240,160]
} # too lazy to implement a ColorLayer :-P
add_child Sprite.new("black_box.png"), :tag => 0
add_child Sprite.new("brown_box.png"), :tag => 1
add_child Sprite.new("wheel.png"), :tag => 2
add_child Sprite.new("wheel.png"), :tag => 3
# set the position of each sub-sprite
@st_x = 240.0 # initial position: center of the screen (landscape)
@st_y = 160.0
positions = [
[ 0.0, 0.0],
[ 0.0, -21.5],
[-25.0, -21.5],
[ 25.0, -21.5]
]
(0..3).each { |i|
child_with_tag(i).tap do |c|
c.position = [@st_x + positions[i][0], @st_y + positions[i][1]]
end
}
Dissecting that: in the first three lines, I add a white background. Then, the sprites are added (with tags, in order to get them later). Then, we specify the initial coordinates (@st_x and @st_y). Finally (line 17), we iterate over each added child and add place it where it should be.
Now, let's add some physics to make things more interesting:
Let's roll the camioncito through the screen. For that to work, we need to simulate the camioncito like a "real" one, first, let's create the bodies:
- black box will be a poly shape with vertices (-16,18), (16,18), (16,-18), (-16,-18). Remeber that the order in which you specify the vertices is important: chipmunk needs a clockwise winding. Also, the shape needs to be convex, this is not a problem here, since we're defining a box :-). We chose those coordinates to keep the center of mass of the shape in the origin.
- brow box will also be a poly shape with vertices (-26,3.5), (26,3.5), (26,-3.5), (-26,-3.5). Again, those coordinates are chosen to keep the center of mass in the origin.
- the wheels are a simple circle shape of radius 7
The initial position of the bodies must match the initial position of the sprites, but if we add them just like that, they will not be "glued" together, we need to attach them, and to do that, chipmunk provides the joints. In this case, we need one pin joint for the black and brown boxes and two groove joints for the wheels to be glued to the brown box.
After creating the joints and adding all the bodies, shapes and joints to the space, we're ready to test it:
cpvzero = CP::Vec2.new(0.0,0.0)
# the black box
verts = [[-16,18],[16,18],[16,-18],[-16,-18]].map{|p| CP::Vec2.new(p[0],p[1])}
body1 = CP::Body.new(10, CP.moment_for_poly(10, verts, cpvzero))
shape1 = CP::Shape::Poly.new(body1, verts, cpvzero)
# the brown box
verts = [[-26,3.5],[26,3.5],[26,-3,5],[-26,-3.5]].map{|p| CP::Vec2.new(p[0],p[1])}
body2 = CP::Body.new(10, CP.moment_for_poly(10, verts, cpvzero))
shape2 = CP::Shape::Poly.new(body2, verts, cpvzero)
# the wheels
body_wheel1 = CP::Body.new(3, CP.moment_for_circle(3, 7, 0, cpvzero))
shape_wheel1 = CP::Shape::Circle.new(body_wheel1, 7, cpvzero)
body_wheel2 = CP::Body.new(3, CP.moment_for_circle(3, 7, 0, cpvzero))
shape_wheel2 = CP::Shape::Circle.new(body_wheel2, 7, cpvzero)
shape_wheel1.u = shape_wheel2.u = 0.5
# brown box and wheels in the same collision group
shape_wheel2.group = shape_wheel1.group = shape2.group = 1
# position each body
body1.p = CP::Vec2.new(@st_x - 0.0, @st_y - 0.0)
body2.p = CP::Vec2.new(@st_x - 0.0, @st_y - 21.5)
body_wheel1.p = CP::Vec2.new(@st_x - 25.0, @st_y - 21.5)
body_wheel2.p = CP::Vec2.new(@st_x + 25.0, @st_y - 21.5)
# create the joints
j1 = CP::Joint::Pin.new(body1, body2, cpvzero, cpvzero)
j2 = CP::Joint::Groove.new(body_wheel1, body2, CP::Vec2.new(0.0,0.1), CP::Vec2.new(0.0,0.0), CP::Vec2.new(-25.0, 0))
j3 = CP::Joint::Groove.new(body_wheel2, body2, CP::Vec2.new(0.0,0.1), CP::Vec2.new(0.0,0.0), CP::Vec2.new( 25.0, 0))
# add all bodies/shapes/joints to the space
[body1, body2, body_wheel1, body_wheel2].each { |b| $space.add_body(b) }
[shape1, shape2, shape_wheel1, shape_wheel2].each { |s| $space.add_shape(s) }
[j1, j2, j3].each { |j| $space.add_joint(j) }
# attach each body to the corresponding sprite
(0..3).each do |i|
child_with_tag(i).tap do |c|
c.attach_chipmunk_shape(shapes[i])
c.shape = shapes[i] if i == 0
end
end
You can learn more about joints in the official chipmunk documentation.
Just a comment about the
The groove joints for the wheel have a very small displacement, we need them there just to keep the wheels attached to the brown box.
Ok. So now, the camioncito is ready to run through your screen... But how do you move it? Let's add a simple touch-based effect: apply an impulse to move it to the right each time we touch it. I already did a small contribution on the code above: line 35 shows c.shape = shapes[i], which given the conditional there, will be shape1.shape = shapes[0]. But CocosNodes do not respond to the shape=() message... Ok, I'm starting to move in other direction.
When I touch the camioncito I want to apply an impulse on the body associated to the shape that is attached to the sprite. But I have no direct way to know what shape is associated to a given sprite. That's why I created a small module that will extend a simple Sprite class and add both the touch delegate methods and the shape association.
This is the module.
module TouchDelegate
def touch_began(touch)
pos = self.position
tpos = @scene.world_to_node_space(touch[:location])
if (tpos[1] - pos[0]).abs < 16 && (tpos[0] - pos[1]).abs < 18
# apply force to the black box
@shape.body.apply_impulse(CP::Vec2.new(1000, 0), CP::Vec2.new(0,0))
# claim touch
return false
end
true
end
def scene=(scene)
@scene = scene
end
def shape=(shape)
@shape = shape
end
end
Let's take a look at the touch_began() method. It's the method of a touch delegate. I have to register an object as a touch delegate with the Director (in ShinyCocos, in Cocos2D you would use the shared instance of the TouchDelegate). What I do first is to get the position of the current instance (which we will later realise it's the black box), convert the received touch to node space, using the @scene as a reference, then we compare the touch with our position. If it's within the box, we apply an impulse to the body of our shape and also, claim the touch by returning false. If the touch is not within the box, we return true and the touch is not claimed.
The other methods are just helpers to store the scene and the shape. So now, with this module, during initialisation I can extend a particular object with this module:
class TestCoordinates < Cocos2D::Scene
def initialize
add_child Sprite.new("tube.png").tap { |sp|
sp.position = [240,160]
} # half pipe background
add_child Sprite.new("black_box.png"), :tag => 0
add_child Sprite.new("brown_box.png"), :tag => 1
add_child Sprite.new("wheel.png"), :tag => 2
add_child Sprite.new("wheel.png"), :tag => 3
# set the position of each sub-sprite
@st_x = 45.0 # initial position
@st_y = 200.0
positions = [
[ 0.0, 0.0],
[ 0.0, -21.5],
[-25.0, -21.5],
[ 25.0, -21.5]
]
(0..3).each { |i|
child_with_tag(i).tap do |c|
c.position = [@st_x + positions[i][0], @st_y + positions[i][1]]
if i == 0 # only add black box as a touch delegate
c.extend(TouchDelegate)
c.scene = self
Director.add_touch_handler(c)
end
end
}
init_physics
add_physics
# no menu for now
#super
end
...
end
Take a loot at lines 23-25. Here, if we're talking about sprite 0 (the black box), we extend the object with the module, set the scene to the running scene and register the sprite as a touch handler. By extend I mean add the methods defined in the module TouchDelegate. If you're new to ruby, you can learn more about modules in this article.
Later on, on the initialisation, we init the physics and add the camioncito.
Ok. So now we can push the camioncito around our screen... Let's make one final adjustment to have even more fun: Let's put the camioncito in a half-pipe, just like those found in the skate games :-)
How would you do that? Well, for start, we could just use a simple half-ellipsis and add it as a segment shape to simulate the floor. But chipmunk does not have that kind of segment shape... So let's build it:
# just a simple formula for an oval
def oval(x, a, b, h, k)
k + -b * Math.sqrt(1 - ((x-h)/a) ** 2)
end
def init_physics
$space = CP::Space.new
$space.resize_static_hash(400, 40)
$space.resize_active_hash(100, 600)
$space.gravity = CP::Vec2.new(0.0, -98)
$space.iterations = 40
$space.elastic_iterations = $space.iterations
# add the floor, we want to simulate the "tube" in tube.png
# to do that, we will add lots of small segments
verts = [CP::Vec2.new(0, 5000)]
(0..480).each do |x|
verts << CP::Vec2.new(x, oval(x, 240.0, 160.0, 240.0, 160.0))
end
verts << CP::Vec2.new(480, 5000)
sbody = CP::Body.new(+1.0/0, +1.0/0) # big static body
first = verts.first
(1...verts.size).each do |i|
shape = CP::Shape::Segment.new(sbody, first, verts[i], 0.0)
shape.u = 1.0
$space.add_static_shape(shape)
first = verts[i]
end
# install default stepper
become_chipmunk_stepper
end
Let's process this step by step.
The function defined in line 1 is just a helper function that returns y for a given x, using the canonical formula for an ellipsis.
In the init_physics function, we create the space (which must be stored in the global variable $space), set some default values, and in line 15 is where the fun really begin. What we're going to do, is to make a discrete representation of the ellipsis, using intervals of 1.0 in the x-axis. The first point will be up high so that the camioncito won't escape when reaching the left border of the ellipsis, that's why the first element of the verts array is (0,5000).
Next, we iterate over the whole screen of the iphone (starting on 0, ending on 480) horizontally, and for each x, we add a new point (x, y(x)), where y(x) is calculated using the oval function.
Finally, we add the last point, which has the same effect as the first one: prevent that the camioncito from going out of the screen.
After that, we create a static body (infinite mass, infinite inertia), and iterate over all of the points, creating a segment shape between two of them and adding it to the space as a static shape.
Then, we install the default chipmunk stepper (line 30).
Oh, I forgot... I replaced the blank background with this:

Which resembles the half ellipsis where our camioncito will run. Anyway... this post is getting too long, so after all the talking and coding, here's a video of the camioncito running on the simulator!
The whole source of this example is in the test project of ShinyCocos, check test_coordinates.rb