You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
3.7 KiB
123 lines
3.7 KiB
class_name Player |
|
extends CharacterBody3D |
|
|
|
@export var camera: Camera |
|
|
|
@export_group("Movement") |
|
@export var move_accel := 6.0 |
|
@export var move_max_speed := 4.0 |
|
@export var friction_floor := 12.0 |
|
@export var friction_air := 2.0 |
|
@export var gravity := -12.0 |
|
|
|
@export_group("Jumping") |
|
@export var jump_velocity := 5.0 |
|
@export var jump_early_time := 0.0 # Time (in seconds) after pressing the jump button a jump may occur late. |
|
@export var jump_coyote_time := 0.0 # Time (in seconds) after leaving a jumpable surface when a jump may still occur. |
|
|
|
enum MovementModeEnum { DEFAULT, FLYING, NO_CLIP } |
|
var movement_mode := MovementModeEnum.DEFAULT |
|
|
|
var is_moving := false |
|
var is_sprinting := false |
|
|
|
var time_since_jump_pressed := INF |
|
var time_since_on_floor := INF |
|
|
|
|
|
func _input(event: InputEvent) -> void: |
|
# Inputs that are valid when the game is focused. |
|
# =============================================== |
|
|
|
if event.is_action("move_sprint"): |
|
is_sprinting = event.is_pressed() |
|
get_viewport().set_input_as_handled() |
|
|
|
if event.is_action_pressed("move_jump"): |
|
time_since_jump_pressed = 0 |
|
get_viewport().set_input_as_handled() |
|
|
|
# Cycle movement mode between default, flying and noclip. |
|
if event.is_action_pressed("cycle_movement_mode"): |
|
if (+movement_mode > MovementModeEnum.NO_CLIP): |
|
movement_mode = MovementModeEnum.DEFAULT; |
|
get_viewport().set_input_as_handled() |
|
|
|
# Inputs that are valid only when the mouse is captured. |
|
# ====================================================== |
|
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: |
|
pass |
|
|
|
|
|
func _physics_process(delta: float) -> void: |
|
match movement_mode: |
|
MovementModeEnum.DEFAULT: |
|
process_movement_default(delta) |
|
MovementModeEnum.FLYING: |
|
process_movement_flying(delta, false) |
|
MovementModeEnum.NO_CLIP: |
|
process_movement_flying(delta, true) |
|
|
|
|
|
func process_movement_default(delta: float) -> void: |
|
var input := Input.get_vector("move_strafe_left", "move_strafe_right", "move_forward", "move_backward") |
|
|
|
velocity.y += gravity * delta |
|
var hor_vel := Vector3(velocity.x, 0, velocity.z) |
|
|
|
var target := basis.rotated(Vector3.UP, camera.current_yaw) * Vector3(input.x, 0, input.y) * move_max_speed |
|
is_moving = target.dot(hor_vel) > 0 |
|
|
|
var accel: float |
|
if is_moving: accel = move_accel |
|
elif is_on_floor(): accel = friction_floor |
|
else: accel = friction_air |
|
|
|
if is_sprinting: |
|
target *= 5 |
|
accel *= 5 |
|
|
|
hor_vel = hor_vel.lerp(target, accel * delta) |
|
velocity = Vector3(hor_vel.x, velocity.y, hor_vel.z) |
|
|
|
# TODO: Check if this still applies. |
|
# Sometimes, when pushing into a wall, jumping wasn't working. |
|
# Possibly due to `IsOnFloor` returning `false` for some reason. |
|
# The `JumpEarlyTime` feature seems to avoid this issue, thankfully. |
|
|
|
if is_on_floor(): time_since_on_floor = 0 |
|
else: time_since_on_floor += delta |
|
|
|
if time_since_jump_pressed <= jump_early_time && time_since_on_floor <= jump_coyote_time: |
|
velocity.y = jump_velocity |
|
time_since_jump_pressed = INF |
|
time_since_on_floor = INF |
|
|
|
move_and_slide() |
|
|
|
func process_movement_flying(delta: float, no_clip: bool) -> void: |
|
var input := Vector3( |
|
Input.get_axis("move_strafe_left", "move_strafe_right"), |
|
Input.get_axis("move_downward", "move_upward"), |
|
Input.get_axis("move_forward", "move_backward")) |
|
|
|
velocity *= 1 - friction_air * delta; |
|
|
|
var target := camera.global_transform.basis.get_rotation_quaternion() * input * move_max_speed |
|
is_moving = target.dot(velocity) > 0 |
|
|
|
var accel: float |
|
if is_moving: accel = move_accel |
|
else: accel = friction_air |
|
|
|
target *= 4 |
|
accel *= 4 |
|
|
|
if is_sprinting: |
|
target *= 5 |
|
accel *= 5 |
|
|
|
velocity = velocity.lerp(target, accel * delta) |
|
|
|
if no_clip: translate(velocity * delta) |
|
else: move_and_slide()
|
|
|