Can't get movement to work relative to the camera

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By WhisperMain555

I am working on a 3D Sonic framework for Godot in GDScript, and currently I struggle to implement camera relative movement. I have already refractored several times, but the player keeps moving relative to it’s starting axises instead of relative to the camera! Help would be greatly appreciated.

I followed several tutorials to slowly build this (currently very early) framework, and similarly to GDScript’s 3D movement tutorials, the player object is a 3D KinematicBody and the camera is a springarm with the actual camera as it’s child object. Here is my current character controller script (attached to KinematicBody), minus some unused (commented-out) functions I removed:

extends KinematicBody

export var gravity = Vector3.DOWN * 10
export var speed = 8
export var rot_speed = 1.95
export var jump_speed = 9

var velocity = Vector3.ZERO

onready var _spring_arm: SpringArm = $SpringArm
onready var camera: Camera = $SpringArm/Camera

func _physics_process(delta):
	velocity += gravity * delta
	get_input(delta)
	velocity = move_and_slide_with_snap(velocity, Vector3.DOWN*2, Vector3.UP, true)
	if $RayCast.is_colliding():
		var n = $RayCast.get_collision_normal()
		var xform = align_with_y(global_transform, n)
		global_transform = global_transform.interpolate_with(xform, 0.2)
	else:
		set_rotation(Vector3(0,rotation.y,0))
	if Input.is_action_pressed("act_jump"):
		velocity.y = jump_speed
		velocity = move_and_slide_with_snap(velocity, Vector3.ZERO, Vector3.UP, true)

func align_with_y(xform, new_y):
	xform.basis.y = new_y
	xform.basis.x = -xform.basis.z.cross(new_y)
	xform.basis = xform.basis.orthonormalized()
	return xform

func get_input(_delta):
	var vy = velocity.y
	velocity = Vector3.ZERO
	var move_direction := Vector3.ZERO
	move_direction.x = Input.get_action_strength("act_right") - Input.get_action_strength("act_left")
	move_direction.z = Input.get_action_strength("act_down") - Input.get_action_strength("act_up")
	move_direction = move_direction.rotated(Vector3.UP, _spring_arm.rotation.y).normalized()
	get_node("../Control/Label").text = str(move_direction) + "\n" + str(_spring_arm.rotation.y)
	velocity.x = move_direction.x * speed
	velocity.z = move_direction.z * speed
	velocity.y = vy

I refractored the code several times, at one point get_input() used to look like this (I am including this anyway in case this is actually better that what I currently have, even though I really doubt it):

func get_input(_delta):
	var vy = velocity.y
	velocity = Vector3.ZERO
	if Input.is_action_pressed("act_up"):
		velocity += transform.basis.z * speed * Input.get_action_strength("act_up")
	if Input.is_action_pressed("act_down"):
		velocity += -transform.basis.z * speed * Input.get_action_strength("act_down")
	if Input.is_action_pressed("act_right"):
		velocity += -transform.basis.x * speed * Input.get_action_strength("act_right")
	if Input.is_action_pressed("act_left"):
		velocity += transform.basis.x * speed * Input.get_action_strength("act_left")
	#velocity.x *= camera.global_transform.basis.x
	#velocity.z *= camera.global_transform.basis.z
	velocity.y = vy

And finally, here’s the script that controls the camera (attached to SpringArm):

extends SpringArm

export var mouse_sensitivity := 0.05
export var rstick_sensitivity := 2

func _ready() -> void:
	#set_as_toplevel(true)
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		rotation_degrees.x -= event.relative.y * mouse_sensitivity
		rotation_degrees.x = clamp(rotation_degrees.x, -78.0, 30.0)
		
		rotation_degrees.y -= event.relative.x * mouse_sensitivity
		rotation_degrees.y = wrapf(rotation_degrees.y, 0.0, 360.0)

func _process(_delta):
	if Input.is_action_pressed("act_caml"):
		rotation_degrees.y -= Input.get_action_strength("act_caml") * -rstick_sensitivity
		rotation_degrees.y = wrapf(rotation_degrees.y,0.0, 360)
		
	if Input.is_action_pressed("act_camr"):
		rotation_degrees.y -= Input.get_action_strength("act_camr") * rstick_sensitivity
		rotation_degrees.y = wrapf(rotation_degrees.y,0.0, 360)
	
	if Input.is_action_pressed("act_camu"):
		rotation_degrees.x -= Input.get_action_strength("act_camu") * -rstick_sensitivity
		rotation_degrees.x = clamp(rotation_degrees.x, -78, 30)
		
	if Input.is_action_pressed("act_camd"):
		rotation_degrees.x -= Input.get_action_strength("act_camd") * rstick_sensitivity
		rotation_degrees.x = clamp(rotation_degrees.x, -78, 30)
	
	if Input.is_action_just_pressed("act_camreset"):
		rotation_degrees.y = get_node("../SonicDashSonic").rotation_degrees.y + 180
		rotation_degrees.x = -10

Sorry for my kinda messy code as well as not using Godot 4, I am still new to the engine and I haven’t had time to make the move to Godot 4 yet.

you can extract “right” vector from camera if it doesn’t rotate around Z axis

var right_vector = camera.global_transform.basis.x

with that you can get your “forward” vector like this

var forward_vector right_vector.cross( -gravity.normalized() )

LazyBigCat | 2023-03-05 22:32