Area 3D on body entered/exit signal out of order

Godot Version

Godot Engine v4.2.1.stable.official.b09f793f5

Question

Hi, I’m having some issue with Area 3D detections. I have 2 area 3d in godot, each with on body enetered and on body exit signal. The object is a rigid body 3d moved by dragging with mouse, which tweens it’s position to the raycast collision location. The object have kinematic freeze mode. When the object enters or exit one of the areas, there is a slight lag for signal emission. When dragged from one area 3d to another, sometimes the entered signal for the new area would be emitted before exit signal from the previous area.

(like shown below)
Signal

func _on_body_entered(body):
    if body is Die:
        print(body.name, " entered: ", self.name)
func _on_body_exited(body):
    if body is Die:
        print(body.name, "left: ", self.name)

Sounds intentional, if the body is overlapping two areas it should trigger enter on both of them. I don’t see how the physics engine could differentiate your scenario from overlapping two areas. Maybe you could connect the entered signal as deferred?

Thanks for replying, I made a work around by adding Area 3d to the rigid body to detect the Area 3D instead of the other way around, no lag.

But I also made a minimal reproducible example for anyone interested in identifying the issue. You can download it here.

Here is a video demonstration.


Scene set ups.
Main Scene has 4 detection areas, 4 objects to be dragged around, floor, camera, light, and a static body 3D to keep the object on the same height as detection areas when dragging.

Detection Scene is a Area 3D with mesh as visual indicator and a 5x1x7 collision shape 3D. Collision mask set to 3.

3D Obejct is a Rigid Body 3D with 2x2x2 collision shape and mesh. Collision layer set to 1 (to collide with ground), 2(collide with ray), 3(collide with detection area)

Scripts
3D object
Stores a 3d coordinate that the object will return to
Check to see if the object is inside of a detection area

extends RigidBody3D
class_name Collider

var detected: bool = false
var memorized_position: Vector3 = Vector3.ZERO

func _ready():
	memorized_position = global_transform.origin

Detection Area
Check if the body entered is desired object
Set the condition accordingly and print to check for the correct interaction

extends Area3D

func _on_body_entered(body):
	if body is Collider:
		body.detected = true
		print(body.name, " entered: ", self.name)

func _on_body_exited(body):
	if body is Collider:
		body.detected = false
		print(body.name, " Left: ", self.name)

Script for main scene
Camera reference and variables

@onready var camera_3d = $Camera3D
var selected_object : Node3D = null
var is_dragging : bool = false
var offset : Vector3 = Vector3.ZERO

Input function

func _input(event):
    # Clicking on 3D object
    if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
        if event.pressed:
            # Selecting the object
            var mouse_pos = event.position
            var ray_origin = camera_3d.project_ray_origin(mouse_pos)
            var ray_direction = camera_3d.project_ray_normal(mouse_pos)

            # Create the PhysicsRayQueryParameters3D
            var ray = PhysicsRayQueryParameters3D.new()
            ray.from = ray_origin
            ray.to = ray_origin + ray_direction * 1000
            ray.collide_with_bodies = true
            ray.collide_with_areas = false
            ray.collision_mask = 2
            var space_state = get_world_3d().direct_space_state
            # Perform the raycast
            var result = space_state.intersect_ray(ray)
                if result:
                    var collider = result.collider
                    if collider:
                        selected_object = collider
					
                        # Adjust the offset to account for the die's floating position
                        var ground_position = result.position
                        offset = selected_object.global_transform.origin - ground_position
                        is_dragging = true
        else:
            #Releasing the object
            if selected_object:
                var tween = selected_object.create_tween()
                tween.tween_property(selected_object, "position", selected_object.memorized_position, 0.5)
				
                #delete dragged die
                if selected_object.detected:
                    selected_object.queue_free()

                selected_object = null
                is_dragging = false

    # Dragging the selected object
    if event is InputEventMouseMotion and is_dragging and selected_object:
        var mouse_pos = event.position
        var ray_origin = camera_3d.project_ray_origin(mouse_pos)
        var ray_direction = camera_3d.project_ray_normal(mouse_pos)

        # Recalculate ray
        var ray = PhysicsRayQueryParameters3D.new()
        ray.from = ray_origin
        ray.to = ray_origin + ray_direction * 1000
        var space_state = get_world_3d().direct_space_state
        var result = space_state.intersect_ray(ray)
        if result:
            var new_position = result.position + offset
            selected_object.global_transform.origin = new_position
            var tween = selected_object.create_tween()
            #selected_object.global_transform.origin = start_position + position_offset ##Line up instantly
            tween.tween_property(selected_object, "position", new_position, 0.05)

I feel like it has something to do with the way tween moves the object, but I couldn’t test it because I wasn’t able to figure out how to move it in a similar fashion by applying force.

The issue was in the dragging section

 if result:
            var new_position = result.position + offset
            selected_object.global_transform.origin = new_position
            var tween = selected_object.create_tween()
            #selected_object.global_transform.origin = start_position + position_offset ##Line up instantly
            tween.tween_property(selected_object, "position", new_position, 0.05)

the position is set to a coordinate then tweened to that coordinate again, fixed it by changing it to

 if result:
            var new_position = result.position + offset
            var tween = selected_object.create_tween()
            tween.tween_property(selected_object, "position", new_position, 0.05)

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.