Alright, here’s everything!
extends CharacterBody3D
# node vars
@onready var player: CharacterBody3D = $"."
@onready var nek: Node3D = $nek
@onready var head: Node3D = $nek/head
@onready var eyes: Node3D = $nek/head/eyes
@onready var camera_3d: Camera3D = $nek/head/eyes/Camera3D
@onready var standing_colision_shape: CollisionShape3D = $StandingColisionShape
@onready var crouching_colision_shape: CollisionShape3D = $CrouchingColisionShape
@onready var ray_cast_3d: RayCast3D = $RayCast3D
@onready var ray_cast_3d_2: RayCast3D = $RayCast3D2
@onready var ray_cast_3d_3: RayCast3D = $RayCast3D3
@onready var ray_cast_3d_4: RayCast3D = $RayCast3D4
@onready var player_animation: AnimationPlayer = $nek/head/eyes/AnimationPlayer
# sound vars
@onready var jump_sfx: AudioStreamPlayer3D = $audio/sfx/jumpSFX
@onready var land_sfx: AudioStreamPlayer3D = $audio/sfx/landSFX
@onready var slide_sfx: AudioStreamPlayer3D = $audio/sfx/slideSFX
@onready var sprinting_sfx: AudioStreamPlayer3D = $audio/sfx/sprintingSFX
@onready var walking_sfx: AudioStreamPlayer3D = $audio/sfx/walkingSFX
@onready var supersprinting_sfx: AudioStreamPlayer3D = $audio/sfx/supersprintingSFX
var sfx_volume = -10.0
var sfx_off = -100.0
# speed vars
var current_speed = 5.0
@export var crouch_speed = 3.0
@export var walking_speed = 4.0
@export var sprint_speed = 9.0
@export var super_sprint_speed = 11.0
@export var mouse_sentitivity = 0.3
var lerp_speed = 8.0
var lerp_speed_slow = 2.0
var lerp_speed_air = 1.0
# states
var walking = false
var sprinting = false
var crouching = false
var freelooking = false
var sliding = false
var super_sprinting = false
var dead = false
# slide vars
var slide_timer = 0.0
var slide_timer_max = 1.0
var slide_vector = Vector2.ZERO
@export var slide_jump_velocity = 6
@export var slide_speed = 15
# head bobbing vars
const head_bobbing_sprinting_speed = 16.0
const head_bobbing_walking_speed = 14.0
const head_bobbing_crouching_speed = 10.0
const head_bobbing_sprinting_intensity = 0.3
const head_bobbing_walking_intensity = 0.1
const head_bobbing_crouching_intensity = 0.05
var head_bobbing_vector = Vector2.ZERO
var head_bobbing_index = 0.0
var head_bobbing_current_intensity = 0.0
# movement vars
var direction = Vector3.ZERO
@export var crouching_depth = -0.2
@export var jump_velocity = 4.5
var freeLookTilt = 5
var last_velocity = Vector3.ZERO
var wall_normal = get_wall_normal()
var bounce_velocity = Vector3.ZERO
func _ready() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _input(event: InputEvent) -> void:
#mouse movement
if dead:
return
if event is InputEventMouseMotion:
if freelooking:
nek.rotate_y(deg_to_rad(-event.relative.x * mouse_sentitivity))
nek.rotation.y = clamp(nek.rotation.y,deg_to_rad(-120),deg_to_rad(120))
head.rotate_x(deg_to_rad(-event.relative.y * mouse_sentitivity))
head.rotation.x = clamp(head.rotation.x,deg_to_rad(-89),deg_to_rad(89))
else:
rotate_y(deg_to_rad(-event.relative.x * mouse_sentitivity))
head.rotate_x(deg_to_rad(-event.relative.y * mouse_sentitivity))
head.rotation.x = clamp(head.rotation.x,deg_to_rad(-89),deg_to_rad(89))
func _physics_process(delta):
if !is_on_floor() && is_on_wall():
bounce_velocity = velocity.bounce(wall_normal)
velocity = bounce_velocity
# getting movement input
var input_dir := Input.get_vector("left", "right", "forward", "backward")
## handle movement state
# slide begin logic
if ((sprinting or super_sprinting) && Input.is_action_just_pressed("slide") && input_dir != Vector2.ZERO && !sliding) && !is_on_wall() && is_on_floor():
sliding = true
freelooking = true
slide_timer = slide_timer_max
slide_vector = input_dir
print("slide begin")
head.position.y = lerp(head.position.y,crouching_depth,delta*lerp_speed)
slide_sfx.set_volume_db(sfx_volume)
slide_sfx.play()
# crouching
if Input.is_action_pressed("crouch"):
current_speed = lerp(current_speed,crouch_speed,delta*lerp_speed)
head.position.y = lerp(head.position.y,crouching_depth,delta*lerp_speed)
standing_colision_shape.disabled = true
crouching_colision_shape.disabled = false
sliding = false
walking = false
sprinting = false
crouching = true
super_sprinting = false
elif !ray_cast_3d.is_colliding() and !ray_cast_3d_2.is_colliding() and !ray_cast_3d_3.is_colliding() and !ray_cast_3d_4.is_colliding():
# standing
standing_colision_shape.disabled = false
crouching_colision_shape.disabled = true
head.position.y = lerp(head.position.y,0.0,delta*lerp_speed)
## handle sprinting
if Input.is_action_pressed("sprint"):
# super sprinting
if super_sprinting:
current_speed = lerp(current_speed,super_sprint_speed,delta*lerp_speed)
walking = false
sprinting = false
crouching = false
super_sprinting = true
# sprinting
elif is_on_floor():
current_speed = lerp(current_speed,sprint_speed,delta*lerp_speed)
walking = false
sprinting = true
crouching = false
super_sprinting = false
# walking
elif is_on_floor():
current_speed = lerp(current_speed,walking_speed,delta*lerp_speed)
walking = true
sprinting = false
crouching = false
super_sprinting = false
# handle freelooking
if Input.is_action_pressed("freeLook") or sliding:
freelooking = true
eyes.rotation.z = -deg_to_rad(nek.rotation.y * freeLookTilt)
else:
freelooking = false
nek.rotation.y = lerp(nek.rotation.y,0.0,delta*lerp_speed)
eyes.rotation.z = lerp(eyes.rotation.z,0.0,delta*lerp_speed)
# slide end logic
if sliding:
slide_timer -= delta
standing_colision_shape.disabled = true
crouching_colision_shape.disabled = false
crouching = true
if slide_timer <= 0 and is_on_floor():
sliding = false
freelooking = false
print("slide end")
elif is_on_wall() or is_on_ceiling():
sliding = false
freelooking = false
super_sprinting = false
if !sliding:
slide_sfx.set_volume_db(lerp(slide_sfx.volume_db,sfx_off,delta*lerp_speed_slow))
# handle headbobbing
if !sliding:
if sprinting or super_sprinting:
head_bobbing_current_intensity = head_bobbing_sprinting_intensity
head_bobbing_index += head_bobbing_sprinting_speed*delta
elif walking:
head_bobbing_current_intensity = head_bobbing_walking_intensity
head_bobbing_index += head_bobbing_walking_speed*delta
elif crouching:
head_bobbing_current_intensity = head_bobbing_crouching_intensity
head_bobbing_index += head_bobbing_crouching_speed*delta
if is_on_floor() && !sliding && input_dir != Vector2.ZERO:
head_bobbing_vector.y = sin(head_bobbing_index)
head_bobbing_vector.x = sin(head_bobbing_index/2)+0.5
eyes.position.y = lerp(eyes.position.y,head_bobbing_vector.y*(head_bobbing_current_intensity/2),delta*lerp_speed)
eyes.position.x = lerp(eyes.position.x,head_bobbing_vector.x*head_bobbing_current_intensity,delta*lerp_speed)
else:
eyes.position.y = lerp(eyes.position.y,0.0,delta*lerp_speed)
eyes.position.x = lerp(eyes.position.x,0.0,delta*lerp_speed)
# handle head tilting
if Input.is_action_pressed("left") && sprinting:
camera_3d.rotation.y = lerp(camera_3d.rotation.y,deg_to_rad(2),delta*lerp_speed_slow)
camera_3d.rotation.z = lerp(camera_3d.rotation.z,deg_to_rad(2),delta*lerp_speed_slow)
elif Input.is_action_pressed("right") && sprinting:
camera_3d.rotation.y = lerp(camera_3d.rotation.y,deg_to_rad(-2),delta*lerp_speed_slow)
camera_3d.rotation.z = lerp(camera_3d.rotation.z,deg_to_rad(-2),delta*lerp_speed_slow)
elif Input.is_action_pressed("left") && super_sprinting:
camera_3d.rotation.y = lerp(camera_3d.rotation.y,deg_to_rad(4),delta*lerp_speed_slow)
camera_3d.rotation.z = lerp(camera_3d.rotation.z,deg_to_rad(4),delta*lerp_speed_slow)
elif Input.is_action_pressed("right") && super_sprinting:
camera_3d.rotation.y = lerp(camera_3d.rotation.y,deg_to_rad(-4),delta*lerp_speed_slow)
camera_3d.rotation.z = lerp(camera_3d.rotation.z,deg_to_rad(-4),delta*lerp_speed_slow)
else:
camera_3d.rotation.y = lerp(camera_3d.rotation.y,0.0,delta*lerp_speed_slow)
camera_3d.rotation.z = lerp(camera_3d.rotation.z,0.0,delta*lerp_speed_slow)
# Add the gravity.
if Input.is_action_pressed("backward") && !is_on_floor():
velocity += get_gravity() * delta * 3
elif !is_on_floor():
velocity += get_gravity() * delta
# handle slide jump
if Input.is_action_just_pressed("jump") && is_on_floor() && sliding:
velocity.y = slide_jump_velocity
sliding = false
player_animation.play("slideJump")
super_sprinting = true
jump_sfx.play()
# Handle jump.
elif Input.is_action_just_pressed("jump") && is_on_floor() && !crouching:
velocity.y = jump_velocity
player_animation.play("jump")
jump_sfx.play()
# handle landing
if is_on_floor():
if last_velocity.y < 0.0:
player_animation.play("land")
land_sfx.play()
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
if is_on_floor():
direction = lerp(direction,(transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized(),delta * lerp_speed)
else:
direction = lerp(direction,(transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized(),delta * lerp_speed_air)
if sliding:
direction = (transform.basis * Vector3(slide_vector.x,0,slide_vector.y)).normalized()
head.position.y = lerp(head.position.y,crouching_depth,delta*lerp_speed)
current_speed = (slide_timer + 0.5) * slide_speed
if direction:
velocity.x = direction.x * current_speed
velocity.z = direction.z * current_speed
else:
velocity.x = move_toward(velocity.x, 0, current_speed)
velocity.z = move_toward(velocity.z, 0, current_speed)
last_velocity = velocity
move_and_slide()