I want to see if my first person controller (with headbobbing, footstep sounds, movement smoothing and mouse smoothing) could be improved, fixed or cleaned up in any way or if it is okay as it is now. Input and critique are appreciated.
Here's the code:
extends CharacterBody3D
@onready var neck = $AnimHolder/neck
#Neck: a Node3d which acts as a pivot to rotate the camera.
@onready var animHolder = $AnimHolder
#AnimHolder: a parent Node3D which applies animations without directly rotating the children inside.
@onready var hAnimTree = $AnimHolder/hAnimTree
#hAnimTree: The animation tree which controls the movements of the AnimHolder and other transforms.
@onready var colliderStanding = $collider_standing
#ColliderStanding: The collider that is active, currently. A 'crouching' collider will be added soon.
@onready var raycast_to_ground = $RaycastToGround
#RaycastToGround: A raycast pointing towards the ground.
@onready var pauseMenu = $"../pauseMenu"
#PauseMenu: The game's pause menu.
@onready var fstp_walk_tile = $footstepSound_walk
#FstpWalkTile: A footstep sound which plays when the headbobbing animations call it. More of these will be added soon.
@onready var fstp_jump_generic = $footstepSound_jump
#FstpJumpGeneric: A sound that plays when the player jumps. More will probably be added soon.
@export var captureMouseInsideWindow = true
#Determines if the mouse should be captured inside the window when everything loads into the scene tree.
@export var playerDizziness:float
#A for-fun variable that makes the player feel 'dizzy' if set to 1.
@export var walkSpeed = 5
#The player's walk speed.
@export var walkHeadbobSpeed = 1
#The player's headbobbing and footsteps speed when walking.
@export var runningSpeed = 10
#The player's running speed.
@export var runningHeadbobSpeed = 1.5
#The player's headbobbing and footsteps speed when running.
@export var friction = 10;
#The player's friction whilst on the ground. Lower = smoother, higher = more precise.
@export var midAirFriction = 0
#The player's relative friction whilst in the air.
@export var mouseFriction = 10;
#The player's mouse smoothing. Lower = smoother, higher = more precise.
@export var crouchedWalkSpeed = 3;
#The player's movement speed when crouched. This is unused, for now.
@export var jumpForce = 4.5
#The force that the player has while jumping.
@export var gravity = 9
#The intensity of the effect of gravity on the player.
@export var mouseSensitivity = 0.4;
#The player's mouse sensitivity. Higher = faster movement.
@export var mouseClampingDegrees = Vector2(-90, 90)
#A Vector2 which stores the minimum and maximum clamping degrees that the camera's pitch should be limited to.
var currentSpeed: float;
#The player's current movement speed. Switches between walking and running on-command.
var currentHeadbobSpeed: float;
#The player's current headbob and footstep speed.
var currentFriction: float;
#The player's current friction.
var cameraInput : Vector2
#Raw camera input from the player.
var movementDirection: Vector3
#The movement direction of the player.
var playerDirection: float;
#The yaw rotation of the player.
var cameraPitch: float;
#The pitch rotation of the camera.
var combinedRotation: Vector2
#Combines the camera pitch and player yaw into a Vector2.
var rotationVelocity: Vector2;
#A smoothed version of combinedRotation.
var movementVelocity: float
#A variable that roughly tracks the amount of movement the player is doing.
var isInAir: bool
var isMoving: float;
var isRunning: bool
var time: float
func leftFootstep():
fstp_walk_tile.play()
func rightFootstep():
fstp_walk_tile.play()
#Happens when the game has started and is ready to play.
func _ready():
if(captureMouseInsideWindow):
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
if(hAnimTree.active == false):
hAnimTree.active = true
func _input(event):
if event is InputEventMouseMotion:
cameraInput = event.relative
if event.is_action_pressed("escape"):
pauseMenu.pause()
func _process(delta):
time += delta
#Rough approximation of time since game started.
#Defining player direction and camera pitch
playerDirection -= cameraInput.x * mouseSensitivity
cameraPitch -= cameraInput.y * mouseSensitivity
cameraPitch = clamp(cameraPitch, mouseClampingDegrees.x, mouseClampingDegrees.y)
combinedRotation = Vector2(playerDirection, cameraPitch)
#Rough approximation of movement velocity.
movementVelocity = abs(movementDirection).length() * currentHeadbobSpeed
#Rotating camera and smoothing rotation
rotationVelocity = rotationVelocity.lerp(combinedRotation, delta * mouseFriction)
rotation.y = deg_to_rad(rotationVelocity.x)
neck.rotation.x = deg_to_rad(rotationVelocity.y)
cameraInput *= playerDizziness
#Camera animation
if(movementVelocity > 0.5):
hAnimTree.set("parameters/isMoving/transition_request", "move")
isMoving = true
else:
hAnimTree.set("parameters/isMoving/transition_request", "idle")
isMoving = false
if(is_on_floor()):
hAnimTree.set("parameters/inAir/transition_request", "ground")
else:
hAnimTree.set("parameters/inAir/transition_request", "air")
#The length of every headbob animation is set to 1 second. This will change the headbob and footstep speed.
hAnimTree.set("parameters/HeadbobTimeScale/scale", clamp(movementVelocity, walkHeadbobSpeed, INF))
#Happens in the physics timestep.
func _physics_process(delta):
if Input.is_action_pressed("run"):
isRunning = true
currentSpeed = runningSpeed
currentHeadbobSpeed = runningHeadbobSpeed
hAnimTree.set("parameters/isRunning/transition_request", "run")
else:
isRunning = false
currentSpeed = walkSpeed
currentHeadbobSpeed = walkHeadbobSpeed
hAnimTree.set("parameters/isRunning/transition_request", "walk")
if not is_on_floor():
velocity.y -= gravity * delta
currentFriction = friction*midAirFriction
else:
currentFriction = friction
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jumpForce
fstp_jump_generic.play()
var input_dir = Input.get_vector("left", "right", "forwards", "backwards")
movementDirection = lerp(movementDirection, (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized(), delta * currentFriction)
if movementDirection:
velocity.x = movementDirection.x * currentSpeed
velocity.z = movementDirection.z * currentSpeed
else:
velocity.x = move_toward(velocity.x, 0, currentSpeed)
velocity.z = move_toward(velocity.z, 0, currentSpeed)
move_and_slide()
Now, the scene:
And then the animation tree:
I'm planing to add more features such as leaning (with Q or E), crouching and picking up objects.
Thanks, Alte