Godot Version
Godot 4.2.1
Question
This script I have is acceptable in almost every way for what I need, but the way it works makes it so that the character tilts in the camera’s direction when moving. For example, if I move the camera above the character, the character will tilt upwards. I assume it’s how I settled the camera rotation with the input, but it was the only way I found to give the character a free camera while facing the direction of the input.
extends CharacterBody3D
const MAX_SPEED = 10.0
const ACCELERATION = 20.0
const DECELERATION = 20.0
const JUMP_VELOCITY = 4.5
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
var phantomCamera: PhantomCamera3D
var mouseSensitivity: float = 0.05
var minYaw: float = -89.9
var maxYaw: float = 50
var minPitch: float = 0
var maxPitch: float = 360
func _ready():
# Find the Phantom Camera node by its path
phantomCamera = $%PhantomCamera3D # Update this with the correct path to your Phantom Camera node
# Set mouse mode to captured to hide the cursor and confine it within the game window
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _physics_process(delta):
# Add the gravity.
if not is_on_floor():
velocity.y -= gravity * delta
# Handle Jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement.
var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var camera_direction = -phantomCamera.global_transform.basis.z.normalized()
var move_direction = (phantomCamera.global_transform.basis.x * input_dir.x + phantomCamera.global_transform.basis.z * input_dir.y).normalized()
if input_dir.length_squared() > 0:
# Calculate the target rotation based on the input direction
var targetRotation = atan2(move_direction.x, move_direction.z)
# Apply acceleration towards the maximum speed
var target_speed = MAX_SPEED
velocity.x = lerp(velocity.x, move_direction.x * target_speed, ACCELERATION * delta)
velocity.z = lerp(velocity.z, move_direction.z * target_speed, ACCELERATION * delta)
else:
# Apply deceleration if not moving
velocity.x = move_toward(velocity.x, 0, DECELERATION * delta)
velocity.z = move_toward(velocity.z, 0, DECELERATION * delta)
# Clamp velocity to the maximum speed
velocity.x = clamp(velocity.x, -MAX_SPEED, MAX_SPEED)
velocity.z = clamp(velocity.z, -MAX_SPEED, MAX_SPEED)
# Update character's rotation based on camera rotation when moving forward or backward
if input_dir.y != 0:
$%Character.look_at(global_transform.origin + move_direction, Vector3.UP)
elif input_dir.x != 0:
$%Character.look_at(global_transform.origin + move_direction, Vector3.UP)
# Update camera position to follow the player
if phantomCamera != null:
phantomCamera.global_transform.origin = global_transform.origin
# Set animation parameters
$AnimationTree.set("parameters/conditions/idle", input_dir == Vector2.ZERO && is_on_floor())
$AnimationTree.set("parameters/conditions/walk", input_dir != Vector2.ZERO && is_on_floor())
$AnimationTree.set("parameters/conditions/falling", !is_on_floor())
$AnimationTree.set("parameters/conditions/landed", is_on_floor())
move_and_slide()
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
if phantomCamera != null && phantomCamera.global_transform != null:
var pcamRotationDegrees = phantomCamera.get_third_person_rotation_degrees()
# Change the X rotation
pcamRotationDegrees.x -= event.relative.y * mouseSensitivity
# Clamp the rotation in the X axis so it doesn't go over or under the target
pcamRotationDegrees.x = clampf(pcamRotationDegrees.x, minYaw, maxYaw)
# Change the Y rotation value
pcamRotationDegrees.y -= event.relative.x * mouseSensitivity
# Sets the rotation to fully loop around its target, but without going below or exceeding 0 and 360 degrees respectively
pcamRotationDegrees.y = wrapf(pcamRotationDegrees.y, minPitch, maxPitch)
# Change the Phantom Camera node's rotation and rotate around its target
phantomCamera.set_third_person_rotation_degrees(pcamRotationDegrees)