I have fairly complex collision responses for multiple CharacterBody2Ds that must happen in a single frame. A solution I found was calling move_and_collide for all CharacterBody2D instances from inside the _physics_process method of a single object like the following:
func _physics_process(delta: float) -> void:
for i in range(max_iterations):
# ...
var collision1 := char1.move_and_collide(char1.motion)
# ...
var collision2 := char2.move_and_collide(char2.motion)
# etc.
# handle collision responses, which may involve additional calls
# to `charX.move_and_collide`
That is rather a very simplistic version of the actual code. However, my question is the following:
Is it OK to call move_and_collide from inside the _physics_process method of another object?
If so, Iâve likely found a bug involving move_and_collide. If it isnât, I could not find that limitation in the documentation. Additionally, I donât know how I would implement a similar single-frame solver without re-writing most of what Godot provides for object motion and collision response.
tbh, i donât really know how the physics work, but i read this in the docs
Physics processing works with a similar virtual function: _physics_process(). Use it for calculations that must happen before each physics step, like moving a character that collides with the game world.
Maybe that means that it doesnât matter where you call move_and_collide because they are all called before the physics step?
That was what I though, but I found unexpected behavior by calling move_and_collide() from another objectâs physics_process().
Both DeepSeek and ChatGPT state that move_and_collide() is not supposed to be called from another object due to Godotâs physics engine design, despite not being explicitly stated in the documentation. Of course, it might just be AI rubbish. I hope someone with deep knowledge of the physic engine can answer it.
As far as I know, there shouldnât be a problem with this. What is the âunexpected behaviorâ you are experiencing?
As an aside, even if you call move_and_collide in the associated class they are all executed in the same physics frame. The physics tick is only done, when all _physics_process are done - you donât have to call them in one function.
Basically, the position of the CollisionShape2Ds of the CharacterBody2Ds seem not to be updating correctly. Even though I can visualize them in the new position if I put a breakpoint at the end of the for loop of my example, they behave like they are still in their old position. Maybe, there is some sort of cache issue.
Iâll create a minimal reproduction project and a bug report on Godotâs repo.
Somewhat related - if all the processing is done after all the physics process calls, does that mean that is_on_floor is always one frame behind, even if you call it after move_and_slide on the same frame?
Indeed! Before, I have misunderstood what you meant.
Since we can call move_and_collide() in a loop to implement collision response (like is done in the move_and_slide() implementation) and it usually works as expected, I was assuming that the new positions of the other CharacterBody2Ds that result from move_and_collide() calls would be available when calling move_and_collide() again in the next iteration. Even the debug view of the collision shapes show them in the up-to-date position, but for the next move_and_collide() calls in the same frame, all the CharacterBody2Dâs collision shapes are still in the old position.
When I found that behavior, I thought that it was due to the fact I was supposedly calling move_and_collide() from unsupported places. However, I implemented additional test scenarios and confirmed that the behavior is like that no matter whether I call move_and_collide() from another objectâs _physics_process() or from selfâs.
Now, I have a problem: I still have to resolve all collisions through multiple iterations in a single frame. How can I do it? Is there some sort of âflushâ or âforce_updateâ function?
I donât believe it is. As far as I know, on_floor is update immediately with a call to move_and_slide. Itâs only the transform updates themselves that can be delayed. But TBH I donât know how/when exactly what physics calculation is happening. My knowledge is mostly based on the documentation
Unfortunately, force_update_transform() doesnât do the trick. Even if I call it just before the next calls to move_and_collide(), the latter will still think that the previously moved CharacterBody2Ds are in the old position.