Why are Area2D collisions triggered 1 frame late?

Godot Version

4.2

Question

I created a project following a tutorial. It’s a 2D platformer where I have a Player (CharacterBody2D) and then some coins (Area2D). The player is being moved with move_and_slide() within _physics_process(delta) function.
On the coin scene, I’ve set up the _on_body_entered(body) signal like this:

func _on_body_entered(body):
	if body.is_in_group("Player"):
		body.add_score(1)
		queue_free()

It works as expected, BUT, for some reason I can actually see the player overlapping the coin for about 1 frame. The collision shapes are fine.

To my understanding, the coin should dissapear as soon as the player collider is overlapping the coin collider, so there shouldn’t be any frames where I can actually see both sprites overlapping. Why does this happens? Why does the coin dissapear slightly after I can see the player overlapping it?

I even tried this on the coin script to no avail, same thing happens:

func _on_body_entered(body):
	if body.is_in_group("Player"):
		body.add_score(1)
		visible = false
		queue_free()

Thank you

queue_free()

Queues a node for deletion at the end of the current frame.

visible = false does this work at all without queue_free?


hard to say without having the project. i was going to suggest hide() but it’s identical

void hide ( )

Hide the CanvasItem if it’s currently visible. This is equivalent to setting visible to false.

Yes, it works fine without queue_free.

I think this issue has something to do with “physics/common/physics_ticks_per_second” in project settings. It’s set by default to 60, while my monitor refresh rate is 120hz. If I set it to 120, I can’t see the sprites overlapping anymore. On the other hand, if I lower it to like 20, the issue becomes more obvious.

I’d like to know if there’s a proper way to make this work so the sprites won’t be seen overlapping each other… Raising the physics tick rate is not ideal because it decreases performance and there’s no guarantee that it will work for all screens (what if the user has a 240hz monitor?)

Jesus, I once wrote my own shitty HTML5 javascript engine for a small game and this was not an issue.

I made a visual representation of what I want to happen and what is happening:
0899f725eefd2bdb623863242a607428f34f7f75

Currently, with the setup specified in my first post, I can see the player and the coin overlapping for the frame when the player first touches the coin.

However, I need the coin to dissapear right when the player is already over it, in the same frame, not the frame after. This should be how it works, because the coin shouldn’t be drawn by the engine if the code to make it invisible has already been executed. But there’s clearly something I’m missing or the engine is doing deceptively behind the scenes.

My understanding of game engines makes me think this happens because the engine execution order, currently must be something like this: signals → process → physics_process → draw
Therefore, when the player (physics_process) touches the coin, the body_entered signal from the coin won’t be processed until the next frame. However, this is just an assumption, I don’t really know what Godot is doing behind the scenes.

I really need to know if there’s a way to make it work like in the desired example without having to write my own collision detection solution or tampering too much with the engine. The only partial solution I found is increasing the physics tick rate in the project settings, but this is not a reliable solution. If there’s no other way, I’ll sadly have to discard Godot for my project.

Thank you all

Does this not mean that the physics engine checks for collision every second frame in your case? Would it not make sense that a 1 frame overlap can happen?

But then why the body_entered signal from the coin is emmited a frame late? This makes no sense. If my player is a CharacterBody2D being moved in _physics_process, the signal should be executed the same physics tick as it touches the coin’s Area2D, no matter the physics tick rate. If the player position is only updated in physics ticks, there should be no overlapping frames

Yes, Godot does have a discrepancy between rendering and physics frames, and that is for technical reasons that come up when making more complex games (e.g. networked physics). If you are curious about it, I recommend this article.

Now, what you can do is force Godot to match those framerates via project settings. Maybe setting application/run/max_fps to 60 helps, because then rendering is slowed down to match the physics. There are other settings that might help, check the docs.

1 Like

In 3D there is often a simpler (dummy) geometry used for collisons (box, elispoid, simplified mesh)… i don’t know much about the 2D collision features… maybe just use a bigger “area” than the actual coin ??

I tried this but doesn’t help. I even tried setting both max_fps and physics_ticks_per_second to 20. Even if the coin body_entered does “visible = false”, the player can be seen overlapping the coin.

I have a basic understanding of how a game engine loop works, I once wrote my own javascript + html canvas engine to make a simple Space Invaders clone, and made sure that each tick drawing happened after all the updates have been processed, and entities destroyed that tick would be excluded from the drawing, so stuff like this didn’t happen: if a collision was detected, the sprites wouldn’t ever overlap. However I didn’t have a separate update loop for physics, there was just a single update and drawing thread. I really want to replicate that behavior on Godot but can’t see how :frowning:

That’s a bad solution… then the player would be able to get the coin without actually touching it, just by passing near it

The size… of course would depend on the speed of the actors and also space in th envrionment…

…but also one frame… ( as mention above 1/60 sec) … you really thing this is to late ??

I mean… just have a look at Wikipedia:

I’m not saying if it’s okay or not to be 1 frame late, it’s really subjective, depends on what kind of game you want to make. What I’m asking here is, why? Why does that happens? And is there any way to make it 0 frames late?

Here’s the project if anybody wants to see it: WeTransfer - Send Large Files & Share Photos Online - Up to 2GB Free
You might not see anything “wrong” if your monitor is 60hz. But go into project settings and set both max_fps and physics_ticks_per_second to a low value and it will become more obvious.

Hidden by Moderator

Original Reply

Trust me.
… when spending time asking why things do work as they work ( while this may not be mentioned in any documentation… ) then you never will finish any project…
at all…
ever…

:wink:

I’m sorry but this is a really shitty answer to a serious topic. I’m not trying to figure out why things work how they work, I’m trying to figure out how to make it work the way it should work (see the visual representation I posted on #4). You are trying to discredit my concern because you believe it doesn’t have any relevance for you, but it does for my future project. I’m trying to figure out the best engine for my project and I’d really like to use Godot.

Unity engine has a diagram in their docs showing the execution order, so stuff like this can be easier to understand and fix: Unity - Manual: Order of execution for event functions

However, in Godot, I have no clue why the Area2D body_entered signal is being called a frame late, and how to fix it.

If anybody has a proper explanation on why does this happens or how to fix it, it’s welcome. If you are just going to tell me that I shouldn’t worry about that, then just shut up
Thanks

2 Likes

Hey flechi,

I personally think Area2D signals are not the best way to get what you want to achieve here.

To get exactly the behaviour you want, you usually would use the move_and_collide() and than handle the collision youself (ref:).

Kind regards,
KowalewskajA

3 Likes

Again, if the render framerate is twice as high as the physics framerate, then in the render frame without the physics check the overlap might happen. That would fit your observation that the overlap is exactly one pixel given that ratio of 120fps to 60 render-fps.

Or am I missing something here?

Thanks, I just tested this, it seems the right approach, although it makes signals useless. For this to work, the coins should be StaticBody2D instead of Area2D. I’m not sure if this can degrade performance on the long run, but seems like a good trade-off. Although I have to make sure only the player collides with the coins, and other objects can pass through them. Seems a bit overkill if you think about it

The player is suppossed to move only during physics frames, since im calling move_and_slide() in _physics_process(). There should not be visible overlap. However this doesn’t seem to be the cause of the problem, as setting max_fps to 60 or even 30 doesn’t fix the problem, there’s still a visible overlap.

I think it has to do with the way the engine processes signals… there’s something odd going on behind the scenes which makes the Area2D body_entered signal to be executed not on the frame that caused the signal but slightly late

@flechi Could you create a minimal reproduction project for this? As soon as I find time I’ll try to debug the C++ sources.

Thanks for clarifying! :pray:

Here I created a minimal reproduction project: WeTransfer - Send Large Files & Share Photos Online - Up to 2GB Free
It’s just a CharacterBody2D which you can move to the right by pressing the right arrow key. Once it reachs the Area2D on the right, a body_entered signal is set to reset the player to its starting position. However, I can clearly see the CharacterBody2D penetrating the Area2D for a frame, like shown here:

Again, I’m not saying if this is right or wrong (although for me it’s not ideal), I just need to know if there’s a way to make it so the CharacterBody2D never actually overlaps the Area2D for the same frame. I need to make it so the player is teleported back right when it touches the Area2D, not a frame late.

This might not a practical example, but think about it in a bullet hell game context, it would be weird to see bullets penetrating walls and such

1 Like