Making a Katamari Ship in Chipmunk
Chipmunk Physics really isn’t built for modifying bodies and shapes on the fly, but it works well enough. Here are the steps I use to make two things stick together. I will refer to two objects, Big and Little, each with a body. The big body has many “units” and the little body has only one which is centered at the little body’s center of mass. Each unit has one circular shape and other game-related functions like thrusting and shooting. All of these steps happen within the big body, so it will be referred to as “self.”
1. In the collision callback function, store the wrapper object in a list to be processed in the big object’s next update, otherwise risk a segfault.
2. Chipmunk seems to ignore the effects of colliding when this removal business happens, so calculate the new velocity vector of the big object based on the vectors and masses of both objects. I would do the same for angular velocity, but I don’t know how to do that, and it’s not that noticeable anyway.
For the physics-challenged among you:
vx_new = (m1*vx1+m2*vx2)/(m1+m2)
vy_new = (m1*vy1+m2*vy2)/(m1+m2)
3. Get the offset of the new shape with
big.body.world_to_local(little.body.position)
4. Name a new variable to avoid confusion:
new_unit = little.unit
5. Remove the little body and its unit’s associated shape from the pymunk space. Also remove the little object from any game-related thingies.
space.remove(little.body)
space.remove(little.unit.shape)
object_update_list.remove(little)
6. Add the new unit to the big object’s list of units. Give the unit a new shape associated with the big object’s body. Set its offset to what you got in step 3. Set its angle to the big object’s rotation added to the unit’s existing rotation to account for the fact that it will now be drawn with its rotation relative to the big object’s rotation. Add the new shape to the pymunk space. (I would show the code for this, but it’s all specific to gw0rp and therefore not pertinent to this guide.)
7. Update the center of mass of the big object (see below, it’s complicated).
8. Set the velocity of the big object’s body to what you got in step 2.
Updating the Center of Mass of a Katamari Object
In simple terms, it goes like this:
-Calculate the new center of mass. If you can’t do this, go back to high school physics. In gw0rp, it’s easy, because all masses are circular and have the same mass, so I can just average them. The new center of mass is at V1 relative to the old one.
-Also calculate the total mass and inertia. Just a sum of parts. the pymunk.moment_for_circle() function is my good friend here.
-Since the new center of mass is relative to the old center of mass, you can just subtract V1 from each offset to move it to the right place.
-Translate V1 to world coordinates and move the body there.
Unfortunately, it’s not that easy. In reality:
1. Calculate the new center of mass. Store a vector for the body’s new position:
new_body_position = self.body.local_to_world((new_com_x,new_com_y))
2. Remove all the body’s shapes from the pymunk space.
3. Store the object’s body as the “old body” for later copying
4. Remove the “old body” from the pymunk space
5. Create a new body with all the same attributes as the old one, except with position new_body_position.
6. Add the new body to the space.
7. Create new shapes with all the same attributes as the old shapes, except with the new center of mass position subtracted from the old offset. Add the shapes to the pymunk space.
I noticed today that there is a possible memory leak: when objects are stuck together, the old sprite object might persist. But I don’t care, since that doesn’t happen very often, relatively speaking, and I can clear all the lists between levels anyway.
In local news, my electric toothbrush has stopped working.