Issues with area3d exit signal

Godot Version

4.4 beta 2

Question

This might be a weird one.

I have a spaceship object with an area3d. When the player enters the area i am reparenting the player to the ship node. When the player exits the area i want to unparent them.

The on_body_entered signal works fine but then _on_body_exited signal never fires when the player walks out of the area.

heres my code below for entering/exiting. If anyone knows where im going wrong or perhaps a better way to do what im trying to do i would really appreciate it. Im parenting the player so the ships movements/ rotations can easily be applied to the player to simulate an artificla gravity system.

heres what ive tried

  • different node types for the ship class
  • checked all variables in remote scene (masks, layers, variables etc)
  • printing(get_overlapping_bodies()) on area every frame shows the player always overlapping after entering despite walking out of area.
  • tried godot 4.3
  • im disconnecting and reconnecting the signals as the reparenting appears to retrigger the entered and exited signals infinitely.
extends Area3D

var ship


func _ready() -> void:
	ship = get_parent()
	
	
func disconnect_signals() -> void:
	#set_deferred("monitoring", false)
	disconnect("body_entered", _on_body_entered)
	disconnect("body_exited", _on_body_exited)
	

func reconnect_signals() -> void:
	#set_deferred("monitoring", true)
	connect("body_entered", _on_body_entered)
	connect("body_exited", _on_body_exited)


func _on_body_entered(body: Node3D) -> void:
	if body not in ship.occupants:
		print('player entered ship')
		var player = body
		var player_trans = player.global_transform
		disconnect_signals()
		player.get_parent().remove_child(player)
		ship.add_child(player)
		player.global_transform = player_trans
		ship.occupants.append(player)
		player.aboard_ship = true
		reconnect_signals()


func _on_body_exited(body: Node3D) -> void:
	print('player exited ship')
	if body in ship.occupants:
		var player = body
		var player_trans = player.global_transform
		disconnect_signals()
		player.get_parent().remove_child(player)
		get_tree().current_scene.add_child(player)
		player.global_transform = player_trans
		ship.occupants.erase(player)
		player.aboard_ship = false
		reconnect_signals()

Nodes have a reparent method, maybe you’d find that useful. Probably pass true for the keep_global_transform parameter. Your enter/exit signals get retriggered, because the player node leaves the tree for a moment and then reenters it. Maybe the reparenting method would solve this and you wouldn’t have to do these shenanigans with disconnecting and reconnecting the signals :smiley:

Hope it helps a little bit. Other than that I don’t really see any mistakes in the code you posted.

2 Likes

I have seen issues in the past where modifying a transform into and out of an area can lead to strange behavior. I too was thinking that the OP should try adding the child directly into the area without the transform, but this last clue:

Makes me think it is working fine because the body enter happens after the parent change and transform. Just not the body exit after the signal reconnect. I cant rule out some physics engine shenanigans but there could be something we are missing that is external to this code.

1 Like

Thank you for your response! I was not aware of the reparent method. I’ve refactored my code to suit and am getting similar but different resuilts

func _on_body_entered(body: Node3D) -> void:
	if body not in ship.occupants:
		print('player entered ship')
		var player = body
		player.reparent(ship, true)
		ship.occupants.append(player)
		player.aboard_ship = true


func _on_body_exited(body: Node3D) -> void:
	print('player exited ship')
	if body in ship.occupants:
		var player = body
		player.reparent(get_tree().current_scene, true)
		ship.occupants.erase(player)

when walking into the area this is printed

player entered ship
player exited ship
player entered ship

but still, when exiting the area the exit signal does not trigger.

Thank you for your response. It seems to me something undocumented is happening here but I am by no means an expert.

What is strange is that my the parent Ship class which the area in question is parented to has other area3d nodes which i use for oxygen areas. The character bodys entry and exit into these areas works flawlessly and they are setup identically to the area that handles the player reparenting. so this really has me scratching my head.

1 Like

I’ve made a test project to showcase the issue.

If using Jolt physics the behaviors i initially described will occur. if switching to godot physics the entered and exited signals will be triggered infinitely while inside the area even if the signals are disconnected.