Godot and Programming Novice here, I found that when using camera.transform.basis to use camera based movement, z velocity becomes reverse proportional to the camera’s x rotation, if the camera is top down (x rotation at 90.0 degrees), my z movement is 0, I become unable to move forwards and backwards at all, only left and right (x movement is just fine). Z movement gets faster the further away from 90 degrees the camera becomes. Is there a way to keep camera based movement and not have this side effect?
The game I want to make will have a camera system like metal gear solid with dynamic angles and follow rates based on global position, which will include top down perspective pov
I use the phantom camera plugin for this, but I tested this in a fresh scene with only my player character scene, a static body 3d floor and a camera3D node and received the same results
(I originally thought this was a phantom camera problem)
My player character uses state machine level movement (no movement code in the character body 3d root node script), here is the full script
(note 1, handle_input and Physics_Update calls _input and _physics_process respectively)
(note 2, my custom class “State” contains the @onready for my character body 3D node, as well at the func in this script, which only calls the named functions of the active state)
extends State
class_name Running
@onready var cam1: Camera3D = $"../../../MainCamera3D"
@onready var anim: AnimationPlayer = %AnimationPlayer
func Enter():
print("you are in running")
anim.play("Snoke Movements Test 2/Run")
func handle_input(_event: InputEvent):
if Input.is_action_just_pressed("testroll"):
state_machine.change_state("Tackle")
func Physics_Update(_delta: float):
var input_vector = Input.get_vector("testrunleft", "testrunright", "testrunforward", "testrundown")
var direction := (cam1.transform.basis * Vector3(input_vector.x, 0, input_vector.y)).normalized()
if direction == Vector3.ZERO:
state_machine.change_state("idle")
return
player.velocity.x = direction.x * SPEED
player.velocity.z = direction.z * SPEED
player.move_and_slide()
I think it might be the pitch component of your Camera that’s causing the forward vector to point downward. You need to remove it like this for example:
var camera_basis := cam1.global_transform.basis
# Remove the camera's pitch so forward/right stay horizontal
camera_basis.y = Vector3.UP
# Get the flattened basis
camera_basis = camera_basis.orthonormalized()
# Replace the cam1.transform.basis in the direction with the flattened camera_basis
var direction := (camera_basis * Vector3(input_vector.x, 0, input_vector.y)).normalized()
Try this if it works. This basically just removes the tilt from the Y axis and creates a flattened, normalized orthogonal movement (not view), which prevents the pitch from affecting horizontal movement. This does not remove the camera-relative movement, just the vertical influence.
Edit: I had to try if this works, and it does, but when looking straight down, a gimbal lock happens sometimes and causes random rotations. Here is a cleaner way I found:
var input_vector = Input.get_vector("testrunleft", "testrunright", "testrunforward", "testrundown")
# Get the camera basis
var camera_basis := cam1.global_transform.basis
# Get the horizontal and vertical directions using the camera's X and Z
# The X for Left/Right and Z for Forward/Backward
# Since Godot has negative Z as forward, we use negative for Z
var movement_dir = (cam_basis.x * input_vector.x) - (cam_basis.z * input_vector.y)
# Then we "flatten" the movement by removing the Y influence
movement_dir.y = 0
var direction := movement_dir.normalized()
This ensures proper camera-relative movement while preventing potential Gimbal Lock. If the forward/backward is still reversed, just change the subtract in the movement_dir to add. I hope this helps!
You solved the core problem of the movement being locked on the camera’s x axis! However a few other issues popped up in that my z movement was now mirrored under a 90 degree camera and in one camera up and right and down and left were going in the same left/right direction.
Perhaps it’s not optimal, but I found a compromise using your code as the new var direction and I added this
# Alternative direction specifically for -90 degree x rotated camera
var directiontop := (player.transform.basis * Vector3(-input_vector.x, 0, -input_vector.y)).normalized()
# Conditional movement depending on top down view or not
if cam1.rotation_degrees == Vector3(-90.0, 0.0, 0.0):
player.velocity.x = directiontop.x * SPEED
player.velocity.z = directiontop.z * SPEED
else:
player.velocity.x = direction.x * SPEED
player.velocity.z = direction.z * SPEED
This achieves the result I need, so i suppose it’s all worked out!
Thank you!