godot: Changing node parent produces Area2D/3D signal duplicates
Godot 2.1.4, Godot 3.0 beta 1 Windows 10 64 bits
I found that if you teleport-away and reparent a node which is inside an Area2D, you will get an area_exit
, but ALSO extras notifications body_enter
and body_exit
, two in my case, even though the object is not anymore in the area.
This gave me headaches for several hours in my game until I narrow it down to this, because this behavior causes my teleport system to trigger everything multiple times, and I can’t find a way to avoid this so far without atrocious hacks…
It does this wether I teleport from fixed_process
or not, and emptying layer/collision masks before teleport has no effect.
Here is a demo project reproducing it: 2.1.4: TeleportArea2D.zip 3.0b1: TeleportArea2D_Godot3b1.zip
Repro:
- Launch main scene, notice you get one
body_enter
because the body overlaps with the area, wich is expected - Press any key, this teleports the body and reparents it (in code, the node is removed from tree, translated, and then added to the tree)
- Notice you get an expected
body_exit
, but also two unexpected pairs ofbody_enter
andbody_exit
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 15
- Comments: 32 (12 by maintainers)
Commits related to this issue
- Refactor & fix bugs. Player: - Fix some player state machine bugs preventing player from transitioning to the correct state due to having multiple valid triggers, which in turn is caused b... — committed to forerunnergames/coa by knightofiam 3 years ago
- Refactor & fix bugs. Player: - Fix some player state machine bugs preventing player from transitioning to the correct state due to having multiple valid triggers, which in turn is caused b... — committed to forerunnergames/coa by knightofiam 3 years ago
- Refactor & fix bugs. (#29) Player: - Fix some player state machine bugs preventing player from transitioning to the correct state due to having multiple valid triggers, which in turn is ... — committed to forerunnergames/coa by knightofiam 3 years ago
- WIP Preparation: - Re-center camera on player when not moving. This can reveal what's on the other side of a ravine from a broken bridge, for example, instead of just empty space. - Cre... — committed to forerunnergames/coa by knightofiam 2 years ago
- WIP Preparation: - Re-center camera on player when not moving. This can reveal what's on the other side of a ravine from a broken bridge, for example, instead of just empty space. - Cre... — committed to forerunnergames/coa by knightofiam 2 years ago
- WIP Preparation: - Re-center camera on player when not moving. This can reveal what's on the other side of a ravine from a broken bridge, for example, instead of just empty space. - Cre... — committed to forerunnergames/coa by knightofiam 2 years ago
- WIP Preliminary features: - Re-center camera on player when not moving. This can reveal what's on the other side of a ravine from a broken bridge, for example, instead of just empty space.... — committed to forerunnergames/coa by knightofiam 2 years ago
- Add broken bridge. Preliminary features: - Re-center camera on player when not moving. This can reveal what's on the other side of a ravine from a broken bridge, for example, instead of ju... — committed to forerunnergames/coa by knightofiam 2 years ago
- Add broken bridge. (#59) Preliminary features: - Re-center camera on player when not moving. This can reveal what's on the other side of a ravine from a broken bridge, for example, instead... — committed to forerunnergames/coa by knightofiam 2 years ago
This is still a problem in 4. Reparenting a node while inside an area2d triggers an additional body_entered signal.
Still reproducible in 3.2: https://www.reddit.com/r/godot/comments/g9cosc/area2d_body_entered_firing_when_it_should_not/
The enter signal is emitted immediately after re-adding the red_level scene back to the tree. When I check “body.position” in the “entered” function, it clearly shows the body is far away, so the signal should not be emitted.
Yes I can still reproduce it in 4.3 dev1. I quickly assembled a project to test it. I included 2D and 3D examples.
I’ll leave a zip attached in this comment. If you want to test it, please run (using F6) the
world.tscn
scene for each example (I didn’t set a main scene sorry)bugarea-4.3-dev1-2D-and-3D.zip
Forgot to mention, I’ve been testing and apparently, after removing child by using the method
remove_child(child_to_remove)
if you putyield(get_tree(), "idle_frame")
afterwards, the whole issue gets fixed, even on my other project with multiple viewports. bugarea-fixed.zipJust tested this and it’s still reproducible in Godot 3.3.2
It reproduces in 3.1 alpha 1.
On scene start:
Which is expected. Then the reparent-teleport happens, and all these signals get fired, while I expected only one
Body exit
:TeleportArea2D.zip
I tested this against master branch.
I downloaded the
bugarea.zip
and converted it to Godot 4 (had to fix some collision layers because it didn’t collide with the door) then tested it and it seems to keep happening, signal is fired an indefinite amount of times making the game freeze. I haven’t tried the workarounds though.That’s for the 2D version, the 3D version (
bugarea3d.zip
) directly closes the game upon opening it without moving so I assume something in the automatic conversion to Godot 4 went wrong, but I don’t know how to debug that.Pretty sure I am battling this issue in 3.4.5.stable right now. Here is a minimal reproduction project (Move the capsule with left and right arrow keys): area2d_body_entered_triggered_multiple_times.zip
using call_deferred() for the add_child() seems to fix this for me, but when I exit the game I get errors that I leaked memory:
I can reproduce this in 3.3.4 in 2D.
I thought I found a workaround for it but so far the best way I’ve found to prevent it is to just not use the event system and just use _process(delta) with get_overlapping_bodies, but that’s not ideal.
I’m going to try and think of something else, I feel like this is a pretty bad bug because IMO the docs lead you right into it https://docs.godotengine.org/en/stable/tutorials/best_practices/scene_organization.html#choosing-a-node-tree-structure
I wonder if some other workaround the docs kind of recommend against because of “bad coding practices” might also work (e.g., adding the player to the scene every time).
I’ll hopefully have more ideas for this later.
Just wanted to point out that this happens too in 3d. The workaround proposed by @AttackButton works in this case too
bugarea3d.zip bugarea3d-workaround.zip
Damn you are absolutely right, I didn’t notice this…
After a lot of testing, it seems that what you mention is the only reliable way of guaranteeing one signal received instead of multiple
I also tested putting some timers between lines of code and it seems that the problem also got fixed, but i find my solution very arbitrary and not very clean, but may help in finding out the issue. For example:
^ This makes the signal emit only once, but if you change the timer for something shorter, let’s say 0.01 like this…
then the signal emits twice.
For me it seems that the “add” and “remove” child are not in sync with the physics process or something like that (i know nothing about Godot’s internals so I might be completely wrong)