How to direct raycast to the middle of the player's screen?

Godot Version

4.4.1

Question

So, I’m currently in the middle of implementing my projectile shooting systems. (For context, I’m making a third-person shooter game)

I DO know how to manipulate raycasts to go to a specific location via setting a new target position and force updating.

However, I DON’T know what math formula I need to calculate a raycast to the middle of the screen. (Toward a UI crosshair in the center of the screen) Then, shoot a physical projectile in the trajectory of that raycast.

My shooting code is pretty simple, as this is the test version I’m using to implement the base gunplay systems.

The player script shoots the projectile:

func primary_action() -> void:
	
	print("Primary Action")
	
	var Projectile_Instance : Node = Test_Projectile.instantiate()
	
	Projectile_Instance.Projectile_Spawn_Position = Temp_Gun_Position.global_position
	
	Projectile_Instance.Projectile_Spawn_Rotation = Temp_Gun_Position.global_rotation
	
	Projectile_Instance.Projectile_Movement_Direction = Temp_Gun_Position.global_rotation 
	
	add_sibling(Projectile_Instance)

Then, the projectile script just acts as a template to be filled by the player script:

#------------------------------------------#
# Variables:
#------------------------------------------#

# Projectile stats
var Projectile_Movement_Direction : Vector3
var Projectile_Spawn_Position : Vector3
var Projectile_Spawn_Rotation : Vector3
var Projectile_Speed : float = 15

var Current_Projectile_Lifetime : float = 0.0
const MAX_PROJECTILE_LIFETIME : float = 2.0

# Nodes
@onready var Projectile_Hitbox : CollisionShape3D = $CollisionShape3D
@onready var Projectile_Mesh : MeshInstance3D = $MeshInstance3D

#------------------------------------------#
# Virtual Functions:
#------------------------------------------#

func _ready() -> void:
	
	# Apply player given stats to the projectile on spawn
	global_position = Projectile_Spawn_Position
	global_rotation = Projectile_Spawn_Rotation
	

@warning_ignore("unused_parameter")
func _process(delta: float) -> void:
	pass

func _physics_process(delta: float) -> void:
	
	# Move the projecile forward every physics tick
	velocity -= global_basis * Vector3.FORWARD * Projectile_Speed * delta
	
	# Needed to trigger physics
	move_and_slide()
	
	# Tick projectile lifetime counter
	Current_Projectile_Lifetime += delta
	
	# Delete projectile if lifetime runs out
	if Current_Projectile_Lifetime >= MAX_PROJECTILE_LIFETIME:
		queue_free()

I hope this is a good enough explanation because my brain is fried at the moment. Let me know if you need any clarification.

If you want to raycast towards where the crosshair is pointing then you will need another raycast from the crosshair forwards, get it’s colliding position and target that with your original raycast.

1 Like

Ok, it’s 20 days later, and after a lot of lollygagging, I figured out how to do this with Gert’s help and my old notes. All while livestreaming as well.

Edit: It’s also NOT a math formula. I just got it confused because of my old Unreal 4/5 implementation muscle memory.

To do this, you need to convert the camera raycast’s hit location to a local position rather than a global position.

The code is the following:

# Update the camera raycast
	Camera_Raycast.force_raycast_update()
	
	# Create collision variables
	var Camera_Raycast_Collision_Point : Vector3 = Camera_Raycast.get_collision_point()
	var Target_Raycast_Location : Vector3 = Muzzle_Raycast.to_local(Camera_Raycast_Collision_Point)
	
	# Update muzzle raycast to go toward camera raycast hit location
	Muzzle_Raycast.target_position = Target_Raycast_Location
	Muzzle_Raycast.force_raycast_update()
	
	# Instantiate projectile
	var Projectile_Instance : Node = Test_Projectile.instantiate()
	
	# Set projectile spawn position at the weapon muzzle
	Projectile_Instance.Projectile_Spawn_Position = Temp_Gun_Position.global_position
	
	# Set projectile spawn rotation
	Projectile_Instance.Projectile_Spawn_Rotation_X = Camera_Reference.global_rotation.x * -1
	Projectile_Instance.Projectile_Spawn_Rotation_Y = Camera_Pitch.global_rotation.y
	Projectile_Instance.Projectile_Spawn_Rotation_Z = Camera_Reference.global_rotation.z * -1
	
	# Spawn projectile in scene
	add_sibling(Projectile_Instance)

The main lines are these here:

var Camera_Raycast_Collision_Point : Vector3 = Camera_Raycast.get_collision_point()
var Target_Raycast_Location : Vector3 = Muzzle_Raycast.to_local(Camera_Raycast_Collision_Point)

The first line gets the camera raycast’s collision point and saves it to a variable.

The second line takes that collision point, and converts it to a local position for the muzzle raycast.

This local position is then made into the muzzle raycast’s target position and an update is forced.

Now that that’s done, I need the projectile to actually follow the muzzle raycast. I’ll figure that out soon enough. Thanks for the help.

Normal or normal reversed of your successful raycast should be the projectile direcrion, barring walking while posting stupidity on my part :wink:
Cheers!