I have an Area3D that is a child of a Rigidbody3D, and a player (CharacterBody3d) that are under the same “world” Node3D.
Intended Behavior:
When the player enters the Area3D, the player gets reparented to the Area3D’s parent (which is the Rigidbody3D).
Upon exited the area, the player is reparented BACK to the world node.
Actual Behavior:
When you enter the Area3D, the entered signal triggers, reparenting the player onto the Rigidbody3D and then immediately triggers the exited signal, reparenting the player back to the world again.
This cycle repeats infinitely until the player leaves the Area3D.
Attempted Solutions:
Using a boolean to check if the player is already inside of the Area3D before reparenting.
Disabling and re-enabling the Area3D’s collision when entering and exiting.
Disconnecting and reconnecting the Area3D’s signals then reconnecting them.
Would appreciate some help! Thanks in advance.
Here is is the code snippet with the two signals as well as a screenshot of the basic node structure:
func _ready():
#connect signals
player_detection_area.body_entered.connect(_on_area_3d_body_entered)
player_detection_area.body_exited.connect(_on_area_3d_body_exited)
#~-rest of the code-~#
func _on_area_3d_body_entered(body):
if body is Player:
body.ship_entered(self)
piloting_player = body
body.reparent(self, true) #reparents player onto self (RigidBody3D)
func _on_area_3d_body_exited(body):
if body is Player:
body.ship_exited()
ship_is_piloted = false
piloting_player = null
body.reparent(get_node("/root/DevLevel/"), true) #reparents player back onto world node
func _on_area_3d_body_entered(body):
if body is Player:
body.ship_entered(self)
piloting_player = body
reparent = true
body.reparent(self, true) #reparents player onto self (RigidBody3D)
func _on_area_3d_body_exited(body):
if reparent:
reparent = false
return
if body is Player:
body.ship_exited()
ship_is_piloted = false
piloting_player = null
body.reparent(get_node("/root/DevLevel/"), true) #reparents player back onto world node
This was just a CharacterBody walking onto an Area, so I don’t know if what else you have will make that different, but you readd the body as player if statements at the top, and hopefully it will work. If not…
Weird, at some point if I find time, I’ll try and recreate more exactly what your node structure is, and see if that works. Since there is something in your node structure that is causing the issue, since I was able to get it to work with minimalist code.
I think this fails because overlaps_body will check the internal array of detected bodies to confirm if is overlapping or not, so if body_exited signal was emmited, probably the body will be already out of this array.
You can try do a manual check using the body position to confirm if the body really exited:
var _ignore_next_call := false
func _on_area_2d_body_entered(body: Node2D) -> void:
if _ignore_next_call:
_ignore_next_call = false
return
body.call_deferred("reparent", self)
func _on_area_2d_body_exited(body: Node2D) -> void:
var rect := Rect2()
# Im assuming you're using a rectangle/squared shape
var col_size = $CollisionShape2D.shape.size
rect.position = $CollisionShape2D.global_position - (col_size/2)
rect.size = col_size
if rect.has_point(body.global_position):
_ignore_next_call = true
return
body.call_deferred("reparent", get_node("/root/Node/"))
Sorry, that is my mind too 2D focused in action hahaha. For 3D would be used AABB instead, but while testing this problem i found a more easier solution:
func _on_area_3d_body_entered(body):
if body is Player and body.get_parent() != self:
body.call_deferred("reparent", self, true)
func _on_area_3d_body_exited(body):
if body is Player and body.get_parent() != get_node("/root/Node/")
body.call_deferred("reparent", get_node("/root/Node/"))