- Edited
I'm new to Godot and I've been struggling with this issue for quite some time now:
In this prototype, the player is controlling a crosshair on screen by moving their mouse:
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
camera_look_input = event.global_position
func _process(delta: float) -> void:
process_input()
func process_input():
# Mouse
if Global.control_mode == Global.DEBUG_CONTROL_MOUSE:
raw_input_dir = camera_look_input
func move_crosshair():
crosshair.position = raw_input_dir
crosshair.position.x = clamp(crosshair.position.x, 0 ,get_viewport().get_visible_rect().size.x)
crosshair.position.y = clamp(crosshair.position.y, 0, get_viewport().get_visible_rect().size.y)
Then, a CharacterBody3D goes towards that crosshair:
func get_target_position() -> Vector2:
var target_position : Vector2
target_position.x = crosshair.position.x
target_position.y = crosshair.position.y
return target_position
func get_distance_to(point : Vector2) -> float:
var distance = point.distance_to(camera.unproject_position(global_position))
distance = min(distance, max_crosshair_distance)
distance = max(min_crosshair_distance, distance)
return distance / max_crosshair_distance
All of the calculations are done on a script on the CharacterBody3D:
func _physics_process(delta: float) -> void:
# Get the input direction and handle the movement/deceleration.
move_crosshair()
var target_position = get_target_position()
# Get the direction from the crosshair to the ship
var crosshair_direction = target_position.direction_to(camera.unproject_position(global_position))
var distance_accel = get_distance_to(target_position)
var crosshair_vector = Vector3(crosshair_direction.x, crosshair_direction.y, 0.0)
# Calculate the ship's direction based on its current transformation
var ship_direction = (global_transform.basis * crosshair_vector).normalized()
# Set maximum speed in both axis
var target_velocity = Vector3(ship_direction.x * pitch_speed, ship_direction.y * yaw_speed, 0.0)
# Gradual acceleration
current_velocity.x = lerp(current_velocity.x, target_velocity.x * distance_accel, movement_acceleration * delta)
current_velocity.y = lerp(current_velocity.y, target_velocity.y * distance_accel, movement_acceleration * delta)
velocity = current_velocity
move_and_slide()
All of this works well when the CharacterBody3D is facing the same direction as the world coordinates. I can even move forward automatically by adding a constant velocity.z.
Now, my idea was to be able to design levels by using Path3D and PathFollow3D, and still keep all the functionality described above. Think of it as a XY plane on a rail.
This is my setup:
- I've got a pair of RemoteTranform3D that will follow the path. As far as I know, placing the CharacterBody3D as a child of PathFollow3D would make it stick to the path, and I need it to move freely on the XY plane. The same idea applies to the camera, as I might want to move slightly following the player to make the scene more dynamic;
- Both CharacterBody3D and Camera3D as nested inside a Node3D. The camera has an offset on the Z axis so that it's behind the player automatically.
And this is the small test I've performed:
The green arrows represents which way the path aligns with the world's coordinate, and so everything works flawlessly. On the red arrow sections, the x-axis input translates to the world's x-axis instead of the desired one. Both the CharacterBody3D, as well as the Camera3D, align correctly at the new orientation, but my operations result in a Vector3 which z-axis is not parallel with the current basis.
I have been searching online and educating myself on vector math, including the official documentation, but I'm clearly missing something. Thank you for your attention, and apologies if the issue is something trivial that I'm not seeing.