Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Area2D wrongly fires body_entered signal when a body is inside it but collision is disabled, and then setting the body to be outside the Area2D and re-enabling collision again #69407

Closed
CentiGames opened this issue Nov 30, 2022 · 5 comments

Comments

@CentiGames
Copy link

Godot version

Godot Engine v4.0.beta6.official

System information

Windows 10, GeForce RTX 3070 (Driver version 526.86)

Issue description

If a body enters an Area2D the body_entered signal fires correctly. I then disable the collision for the Area2D (setting the collision_mask to 0). If at a later point I set the position of the CharacterBody2D to be outside of the Area2D and in the same function (i.e. same physics frame) re-enable collisions again (setting the collision_mask to 1 again), the body_entered signal fires again, although I set the position to be outside the Area2D beforehand.

To showcase the issue, I made a small game where the color of the player changes when the body_entered signal is fired. When I press the space bar, the position of the player is set to be outside of the Area2D and the collision_mask is set to 1 again, however, the color changes when pressing the space bar, as the body_entered is fired again:

showcase_issue_collision.mp4

Related Issues

I am aware that this is probably difficult to fix when changing position and collision mask in the same function/physics frame, but it took me a long time to figure it out, so maybe other people will at least find this issue helpful.

I found similar/related issues for previous Godot versions, but I did not find any for Godot 4.0, so that's why I opened this issue, because I initially thought this might a problem of Godot 4.0 and did not found any 4.0 related issues. I hope that's okay. Related issues are probably #45131 and #14578 , (maybe also #25769 , #19271 , and #14578 ).

Workaround

A proposed workaround from the related issues was to call await(get_tree().physics_frame) before re-enabling collisions, which I can confirm works.

Steps to reproduce

  • Have an Area2D and a CharacterBody2D
  • Connect the body_entered signal to a function
  • The function connected to the signal should call a print statement (or something else you can observe) and the same function should set the collision_mask of the Area2D to 0
  • Move the CharacterBody2D inside the Area2D, the body_entered signal fires
  • While the CharacterBody2D is still inside the Area2D, call a function that moves the position of the CharacterBody2D to be outside the Area2D and in the same function re-enable the collision_mask by setting it to 1 again (or whatever layer the CharacterBody2D is on)
  • The body_entered signal will be fired again, although the position was set to be outside of the Area2D before re-enabling the collision_mask

Minimal reproduction project

Here is the minimal reproduction project:

CollisionReenabledWrongSignal.zip

Use arrow keys to move the player inside the Area2D, then press the space bar while still being inside the Area.

@markdibarry
Copy link
Contributor

markdibarry commented Nov 30, 2022

Possible duplicate or related to #61420. Please confirm.

@CentiGames
Copy link
Author

Hi, sorry for the long wait. I looked at the other issue #61420, but I am not sure if they are related since I have too little knowledge of Godot's internal code, sorry. However, I am pretty sure it's not a duplicate since the other issue has the problem that no body_entered signals are fired (or the signals are disconnected?) after pausing and unpausing the game if the Area2D is below the Body in the hierarchy. In this issue here, however, the signal is fired even though it actually should not have been fired. (Or maybe it should? I am unsure what the expected behavior should be, but the current behavior is definitely confusing and should at least be documented somewhere in my opinion)

Also, I can confirm that this issue here still occurs in Godot 4.0 Beta 12.

@hsandt
Copy link
Contributor

hsandt commented Apr 18, 2023

While writing another bug report I also discovered this bug. So I'm sharing this alternative repro project:

Issue 69407 - v4.0.2 - Alternative MWE to move PhysicsBody2D outside Area2D while disabled then reenable to cause 1-frame re-register.zip

Repro steps:

  1. Open Test2D scene
  2. Play scene
  3. Press B, M, B in order to disable, move and re-enable the character body (represented by Godot icon). It becomes red when disabled.
  4. Observe the registered body count (both via get_overlapping_bodies and manual registration on enter/exit) become 0 after the first B, and blink from 0 to 1 on the second B, instead of staying at 0.

I tried other combinations like moving the area instead of the body: B, N, B or disabling the area: A, N, A or A, M, A but none of these triggered the bug. The area must stay still and not disabled.

@hsandt
Copy link
Contributor

hsandt commented Apr 27, 2023

For now, I used the following workaround in a real project, and it works:

Do not check get_overlapping_bodies() (or manual registrated bodies via enter/exit) on the first frame after re-enabling the node. You can use any strategy to wait 1 frame, such as using call_deferred.

@Rindbee
Copy link
Contributor

Rindbee commented May 23, 2023

Seems to be a duplicate of #45131 in 4.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants