Safe way to remove objects during callbacks.
Posted: Mon Nov 09, 2009 2:18 pm
The problem: You cannot remove/free things from a cpSpace from within a callback. The most obvious reason for wanting to do this is destroying objects that touch each other from within a collision callback.
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:
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.
Because it doesn't care what types you are using, you could use your game's game object type as 'obj' and your game scene as the data context pointer and do pretty much the same thing. Another bonus is that the contact information, collision force strength and such, will be calculated by the time the delayed callback is called making it easy to conditionally handle a collision based on collision strength.
I'm happy enough that this solution is generic, flexible and simple. What do you all think?
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?