# How can I separate my movement direction and velocity from my Camera3D's X rotation axis?

Attention Topic was automatically imported from the old Question2Answer platform.

Hello, I’m making a 3D first person game and I’ve been struggling with character movement.

If I’m looking downward or upward with my camera while moving, the movement direction and velocity are shifted toward unwanted values. The X axis’s value of the camera is clamped between -1.5 and 1.5. Basically, I’d like the movement direction and velocity to be independent of whether I’m looking up or down.

Hopefully someone has some kind of clue of what might be happening. I’ve linked the methods involved down below. If anything else is required, let me know so I can provide more information.

Hope you all have a good day, cheer!

``````func _rotate_camera(delta: float, sens_mod: float = 1.0) -> void:
look_dir += Input.get_vector("look_left", "look_right", "look_up", "look_down")
camera.rotation.z = lerp(camera.rotation.z, (-move_dir.x * (rotation_amount if (enable_camera_z_rotation && !leaning) else 0.0)), delta * 10)
body.rotation.y -= look_dir.x * camera_sensitivity * sens_mod * delta
camera.rotation.x = clamp(camera.rotation.x - look_dir.y * camera_sensitivity * sens_mod * delta, -1.5, 1.5)
look_dir = Vector2.ZERO

func _walk(delta: float) -> Vector3:
move_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_backwards")
var _forward: Vector3 = (camera.transform.basis * body.transform.basis) * Vector3(move_dir.x, 0, move_dir.y)
var walk_dir: Vector3 = Vector3(_forward.x, 0, _forward.z).normalized()
walk_vel = walk_vel.move_toward(walk_dir * (walking_speed + (running_speed if running else 0.0)) * move_dir.length(), acceleration * delta)
return walk_vel
``````

What do you mean by “unwanted values”?

TRAILtheGREAT | 2023-04-29 16:24

Well, simply put, it moves the player into incorrect direction. For example, if I’m pressing the forward key while looking either down or up, the player will be moved either left or right depending on the value of the camera’s Y rotation axis.

It’s somewhat tricky to describe without actually testing it, so I’ve linked the whole code and the scene if you wish to take a close look or even test it yourself.

Character controller (player.gd)

``````class_name Player extends CharacterBody3D

@export_category("Player Variables")
@export_range(1, 35, 1) var walking_speed: float = 4 # m/s
@export_range(1, 35, 1) var running_speed: float = 3 # m/s
@export_range(10, 400, 1) var acceleration: float = 40 # m/s^2
@export_range(0.1, 3.0, 0.1) var jump_height: float = 1 # m

@export_category("Player Controls")
@export var always_run: bool = false
@export var enable_camera_z_rotation: bool = true

@export_category("Camera Variables")
@export_range(0.1, 9.25, 0.05, "or_greater") var camera_sensitivity: float = 6.2
@export_range(75, 120, 1) var camera_fov: float = 90
@export_range(0, 0.1, 0.01) var rotation_amount: float = 0.06
@export_range(0.40, 1.7, 0.05) var camera_crouched_height: float = 0.85

var walking: bool = false
var jumping: bool = false
var running: bool = false
var leaning: bool = false
var crouching: bool = false
var crouch_toggle: bool = false
var mouse_captured: bool = false

var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")

var move_dir: Vector2 # Input direction for movement
var look_dir: Vector2 # Input direction for look/aim

var walk_vel: Vector3 # Walking velocity
var grav_vel: Vector3 # Gravity velocity
var jump_vel: Vector3 # Jumping velocity

var base_position_x: float
var base_position_y: float

@onready var body: CharacterBody3D = self
@onready var camera: Camera3D = \$Camera

capture_mouse()

func _input(event: InputEvent) -> void:
running = always_run != Input.is_action_pressed("run")
leaning = Input.is_action_pressed("lean_left") or Input.is_action_pressed("lean_right")
if event is InputEventMouseMotion: look_dir = event.relative * 0.01
if crouch_toggle:
if Input.is_action_just_released("crouch"):
crouching = false
crouch_toggle = false
if Input.is_action_just_pressed("toggle_crouch"):
crouch_toggle = !crouch_toggle
crouching = crouch_toggle or Input.is_action_pressed("crouch")
if Input.is_action_just_pressed("jump"): jumping = true
if Input.is_action_just_pressed("exit"):
capture_mouse() if (Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE) else release_mouse()

func _physics_process(delta: float) -> void:
if mouse_captured: _rotate_camera(delta)
velocity = _walk(delta) + _gravity(delta) + _jump(delta)
walking = true if velocity != Vector3.ZERO else false
_running(delta)
_leaning(delta)
_crouching(delta)
move_and_slide()

func capture_mouse() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
mouse_captured = true

func release_mouse() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
mouse_captured = false

func _rotate_camera(delta: float, sens_mod: float = 1.0) -> void:
look_dir += Input.get_vector("look_left", "look_right", "look_up", "look_down")
camera.rotation.z = lerp(camera.rotation.z, (-move_dir.x * (rotation_amount if (enable_camera_z_rotation && !leaning) else 0.0)), delta * 10)
body.rotation.y -= look_dir.x * camera_sensitivity * sens_mod * delta
camera.rotation.x = clamp(camera.rotation.x - look_dir.y * camera_sensitivity * sens_mod * delta, -1.5, 1.5)
look_dir = Vector2.ZERO

func _walk(delta: float) -> Vector3:
move_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_backwards")
var _forward: Vector3 = (camera.transform.basis * body.transform.basis) * Vector3(move_dir.x, 0, move_dir.y)
var walk_dir: Vector3 = Vector3(_forward.x, 0, _forward.z).normalized()
walk_vel = walk_vel.move_toward(walk_dir * (walking_speed + (running_speed if running else 0.0)) * move_dir.length(), acceleration * delta)
return walk_vel

func _gravity(delta: float) -> Vector3:
grav_vel = Vector3.ZERO if is_on_floor() else grav_vel.move_toward(Vector3(0, velocity.y - gravity, 0), gravity * delta)
return grav_vel

func _jump(delta: float) -> Vector3:
if jumping:
if is_on_floor(): jump_vel = Vector3(0, sqrt(4 * jump_height * gravity), 0)
jumping = false
return jump_vel
jump_vel = Vector3.ZERO if is_on_floor() else jump_vel.move_toward(Vector3.ZERO, gravity * delta)
return jump_vel

func _running(delta: float) -> void:
camera.fov = lerp(camera.fov, camera_fov + 15, delta * 10) if running else lerp(camera.fov, camera_fov, delta * 10)
if running:
crouch_toggle = false
crouching = false

func _crouching(delta: float) -> void:
camera.position.y = lerp(camera.position.y, camera_crouched_height, delta * 10) if (crouching && !running) else lerp(camera.position.y, 1.7, delta * 10)

if crouching:
running = false

func _leaning(delta: float) -> void:
if leaning:
camera.rotation.z = clamp(lerp(camera.rotation.z, 0.7 if Input.is_action_pressed("lean_left") else -0.7, delta * 3), -0.25, 0.25)
camera.position.x = clamp(lerp(camera.position.x, -0.30 if Input.is_action_pressed("lean_left") else 0.30, delta * 6), base_position_x - 1.0, base_position_x + 1.0)
else:
camera.position.x = lerp(camera.position.x, base_position_x, delta * 4)
if camera.position.x == base_position_x: base_position_x = camera.position.x
``````

**Player scene (player.tscn)**
``````[gd_scene load_steps=3 format=3 uid="uid://cvq4dvoyms4vw"]

[ext_resource type="Script" path="res://player/player.gd" id="1_2f8j2"]

[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3rsb5"]
height = 1.8

[node name="Player" type="CharacterBody3D"]
script = ExtResource("1_2f8j2")

[node name="CShape" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0)
shape = SubResource("CapsuleShape3D_3rsb5")

[node name="Camera" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.7, 0)
fov = 90.0
``````

WalterBennet | 2023-04-29 16:51

``````var _forward: Vector3 = (camera.transform.basis * body.transform.basis) * Vector3(move_dir.x, 0, move_dir.y)