RayCast detection does not detect straight away

Godot Version

4.2.2

Question

Hi,
I have an issue with my game were my raycast doesn’t detect on the frame I want it to. I know the collision works because it fires after the fact.

I have two objects in question - a player that moves on a grid and a turret that fires an arrow when it detects the player. I have it set up so that:

When the player moves they emit a signal called ‘moved’ (note it is emitted right after the Area2D position is changed). The turret is connected to this signal and runs a function called ‘_on_player_move()’. This updates the raycast and then fires the projectile if it detects the player. The issue is when the player first moves into the raycast it doesn’t do anything. Then when they player moves out or in another direction along the ray it shoots. i.e. one movement after when I want it to.

Here is the player code:

extends Area2D

var controllable = true
var tile_size = 16
var inputs = {
	"right": Vector2.RIGHT,
	"left": Vector2.LEFT,
	"up": Vector2.UP,
	"down": Vector2.DOWN
}
var frames = {
	"right": 1,
	"up": 2,
	"left": 3,
	"down": 0
}

signal moved

@onready var ray = $RayCast2D
@onready var sprite = $Sprite2D

# Called when the node enters the scene tree for the first time.
func _ready():
	position = position.snapped(Vector2.ONE * tile_size)
	position += Vector2.ONE * tile_size / 2

func _unhandled_input(event):
	if controllable:
		for dir in inputs.keys():
			if event.is_action_pressed(dir):
				move(dir)
				sprite.set_frame(frames[dir])
				moved.emit()

func move(dir):
	ray.target_position = inputs[dir] * tile_size
	ray.force_raycast_update()
	if ray.is_colliding():
		position += inputs[dir] * tile_size
		$MoveAudio.play()

Here is the turret code:

extends TileMap

var bullet = preload("res://scenes/bullet.tscn")

@onready var ray = $RayCast2D

# Called when the node enters the scene tree for the first time.
func _ready():
	%Player.moved.connect(_on_player_move)


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass


func _on_player_move():
	ray.force_raycast_update()
	if ray.is_colliding():
		var b = bullet.instantiate()
		add_child(b)
		b.position += Vector2(2, 8)

Uploaded a video to demonstrate here since I can’t upload attachments yet:

Raycasts are notorious for this. The solution I’ve used in my game is to wait for some frames in the on_player_move function, 2 frames work well for me.

##Wait two frames

var scene_tree = get_tree()
await scene_tree.physics_frame
await scene_tree.physics_frame

Another solution I’ve found to work well is to use the the PhysicsRayQuery to shoot a ray and check if it collides with something. The only downside is that, you don’t get to see a visual representation of the ray because it’s all in code.

var space_state = get_world_2d().direct_space_state
var query = PhysicsRayQueryParameters2D.create(origin_point, target_point)
		query.exclude = [agent]
		query.set_hit_from_inside(true)
		query.set_collide_with_areas(false)
		query.set_collide_with_bodies(true)

		var result = space_state.intersect_ray(query)

		if result:
			##Do something
2 Likes