World
|-Camera3D
|-DirectionalLight3D
|-Floor
|-Player
|-NPC


NPC
|-BodyMesh
|-CollisionShape3D
|-VisionArea(Area3D)
|- - CollisionShape3D
|-RayCast3D
|-VisionTimer

Above is the tree structure of my nodes.

I implemented Player and NPC using CharacterBody3D.

  • Player uses the initial node value that moves according to the input almost as it is, and has CollisionShape3D and Mesh.
  • NPC has the same composition, but has Area3D, spherical CollisionShape3D, and RayCast3D.

I've tried several ways to get the NPC to detect the Player using Area3D. However, no printing is done no matter how much the player is moved into the Area3D included in the NPC.
The collision mask was initially given, and the layer also used the default value. And I didn't forget to put the Player node into the Player group. I'm not sure what the problem is.

Here is my code:

extends CharacterBody3D


const SPEED = 5.0
const JUMP_VELOCITY = 4.5

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")


func _ready():
	pass

func _physics_process(delta):
	# Add the gravity.
	if not is_on_floor():
		velocity.y -= gravity * delta

	# Handle Jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()
	
func _on_vision_area_body_entered(body):
	print("Here's body: ", body)

func _on_vision_timer_timeout():
	var overlaps = $VisionArea.get_overlapping_bodies()
	if overlaps.size() > 0:
		print("something is here")
		for overlap in overlaps:
			if overlap.is_in_group("Player"):
				var playerPosition = overlap.global_transform.origin
				$VisionRaycast.look_at(playerPosition, Vector3.UP)
				
				$VisionRaycast.force_raycast_update()
				
				if $VisionRaycast.is_colliding():
					var collider = $VisionRaycast.get_collider()
					
					if collider.is_in_group("Player"):
						$VisionRaycast.debug_shape_custom_color = Color(174,0,0)
						print("I see you")
					else:
						$VisionRaycast.debug_shape_custom_color = Color(0,255,0)
						print("I don't see you")
  • Update:
    Find out some reasons and problems solved.

    Case 1 -
    func _on_vision_timer_timeout():

    fails to work before ,

    var overlaps = $VisionArea.get_overlapping_bodies()

    works.
    Some nodes in the sample project imported from previous versions of Godot lost their signals.

    Case 2 - Because Area3D did not assign proper CollisionShape to the object to be detected.

    Now my actors can move raycasts against each other and detect them based on Area.

situation update:

For some reason, after consulting on the official Discord and simply visualizing the Debug shape, the code started working properly. Maybe I fixed some part of the code, but I'm not sure when and where I fixed it. I think I will have the same problem over and over again due to my lack of understanding of the engine and scripts. I'll update this post when I run into any issues with it.

Update:
Find out some reasons and problems solved.

Case 1 -
func _on_vision_timer_timeout():

fails to work before ,

var overlaps = $VisionArea.get_overlapping_bodies()

works.
Some nodes in the sample project imported from previous versions of Godot lost their signals.

Case 2 - Because Area3D did not assign proper CollisionShape to the object to be detected.

Now my actors can move raycasts against each other and detect them based on Area.