The answer to solve this has been pretty clear for quite a while; queue up objects and remove them right before exiting cpSpaceStep(). I really really didn't want to implement it this way for two reasons:
- I really wanted the user to be able to remove a shape an not have any more collisions for that shape to be considered. I finally came to realize that this was a really dumb reason.
- Making a list of objects wasn't general enough. Sure I could remove chipmunk objects right away, but what about removing game objects from my scene? Ideally I would want to define removing chipmunk objects owned by a game object only when removing that game object and not have to worry about if the chipmunk objects are already taken care of. I had considered just making a list of user callbacks, but then the user would need to be careful not to double remove or free objects if they were involved in more than one collision.
The API is basically a single new function, and a definition for a callback type. You add a delayed callback tied to a particular objct to the space to be called just before cpSpaceStep() exits. You can also pass the callback a context 'data' value.
In the following example, a cpShape is being used as 'obj' and a cpSpace as the 'data' value.
Code: Select all
typedef void (*cpDelayedCallbackFunc)(void *obj, void *data);
// New space function to add callbacks delayed until just before cpSpaceStep() returns.
void cpSpaceAddDelayedCallback(cpSpace *space, cpDelayedCallbackFunc func, void *obj, void *data);
static void
delayedCallback(cpShape *shape, cpSpace *space)
{
cpSpaceRemoveBody(space, shape->body);
cpBodyFree(shape->body);
cpSpaceRemoveShape(space, shape);
cpShapeFree(shape);
}
static int
collisionCallback(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, cpSpace *space)
{
// The callback is keyed by the 'obj' ('a' in this case) parameter. So delayedCallback will only be called once for 'a')
cpSpaceAddDelayedCallback(space, (cpDelayedCallbackFunc)delayedCallback, a, space);
cpSpaceAddDelayedCallback(space, (cpDelayedCallbackFunc)delayedCallback, a, space);
cpSpaceAddDelayedCallback(space, (cpDelayedCallbackFunc)delayedCallback, a, space);
return 0;
}
// Define the collision callback somewhere
cpSpaceAddCollisionPairFunc(space, 1, 2, (cpCollFunc)collisionCallback, space);
I'm happy enough that this solution is generic, flexible and simple. What do you all think?