Safe way to remove objects during callbacks.

Discuss new features and future development.
Post Reply
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Safe way to remove objects during callbacks.

Post by slembcke »

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:
  • 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 first reason I realized was just dumb. With no way to control what order collisions are considered in, you don't really know what collisions involving the same object were processed before and which will be processed afterwards anyway. Might as well just treat them all the same. The second reason I was able to come up with a satisfactory solution to. Use generic callbacks, but tie them to a particular object so that you can only register an object once.

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);
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?
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
mobilebros
Posts: 90
Joined: Tue Aug 04, 2009 9:53 am
Contact:

Re: Safe way to remove objects during callbacks.

Post by mobilebros »

I give it a thumbs up, it's generic and flexible like you said and will solve one of the most basic problems easily, I see no negatives. :D
zaerl
Posts: 40
Joined: Fri Aug 28, 2009 8:36 am
Contact:

Re: Safe way to remove objects during callbacks.

Post by zaerl »

This is a very nice add. I used to delay the removal of objects after each cpSpaceStep but now I will use the new API. Thanks!
maximile
Posts: 157
Joined: Mon Aug 20, 2007 12:53 pm
Location: London, UK
Contact:

Re: Safe way to remove objects during callbacks.

Post by maximile »

What's the point of having collision callbacks that aren't delayed? I'd replace the existing functions; "delayed" makes it sound like you can add a time or frame delay and I bet you'd still get people removing shapes/bodies in a normal collision callback.
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Safe way to remove objects during callbacks.

Post by slembcke »

If they are delayed, you can't conditionally reject collisions because they would be processed already.

OneSadCookie mentioned that I should rename it as well. He suggested calling them post step callbacks instead of delayed callbacks. I'll probably do that.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
maximile
Posts: 157
Joined: Mon Aug 20, 2007 12:53 pm
Location: London, UK
Contact:

Re: Safe way to remove objects during callbacks.

Post by maximile »

Good point, I forgot about that.

I don't think it's going to suit me, but it sounds like it'll be useful to some people.
zaerl
Posts: 40
Joined: Fri Aug 28, 2009 8:36 am
Contact:

Re: Safe way to remove objects during callbacks.

Post by zaerl »

Hi slembcke. We have cpSpaceAddDelayedCallback on cpSpace.c but no forward declaration in cpSpace.h. It's the same for removeAndFreeShapeAndBody and cpSpaceDelayedRemoveAndFreeShapeAndBody. You will make all of them accessible from users' code?

cpSpaceAddPostCallback is a better name for me too.
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Safe way to remove objects during callbacks.

Post by slembcke »

I'll add the declarations once I settle on the API for certain. Which will probably just be a final renaming of it to post step callbacks.

@maximile Actually having only "delayed" callbacks wouldn't work for the same reason that you can't remove objects in normal callbacks. The reason why the callbacks work is because you only have a single callback registered per object. You can't remove and free something, then find a second collision later and try to access the object you freed.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
Post Reply

Who is online

Users browsing this forum: No registered users and 6 guests