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.
124 lines
3.7 KiB
124 lines
3.7 KiB
2 years ago
|
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()
|