-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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 body_entered signal is generated incorrectly if a body is moved prior to scene initialization #61584
Comments
Also, just FYI, I can also reproduce this in the latest commit as of right now, cd78718 |
More details If I do b servers/physics_2d/godot_area_2d.h:165 I notice that this breakpoint fires when hitting 3 to move from position B to position A, then reload the scene (gdb) bt
#0 GodotArea2D::add_body_to_query (this=0x5555625fb0d0, p_body=0x55556252b4c0, p_body_shape=0, p_area_shape=0) at servers/physics_2d/godot_area_2d.h:165
#1 0x000055555b57bdbd in GodotAreaPair2D::pre_solve (this=0x5555624c3ce0, p_step=0.0166666675) at servers/physics_2d/godot_area_pair_2d.cpp:73
#2 0x000055555b153838 in GodotStep2D::_pre_solve_island (this=0x555562235960, p_constraint_island=...) at servers/physics_2d/godot_step_2d.cpp:84
#3 0x000055555b1540d5 in GodotStep2D::step (this=0x555562235960, p_space=0x5555622da110, p_delta=0.0166666675) at servers/physics_2d/godot_step_2d.cpp:254
#4 0x000055555b133761 in GodotPhysicsServer2D::step (this=0x5555622ba070, p_step=0.0166666675) at servers/physics_2d/godot_physics_server_2d.cpp:1254
#5 0x000055555b4f6bc5 in PhysicsServer2DWrapMT::step (this=0x5555622ba3b0, p_step=0.0166666675) at servers/physics_server_2d_wrap_mt.cpp:74
#6 0x0000555557937c0b in Main::iteration () at main/main.cpp:2718
#7 0x00005555578ed6f8 in OS_LinuxBSD::run (this=0x7fffffffd990) at platform/linuxbsd/os_linuxbsd.cpp:441
#8 0x00005555578e8935 in main (argc=9, argv=0x7fffffffde88) at platform/linuxbsd/godot_linuxbsd.cpp:68 However it does not fire when I hit 2 to move from position B to position A (without reloading the scene). I'm not sure exactly what _pre_solve_island is doing yet, or what an island (looks like a list of constraints) or constraint is. A bit of code feedback: it would be nice to have more comments explaining what these classes/structs/etc. represent. Even a one liner like "A constraint is an object that can be solved for collisions" or "an island is a group of constraints, grouped by ..." would help. |
Another quick update, Unfortunately setting the collision layer and mask to 0 before doing the scene change, and restoring it afterwards is not a workaround. No change in behavior. It also does not make a difference whether you create the new scene before or after moving the player. In main.gd: func initialize_scene(scene_path, move_player):
# Setting collision layer and mask to 0 (collide with nothing) at the start of the function,
# then resetting it to 1 later, does not help
var scene_type = load(scene_path)
if scene_type == null:
print('Unable to find scene ', scene_path)
return
var old_collision_layer = Player.collision_layer
var old_collision_mask = Player.collision_mask
Player.collision_layer = 0
Player.collision_mask = 0
# NOTE: It does not matter if you create an instance of the new scene before or after moving the player
print('Instanced scene')
var scene = scene_type.instance()
if move_player:
move_player()
Player.collision_layer = old_collision_layer
Player.collision_mask = old_collision_mask
print('Changing scene to ', scene_path)
do_scene_change(scene) |
One more update, I found what seems to be a workaround. Hierarchy changes:
New hierarchy is
On every scene change:
extends Node2D
onready var PlayerType = preload("res://Player_Scene.tscn")
onready var Player = PlayerType.instance()
var PositionToggle = false
var CurrentScene = null
func initialize_scene(scene_path, move_player):
var scene_type = load(scene_path)
if scene_type == null:
print('Unable to find scene ', scene_path)
return
print('Instanced scene')
var scene = scene_type.instance()
if move_player:
PositionToggle = not PositionToggle
if Player != null:
Player.queue_free()
Player = null
Player = PlayerType.instance()
move_player()
scene.add_child(Player)
print('Changing scene to ', scene_path)
if CurrentScene != null:
self.remove_child(CurrentScene)
CurrentScene.queue_free()
CurrentScene = null
CurrentScene = scene
self.add_child(CurrentScene)
func _ready():
initialize_scene('res://Room2.tscn', false)
func move_player():
if PositionToggle:
Player.position = Vector2(-100.0, -100.0)
else:
Player.position = Vector2(100.0, 100.0)
print('Player position set to ', Player.position)
func _process(delta):
if Input.is_action_just_pressed('room_2'):
initialize_scene('res://Room2.tscn', false)
elif Input.is_action_just_pressed('room_2_and_move'):
initialize_scene('res://Room2.tscn', true)
elif Input.is_action_just_pressed('position_toggle'):
PositionToggle = not PositionToggle
move_player() |
As of 880a017 (the latest from master as of right now) it looks like it's behaving better. When I hit 3 from position B I am not getting that body_entered event anymore, which is great! |
I'm experiencing this same issue with Godot 3.5.1. I think it's due to #18748 which says this is intended behavior due to physics optimizations. Your workaround(s) didn't do the trick for me. After finding the issue I linked above, I figured out a workaround where I disable the player's
Not great, but it does ensure that a full physics frame passes before the collider is re-enabled. This should work until I have to disable the player's |
I'm experiencing a similar bug in 4.1. I have a body and area that never really overlap, but the area receives entered signal. It happens randomly. |
I confirm that the bug is reproducible in 4.3 dev 1. Trying to workaround this by temporarily changing I'm guessing the reason is that the body doesn't update the coordinates in the physics server when needed (you can ensure that the node coordinates are updated at the right time). |
Godot version
3.4.4 and 3.3.4
System information
Ubuntu 20.04 x86_64
Issue description
What doesn't work:
If a body is moved before a scene is loaded, body_entered will be generated even though the body is nowhere near the Area2D.
How I expected it to work:
The scene would check where the body is when it loaded, and only emit the signal if the body was actually intersecting the area.
What my tree looks like
Controls:
Note: When I refer to 1,2,3, I mean the number keys 1, 2, and 3 on your keyboard. Not Keypad 1,2,3.
Context / Why this is important:
References
This is possibly related to #14578 but that issue talks about changing node parents, there is no node reparenting happening here.
Workarounds
This is not a great workaround but dropping the signal system for body_entered and using
_process(delta)
withArea2D.get_overlapping_bodies()
does work as expected, and does not find "ghost bodies".At the moment I'm early enough in my project that I can change to some other system of going between rooms.
What it isn't
Extra rambling
My guess as to why this is happening
My (wild) guess is that maybe there's some kind of signal caching / queue-ing going on?
Steps to reproduce
Note: When I refer to 1,2,3, I mean the number keys 1, 2, and 3 on your keyboard. Look at the output to see when a body_entered event fires.
Step 7 produces this output:
Carefully note the order of events:
Minimal reproduction project
NodeParentArea2DFalseTrigger.zip
The text was updated successfully, but these errors were encountered: