Open Emynator opened 8 months ago
See also:
For anyone stumbling upon this issue in the future: A workaround I found is adding a flag and timer to the Area2D. as long as the flag is false the collision handling functions don't do any processing. On timeout of the timer the flag is set to true. A second timer on the CharacterBody2D which ideally is longer then the first one enables the CollisionShape2D. This way only the correct Area2D's OnBodyEntered function is triggered.
Despite a workaround existing I would still consider the current behaviour a bug.
I have encountered this before, and here are a few things I know or believe about it:
The whole issue is due to the CharacterBody2D
's default GlobalPosition
being x: 0, y: 0
when add_child()
is called. You would expect setting its GlobalPosition
on the very next line would override this, but the PhysicsServer2D
has already taken that initial position into account.
Due to this, setting the CharacterBody2D
's GlobalPosition
right before calling add_child()
actually causes the collisions to behave as intended without using any workaround.
If you need to set the GlobalPosition
after the add_child()
instruction for whatever reason, awaiting exactly 2 physics frames before enabling the CollisionShape2D
will also result in the proper collision signals. This seems to give the PhysicsServer2D
enough time to start calculating the collisions from the proper GlobalPosition
. In fact, a very similar workaround was shared in this comment, all the way back in Godot 2.1.4 over 6 years ago.
I ran into the same issue using get_tree().paused = true
before modifying a CharacterBody2D
's Position
, resulting in its previous position being used for collision calculations after unpausing.
It's bugged in 4.2.2 rc1 as well. Setting global position before adding didn't help, but waiting for 2 process frames did. Also, I don't think the default position is (0, 0), it's the position the node had before it was removed, at least it's 100% this way in my case. I also checked what the area thinks the body's position is in _body_entered signal and it actually prints the correct position, but the signal still fires.
It's bugged in 4.2.2 rc1 as well. Setting global position before adding didn't help, but waiting for 2 process frames did. Also, I don't think the default position is (0, 0), it's the position the node had before it was removed, at least it's 100% this way in my case. I also checked what the area thinks the body's position is in _body_entered signal and it actually prints the correct position, but the signal still fires.
Yes you're right about the position, it was only (0, 0) specifically in this case because it was instantiating a fresh Node. It is indeed taking the Node's previous position into account.
Tested versions
Reproduceable in:
System information
Godot v4.2.1.stable.mono - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1080 (NVIDIA; 31.0.15.5123) - AMD Ryzen 9 5900X 12-Core Processor (24 Threads)
Issue description
A scene is setup with 4 Area2Ds with a size of 512x512 placed around the origin. In the main scene this scene is instantiated and a CharacterBody2D with it's CollisionShape2D initially disabled is also instantiated. The CharacterBody2D is set to position (256, 256) and then it's CollisionShape2D is enabled with a SetDeferred() call.
Expected behaviour: only one Area2D's OnBodyEntered() function is triggered. Actual behaviour: all 4 Area2D's OnBodyEntered() function is triggered, then the other 3 Area2D's OnBodyExited() function is triggered.
Steps to reproduce
Using the MRP:
Minimal reproduction project (MRP)
CollisionBug.zip
alternative the repo is also available on github