Hi,
Starting learning Godot and GDScript (new to programming actually) recently.
My controller is almost finished, but I can't seem to able to handle prone, however I try.
I once somehow managed to make it work, but couldn't make it togglable (was using "is_action_just_presssed()" and "prone = !prone", it was instantly switching back to walk mode after pressing Ctrl) and as I was messing with it, I somehow broke the whole prone.
So please help, I like something stable, even when i add new stuff, also a way for both crouch & prone be togglable would be cool. This is the script so far, I may also have messed up a few stuff while doing prone, too tired to check them now.
(Also don't mind the crappy comments in the script. Just tried to make it a way to understand what each line is for if I ever forget)
extends CharacterBody3D
# Player nodes
@onready var neck = $neck # Sets a variant for player/neck
@onready var head = $neck/head # Sets a variant for player/neck/head
@onready var eyes = $neck/head/eyes
@onready var cam_player = $neck/head/eyes/camera # Sets a variant for player/neck/head/camera
@onready var stand_collider = $"stand-collsion" # Sets a variant for player/stand-collsion
@onready var crouch_collider = $"crouch-collsion" # Sets a variant for player/crouch-collsion
@onready var prone_collider = $"prone-collsion"
@onready var slide_collider = $"slide-collsion"
@onready var ray_player_up = $RayCast3D # Sets a var for player/RayCast3d
@onready var step_cast = $ShapeCast;
var current_speed = 5.0 # Refrence speed in the code (Change by each action)
@export_group("Settings") # Group a quick access setting section in "lab/player - Inspector"
@export_subgroup("Speeds") # Speed subgroup for a quick access to tweak the each speed of each action
@export var walk_speed = 5.0 # Creates an option to quick set the speed for walking
@export var sprint_speed = 8.0 # Creates an option to quick set the speed for sprinting
@export var crouch_speed = 3.0 # Creates an option to quick set the speed for crouching
@export var prone_speed = 1.5
@export_subgroup("Walk mode logic")
@export var simple_collision: bool = false
@export var dynamic_collision: bool = false
const jump_velocity = 4.5 # Constant value for the jump velocity
const base_sens = 0.35 # Constant value for the mouse sensitivity
var lerp_speed = 10.0 # Value for the lerp strenght
var direction = Vector3.ZERO # Sets the directions based on empty vector3
var crouch_depth = -0.5 # Sets the depth to determine how low the head goes while crouching
var prone_depth = -1.8
var slide_depth = -0.8
var free_look_tilt_amount = 8
var grounded = false
# States
var walk = false
var sprint = false
var crouch = false
var prone = false
var slide = false
var free_look = false
@export_subgroup("Head Bobbing")
@export var head_bob_speed_sprint = 22.0
@export var head_bob_speed_walk = 14.0
@export var head_bob_speed_crouch = 10.0
@export var head_bob_speed_prone = 8.0
@export var head_bob_intens_sprint = 0.2
@export var head_bob_intens_walk = 0.1
@export var head_bob_intens_crouch = 0.05
@export var haed_bob_intense_prone = 2.5
var head_bob_vector = Vector2.ZERO
var head_bob_index = 0.0
var head_bob_intens_current = 0.0
var slide_timer = 0.0
var slide_timer_max = 1.0
var slide_vector = Vector2.ZERO
var slide_speed = 10.0
func _ready(): # Start point for the ready function
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _input(event): # Section to set Inpue Events
if event is InputEventMouseMotion:
if free_look:
neck.rotate_y(deg_to_rad(-event.relative.x * base_sens))
neck.rotation.y = clamp(neck.rotation.y,deg_to_rad(-75),deg_to_rad(75))
else:
rotate_y(deg_to_rad(-event.relative.x * base_sens))
head.rotate_x(deg_to_rad(-event.relative.y * base_sens))
head.rotation.x = clamp(head.rotation.x,deg_to_rad(-75),deg_to_rad(65))
func _physics_process(delta: float) -> void: # Function of the most important element, physics process
# Remember to set inputs in the project settings
var input_dir := Input.get_vector("left", "right", "forward", "backward")
# Checks if the crouch input is being pressed or not
if Input.is_action_pressed("crouch") || slide:
current_speed = crouch_speed # sets the currnt speed to crouch speed when crouch is pressed
if slide:
head.position.y = lerp(head.position.y, slide_depth,delta*lerp_speed)
stand_collider.disabled = true
crouch_collider.disabled = true
slide_collider.disabled = false
prone_collider.disabled = true
else:
head.position.y = lerp(head.position.y, crouch_depth,delta*lerp_speed)
stand_collider.disabled = true
crouch_collider.disabled = false
slide_collider.disabled = true
prone_collider.disabled = true
# System to change colliders - does crouch
if sprint && input_dir != Vector2.ZERO:
slide = true
slide_timer = slide_timer_max
slide_vector = input_dir
free_look = true
print("slide started")
walk = false
sprint = false
crouch = true
prone = false
elif !ray_player_up.is_colliding():
# Rest of the crouch collider system
crouch_collider.disabled = true
stand_collider.disabled = false
slide_collider.disabled = true
prone_collider.disabled = true
head.position.y = lerp(head.position.y,0.0,delta*lerp_speed)
# Putting sprint under crouch is crucial to slow player down, also prevent conflict between the two speeds
if Input.is_action_pressed("sprint"): # Checks for the sprint input
current_speed = sprint_speed # Sets current speed to the sprint speed while sprinting
walk = false
sprint = true
crouch = false
prone = false
else: # When sprint is released and not pressed sets cureent speed to walk speed
current_speed = walk_speed
walk = true
sprint = false
crouch = false
prone = false
# Handle free-looking
if Input.is_action_pressed("freelook") || slide:
free_look = true
if slide:
cam_player.rotation.z = lerp(cam_player.rotation.z,-deg_to_rad(5.0),delta*lerp_speed)
else:
cam_player.rotation.z = -deg_to_rad(neck.rotation.y*free_look_tilt_amount)
else:
free_look = false
neck.rotation.y = lerp(neck.rotation.y,0.0,delta*lerp_speed)
cam_player.rotation.z = lerp(cam_player.rotation.z,0.0,delta*lerp_speed)
if slide:
slide_timer -= delta
if slide_timer <= 0:
slide = false
free_look = false
print("slide ended")
if sprint:
head_bob_intens_current = head_bob_intens_sprint
head_bob_index += head_bob_speed_sprint*delta
elif walk:
head_bob_intens_current = head_bob_intens_walk
head_bob_index += head_bob_speed_walk*delta
elif crouch:
head_bob_intens_current = head_bob_intens_crouch
head_bob_index += head_bob_speed_crouch*delta
if is_grounded() && !slide && input_dir != Vector2.ZERO:
head_bob_vector.y = sin(head_bob_index)
head_bob_vector.x = sin(head_bob_index/2)+0.5
eyes.position.y = lerp(eyes.position.y,head_bob_vector.y*(head_bob_intens_current/2.0),delta*lerp_speed)
eyes.position.x = lerp(eyes.position.x,head_bob_vector.x*head_bob_intens_current,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)
# Add the gravity.
if not is_grounded():
velocity += get_gravity() * delta # Takes gravity and multiapply it by delta, to constantly handle gravity
# Handle jump.
if Input.is_action_just_pressed("jump") and is_grounded():
velocity.y = jump_velocity
slide = false
# Get the input direction and handle the movement/deceleration.
direction = lerp(direction,(transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized(),delta*lerp_speed)
if slide:
direction = (transform.basis * Vector3(slide_vector.x,0,slide_vector.y)).normalized()
# Handles the speeds for moving toward each directions
if direction:
if is_grounded():
velocity.x = direction.x * current_speed
velocity.z = direction.z * current_speed
else :
velocity.x = lerp(velocity.x, direction.x * current_speed, delta * 2.0)
velocity.z = lerp(velocity.z, direction.z * current_speed, delta * 2.0)
if slide:
velocity.x = direction.x * (slide_timer + 0.25) * slide_speed
velocity.z = direction.z * (slide_timer + 0.25) * slide_speed
else:
velocity.x = move_toward(velocity.x, 0, current_speed)
velocity.z = move_toward(velocity.z, 0, current_speed)
move(delta)
func move(delta: float):
step_cast.global_position.x = global_position.x + velocity.x * delta
step_cast.global_position.z = global_position.z + velocity.z * delta
if is_grounded():
step_cast.target_position.y = -1.0
else:
step_cast.target_position.y = -0.45
var query = PhysicsShapeQueryParameters3D.new()
query.exclude = [self]
query.shape = step_cast.shape
query.transform = step_cast.global_transform
var result = get_world_3d().direct_space_state.intersect_shape(query, 1)
if !result:
step_cast.force_shapecast_update()
if step_cast.is_colliding() && velocity.y <= 0.0 && !result:
global_position.y = lerp(global_position.y, step_cast.get_collision_point(0).y, delta*lerp_speed)
velocity.y = lerp(velocity.y,0.0,delta*lerp_speed)
grounded = true
else:
grounded = false
move_and_slide()
func is_grounded() -> bool:
return grounded || is_on_floor()
Thanks.