open-fpsz/characters/player/player.gd
2024-04-07 14:25:59 -04:00

81 lines
2.6 KiB
GDScript

extends RigidBody3D
# constants
const max_energy : float = 100.0
const energy_charge_rate : float = 10 # energy per second
const energy_drain_rate : float = 20 # energy per second
const ground_speed : float = 48 / 3.6 # m/s
const jetpack_vertical_force : float = 800
const jetpack_horizontal_force : float = 400
const jetpack_force_factor : float = 2
# parameters
@export var max_floor_angle : float = 60
@export var energy: float = 100.0
var g : float = ProjectSettings.get_setting("physics/3d/default_gravity") # in m/s²
var gravity : Vector3 = g * ProjectSettings.get_setting("physics/3d/default_gravity_vector")
# floor detection
@onready var hud = $HUD
@onready var shape_cast = $ShapeCast3D
@onready var weapon = $SpringArm3D/Inventory/SpaceGun
# signals
signal energy_changed(energy)
func _ready():
energy_changed.connect(hud._on_energy_changed)
func is_on_floor():
return shape_cast.is_colliding()
func is_skiing() -> bool:
return Input.is_action_pressed("ski")
func handle_jetpack(delta, direction):
if Input.is_action_pressed("jump_and_jet"):
if energy > 0:
var up_vector = Vector3.UP * jetpack_vertical_force * jetpack_force_factor
var side_vector = direction * jetpack_horizontal_force * jetpack_force_factor
apply_force(up_vector + side_vector)
energy -= energy_drain_rate * delta
else:
energy += energy_charge_rate * delta
energy = clamp(energy, 0, max_energy)
energy_changed.emit(energy)
func _physics_process(delta):
# retrieve user's direction vector
var _input_dir = Input.get_vector("left", "right", "forward", "backward")
# compute direction in local space
var _direction = (transform.basis * Vector3(_input_dir.x, 0, _input_dir.y)).normalized()
# adjust direction based on spring arm rotation
_direction = _direction.rotated(Vector3.UP, $SpringArm3D.rotation.y)
handle_jetpack(delta, _direction)
# handle ski
if is_skiing():
physics_material_override.friction = 0
else:
physics_material_override.friction = 1
if is_on_floor():
if not _direction.is_zero_approx() and not is_skiing():
# retrieve collision normal
var normal = shape_cast.get_collision_normal(0)
# calculate the angle between the ground normal and the up vector
var slope_angle = rad_to_deg(acos(normal.dot(Vector3.UP)))
# check if the slope angle exceeds the maximum slope angle
if slope_angle <= max_floor_angle:
# adjust direction based on the floor normal to align with the slope
_direction = _direction.slide(normal)
linear_velocity = lerp(linear_velocity, _direction * ground_speed, .1)
if Input.is_action_just_pressed("jump_and_jet"):
linear_velocity.y = sqrt(2 * abs((mass * gravity * delta).y) * 1)
else:
pass