To move the player around relative to the camera, you to know where the camera is facing. You can do this using transform.basis
, which has three vectors (x
, y
, and z
) which each of the three dimension axes relative to the rotation of the object. To put it another way, the three vectors stored in transform.basis
correspond to the X, Y, and Z arrows seen when selecting an object in the Godot editor with "Use Local Space" (the 3D square icon in next to snapping options) mode enabled.
So long as you are trying to move the player along one of those axis, the code is not too difficult. Something like this, in theory, should allow for basic movement relative to a Camera node (untested):
Extends KinematicBody
var velocity = Vector3.ZERO
var move_speed = 6
var camera_node
func _ready():
camera_node = get_node("Camera")
func _process(delta):
# Get the camera's Z and X vectors
# (Note: we are getting the negative Z because that is what the Camera node faces)
var camera_forward_vector = -camera_node.global_transform.basis.z
var camera_right_vector = camera_node.global_transform.basis.z
# Normalize both vectors so the scale of the Camera node does not change anything
# (While this is unlikely, I have had it happen a few times)
camera_forward_vector = camera_forward_vector.normalized()
camera_right_vector = camera_right_vector.normalized()
# (Optional) Remove any movement on the Y axis so the player cannot fly
# or sink into the ground simply by walking
camera_forward_vector.y = 0
camera_right_vector.y = 0
# Simple movement example.
# Check out the FPS tutorial for a, potentially, better solution
if (Input.is_key_pressed(KEY_RIGHT)):
# Add the vector pointing in the X direction relative to the camera
# so the player moves right relative to the camera.
# Multiply the vector by move_speed and delta to the player moves
# move_speed many units every second.
velocity += camera_right_vector * move_speed * delta
elif (Input.is_key_pressed(KEY_LEFT)):
# Same thing, use the negative X axis so the player moves left
# relative to the camera instead of right.
velocity -= camera_right_vector * move_speed * delta
if (Input.is_key_pressed(KEY_UP)):
# Add the vector pointing in the -Z direction relative to the camera
# so the player moves forward relative to the camera.
velocity.z += camera_forward_vector * move_speed * delta
elif (Input.is_key_pressed(KEY_DOWN)):
# Same thing, but for backwards movement
velocity.z -= camera_forward_vector * move_speed * delta
# Then use move_and_slide like normal, and movement should be relative
# to the camera
move_and_slide(velocity, Vector3.UP)
Hopefully this makes sense and helps a bit! Movement in 3D can be kinda tricky. :smile:
Bonus answer: You can also use a Vector3 and the Basis, though it is not how I normally tackle the issue. It is entirely doable, and for some things it is the best way to do it (like for IK), but I personally find using the vectors stored within the basis is easier to understand and debug.
You can use the code above, just change _process
to the following (untested):
func _process(delta):
# Store where the player intends to go in this variable
var player_input_direction = Vector3.ZERO
# Simple movement example.
# Modify the player input direction like normal.
if (Input.is_key_pressed(KEY_RIGHT)):
player_input_direction.x = 1
elif (Input.is_key_pressed(KEY_LEFT)):
player_input_direction.x = -1
if (Input.is_key_pressed(KEY_UP)):
player_input_direction.z = -1
elif (Input.is_key_pressed(KEY_DOWN)):
player_input_direction.z = 1
# Use the basis to rotate the player_input_direction vector
# relative to the rotation of the Camera node!
var player_world_input_direction = (
camera_node.global_transform.basis.xform(player_input_direction)
)
# Normalize to remove any scaling stored in the Camera's basis
player_world_input_direction = player_world_input_direction.normalized()
# Now we have a normalized vector that is pointing in the direction the player
# wants to go, relative to the Camera node's rotation!
# Now we want to multiply by move_speed and delta, then add to velocity
player_world_input_direction *= move_speed * delta
velocity += player_world_input_direction
# Then use move_and_slide like normal, and movement should be relative
# to the camera
move_and_slide(velocity, Vector3.UP)