box2d icon indicating copy to clipboard operation
box2d copied to clipboard

Allow structural changes during collision callbacks

Open Troid92 opened this issue 6 years ago • 3 comments

As it is right now, any world structural changes (i.e. adding or removing joints, fixtures, and bodies) during collision callbacks are not permitted. The official recommendation in the manual is to "buffer all contact data that you care about and process it after the time step."

I am wondering if there is any particular reason why Box2D does not buffer structural changes itself. As far as I can tell there is no performance reason preventing this, as the developer's code will be buffering these things anyway, and structural changes won't typically be happening in mass amounts frame after frame. I also cannot see any general logical implementation obstacle making such a feature impossible, as with some effort I was able to implement this feature in a Lua physics wrapper I've been working on.

I would actually argue that this could be considered a design flaw, since it goes against how people tend to conceptualize actions and consequences. Take for example, the following psuedo-code in Lua for a classic Snake clone:

function snakeBody:BeginCollide(other, contact)
    if (other.isFood) then
        self:attachToTail(NewTailPiece())
        other:teleportElsewhere()
    end
end

In vanilla Box2D the line self:attachToTail(NewTailPiece()) would crash the program, because the developer forgot about the callback rules. But Box2D-handled buffering would turn this block into an easily readable and completely functioning piece of code, and I see no reason for disallowing this.

I apologize if this issue has already been debated into the ground, but I do hope it gets taken into consideration again since it seems like a low-hanging fruit for improving many developers' experiences. At the very least, allowing these data structures (bodies, joints, and fixtures) to exist on their own before officially linking them to their dependencies (such as fixtures existing without a body) would make it significantly easier for those of us writing wrappers in other languages to make a proper buffering system, as we could still fill in all the properties before activating them in the world later. Thanks.

Troid92 avatar Jul 09 '18 06:07 Troid92

Should you be able to delete the b2World inside a callback? There is always going to be something that breaks. This is the nature of callbacks.

I could spend extra cycles buffering everything, but then I'd be lowering performance unnecessarily for users that are more careful.

erincatto avatar Apr 15 '20 06:04 erincatto

If everything would be in c++11 you can simply create shared instance before calling user code. Therefore object wouldn't be destroyed unless you ended callback. That is done in tgui.

But modifying objects is another job. And There should be some way to do it. In example. Example that buffers data that it can and then use it to modify objects. (Users could simply copy that fragment to their own code. And have smaller performance, or write their own.) And have that future.

1aam2am1 avatar Apr 15 '20 09:04 1aam2am1

Deleting the b2World from its own calls: I see no reason to support this, but it'd be so rare we can dismiss performance as an issue. Maybe only if it ends up fulfilling some guarantee. :)

In the case of fixtures, bodies, and joints, I am not convinced there needs to be any performance hit at all. Outside of Step(), Box2D can perform the actions immediately. Within Step(), Box2D could temporarily replace relevant functions with ones that buffer the action. For creating an object we would allocate memory to be manipulated but not fully activate it yet. In other words, the create and destroy and setActive functions would be function pointers that change for the moment. https://stackoverflow.com/questions/2438539/does-function-pointer-make-the-program-slow

The correct solution would always be to buffer the action. The question is just who writes the code: Box2D once, or every user of Box2D every time. For those who have already implemented their own buffers, there would be no difference as they were never calling those functions in their callbacks in the first place.

As with everything, measuring performance before and after the change would be the only way to know for sure, but I think this would this solve a lot of headaches.

Troid92 avatar Apr 15 '20 18:04 Troid92