Mouse Raycast Projection Help

Godot Version

4.4

Question

I am trying to project a ray from the mouses camera position to the world to select and interact with objects but i can’t get the projection right.

here is my code:

func _init_mouse_line():
	mouse_line = Game.debug_line(Vector3.ZERO, Vector3.ZERO, Color.BLACK)
	mouse_point = Game.debug_point(Vector3.ZERO, 0.05)

func get_mouse_pos():
	var space = get_world_3d().direct_space_state
	var mouse_pos = get_viewport().get_mouse_position()
	var ray_length = 1000

	var ray_origin = camera.project_ray_origin(mouse_pos)
	var ray_end = ray_origin + camera.project_ray_normal(mouse_pos) * ray_length

	var params = PhysicsRayQueryParameters3D.create(ray_origin, ray_end)
	
	params.collide_with_areas = true

	raycast_result = space.intersect_ray(params)

	if !raycast_result.is_empty():
		var mouse_line_immediate_mesh = mouse_line.mesh as ImmediateMesh
		mouse_line_immediate_mesh.clear_surfaces()
		mouse_line_immediate_mesh.surface_begin(Mesh.PRIMITIVE_LINES)
		mouse_line_immediate_mesh.surface_add_vertex(ray_origin)
		mouse_line_immediate_mesh.surface_add_vertex(raycast_result.position)
		mouse_line_immediate_mesh.surface_end()
		mouse_point.position = raycast_result.position

I have a debug point mesh to show the ray collisions and a line from the rays origin and end. Here is a clip of my problem

The collision point follows the mouse but it is not aligned with the mouse the way i want. How do i get the projection to point directly to what is behind the mouse?

I wasn’t able to reproduce this issue.

Might need more details about how your scene is set up, specifically the hierarchy and transform of related objects, and what the mysterious Game singleton is.

One thing I noticed about your code is that you’re setting the mouse_point.position, which is in local space, but raycast_result.position is in global space.

It seems like you’re using some sort of global singleton to make the mouse_point object, so I assume it is not in a transformed space. But it might be worth trying mouse_point.global_position = raycast_result.position instead.

Make sure that nothing related to these objects is rotated or scaled weirdly.

Also check if the inaccuracy is affected by window size, if it is that might indicate an issue with the viewport’s scaled mouse coordinates.

1 Like

The Game singleton is pretty much an event bus for other parts of my project, it just where i placed the function to create the meshes for the line and point debug:

func debug_line(point_a: Vector3, point_b: Vector3, color = Color.WHITE_SMOKE) -> MeshInstance3D:
	var mesh_instance = MeshInstance3D.new()
	var immediate_mesh = ImmediateMesh.new()
	var material = ORMMaterial3D.new()

	mesh_instance.mesh = immediate_mesh
	mesh_instance.cast_shadow = false

	immediate_mesh.surface_begin(Mesh.PRIMITIVE_LINES, material)
	immediate_mesh.surface_add_vertex(point_a)
	immediate_mesh.surface_add_vertex(point_b)
	immediate_mesh.surface_end()
	
	material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
	material.albedo_color = color
	
	get_tree().root.add_child(mesh_instance)
	
	return mesh_instance

func debug_point(pos: Vector3, radius = 0.1, color = Color.WHITE_SMOKE, persist_ms = 0):
	var mesh_instance := MeshInstance3D.new()
	var sphere_mesh := SphereMesh.new()
	var material := ORMMaterial3D.new()

	mesh_instance.mesh = sphere_mesh
	mesh_instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
	mesh_instance.position = pos

	sphere_mesh.radius = radius
	sphere_mesh.height = radius*2
	sphere_mesh.material = material

	material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
	material.albedo_color = color
	
	get_tree().root.add_child(mesh_instance)
	return mesh_instance

I tried your suggestion, changing mouse_point.global_position = raycast_result.position did not have any effect.

Here is my scene:

The script in my op is attached to the PlayerCamera node, the CameraRoot node moves the horizontal node anf vertical spring arm to move the camera around the player as shown in the video in op.

Here is that script:

@onready var cameraRotation_horizontal = %Horizontal
@onready var cameraSpringArm:SpringArm3D = %Vertical

const springLengthMin = -20
const springLengthMax = -6
@export_range(springLengthMin,springLengthMax) var springLength = -10

var direction: Vector3
var camroot_h = 0
var camroot_v = 0

@export var cam_v_max = 75
@export var cam_v_min = -55
var h_sensitivity: float = 0.01
var v_sensitivity: float = 0.01
var h_acceleration: float = 10.0
var v_acceleration: float = 10.0

var horizontal_camRotation

func _ready() -> void:
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	springLength = -10
	horizontal_camRotation = cameraRotation_horizontal.global_transform.basis.get_euler().y
	direction = Vector3.BACK.rotated(Vector3.UP, cameraRotation_horizontal.global_transform.basis.get_euler().y)
	
func _input(event: InputEvent) -> void:
	if event is InputEventMouseMotion and Input.is_action_pressed("camera_drag"):
		camroot_h += -event.relative.x * h_sensitivity
		camroot_v += event.relative.y * v_sensitivity
	if event is InputEventMouseButton and Input.is_action_pressed("camera_drag"):
		direction = cameraRotation_horizontal.global_transform.basis.z
		if Input.is_action_just_released("mouse_scroll_up"):
			print("Wheel Up")
			springLength += 2
		elif Input.is_action_just_released("mouse_scroll_down"):
			print("Wheel Down")
			springLength -= 2

func _physics_process(delta: float):
	self.transform = Game.player.transform
	cameraSpringArm.spring_length = lerpf(cameraSpringArm.spring_length, springLength, 0.1)
	if springLength < springLengthMin:
		springLength = springLengthMin
	if springLength > springLengthMax:
		springLength = springLengthMax
	camroot_v = clamp(camroot_v, deg_to_rad(cam_v_min), deg_to_rad(cam_v_max))
	%Horizontal.rotation.y = lerpf(%Horizontal.rotation.y, camroot_h, delta*h_acceleration)
	%Vertical.rotation.x = lerpf(%Vertical.rotation.x, camroot_v, delta*v_acceleration)

This whole PlayerCamera scene is attached to my player character and added into the main scene.

One thing i have noticed is that the point aligns with the mouse when its in the top left corner of the screen but gets further away from the mouse when it moves away from the corner.