How do I make a point-and-click movement in 3D?

Godot Version

Actually i use Godot 4.8

Question

i saw this code in the documentation :

Iit tried to do it in 3D but it doesn´t work, and all tutorial i´ve seen only confuse me. They talk about camera, raycasts and other things i actually don´t know.

3D is more complicated because of the extra dimension. While 2D is the same dimensions as your screen, a 3D world needs to be projected onto your screen so it can be displayed. As such, you can’t just point-and-click in 3D. It isn’t known, just from mouse position alone, where the point is in 3D space. That’s why you’re seeing stuff about cameras and raycasts.

Raycasts are just lines that you shoot which hit a surface in your 3D scene. In the context of point-and-click movement, rays are shot in the direction you are clicking, and the first surface hit in this direction is the point you want to move towards.

GodotPointAndClickRaycastOrbit

Depending on the features of your game, this movement is either 3D dimensional or limited to a plane (as is commonly seen in RTS games). I will show both because they are not that different. I’ve encapsulated each approach in their own function (world_intersect() and plane_intersect()).

extends Node3D

@export var plane_height = 0.0
@onready var character: Node3D = $"Character"

# The point the character should go to
var target_position: Vector3

func _physics_process(delta: float) -> void:
	# Move character around
	var dir = character.global_position.direction_to(target_position)
	var move_speed = 5
	
	var dist = character.global_position.distance_to(target_position)
	if (dist > 1):
		character.global_position += dir * move_speed * delta
	

func _input(event: InputEvent) -> void:
	if event is InputEventMouse:
		if event.button_mask == MOUSE_BUTTON_LEFT and event.is_pressed() and !event.is_echo():
			world_intersect(event)
			#plane_intersect(event)
plane_intersect() method
func plane_intersect(event: InputEvent):
	# Data
	var cam = get_viewport().get_camera_3d()
	var movement_plane = Plane(Vector3.UP, Vector3(0, plane_height, 0))
	var screen_point = event.position
			
	# Raycast Info
	var ray_normal = cam.project_ray_normal(screen_point)
	var ray_origin = cam.global_position
			
	# Intersect with plane
	var plane_point = movement_plane.intersects_ray(ray_origin, ray_normal)
	
	# Process intersection
	if plane_point:
		target_position = plane_point
world_intersect() method
func world_intersect(event: InputEvent):
	# Data
	var cam = get_viewport().get_camera_3d()
	var screen_point = event.position
	
	# Raycast Info
	var space_state = get_world_3d().direct_space_state
	var ray_normal = cam.project_ray_normal(screen_point)
	var ray_origin = cam.global_position
	var ray_length = 1000
	var ray_query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_normal * ray_length)
	
	# Intersect with world
	var result = space_state.intersect_ray(ray_query)
			
	# Process intersection
	if result:
		target_position = result.position

GodotPointAndClickExampleRaycastPlane
GodotPointAndClickExampleRaycastWorld

Different methods used: plane_intersect on left and world_intersect on right


I hope this gives you an idea of why this stuff is more complicated in 3D.

Here’s some helpful reading:

Thanks a lot, I’ve already tried it.:grin:

Hi, I’ve already tried it and it works, but there’s something that confuses me, and that’s how it interacts with the world.Does the code in the main scene simply a ray and then indicate which direction the player should move? But wouldn’t that affect how the player interacts with the rest of the world?

Yes, movement is a core feature for interacting with the game world. Is there something specific you are worried about?

Yes, when I start the scene, the player kind of sinks halfway into the floor, and I had to add a jump to make it work.
I understand that the main scene script is responsible for this movement, causing the player, as such, not to interact with the space as they should if it were handled from their own script (I’m not sure if I’m correct).

The code sample does not take player collision into account whatsoever, so if your player’s origin point is centered as opposed to at their feet then they will sink into the ground

You are correct. The movement seen in the example is purely to demonstrate how you might utilize the 3D point produced by the aforementioned methods. The exact way in which your character(s) should move is up to you.


If there are no further questions, please mark the post as solved.

That’s true, thank you very much.

Okay, thank you very much for the help.