phs-galaxy/Player.gd
2021-06-24 13:34:25 +02:00

163 lines
5.1 KiB
GDScript

extends KinematicBody
const MAX_VEL = 500.0
var acceleration := Vector3.ZERO
var velocity := Vector3.ZERO
export var move_accel = 60.0
export var rotate_speed = 2.0
export var drag = 0.05
# Jumping
var jumping := false
var time_since_jump_start := 0.0
export var initial_jump_burst = 10.0
export var jump_exponent = 0.05
export var almost_on_ground_length = 1.0
var current_target_velocity := Vector3.ZERO
var has_inherited_velocity := false
export(NodePath) var solar_system
func _ready():
$GroundCheckRay.cast_to = Vector3.DOWN * almost_on_ground_length
func _input(event):
if event.is_action_pressed("jump") and is_on_ground():
jumping = true
time_since_jump_start = 0.0
elif event.is_action_released("jump"):
jumping = false
func get_center():
return global_transform.origin + global_transform.basis.y
func apply_acceleration(acceleration):
# First drag, then add the new acceleration
# For drag: Lerp towards the target velocity
# This is usually 0, unless we're on something that's moving, in which case it is that object's
# velocity
velocity = lerp(velocity, current_target_velocity, drag)
velocity += acceleration
func get_gravity_acceleration():
var total_gravity = get_node(solar_system).get_gravity_acceleration(transform.origin)
# If we're (almost) on the ground, accelerate only towards the planet we're on the ground of.
# Otherwise, get the total gravity of the solar system.
if $GroundCheckRay.is_colliding():
# Lerp between the planet's own gravity and the
var planet_gravity = get_node(solar_system).get_closest_gravity_acceleration(transform.origin)
var distance_to_collision = ($GroundCheckRay.get_collision_point()
- $GroundCheckRay.global_transform.origin).length()
# This factor is 0.0 if the player is exactly on the ground, and 1.0 if they're just barely
# almost grounded
var factor = inverse_lerp(0.0, almost_on_ground_length, distance_to_collision)
return lerp(planet_gravity, total_gravity, factor)
else:
# If the player is not grounded: return the whole acceleration caused by the entire solar
# system.
return total_gravity
# Returns true if the player is currently (almost) on the ground.
# Surfaces with an angle of 45° or less are considered a ground.
func is_on_ground():
if $GroundCheckRay.is_colliding():
var normal = $GroundCheckRay.get_collision_normal()
# An angle of >45° to the local up vector counts as grounded
if normal.dot(global_transform.basis.y) > 0.5:
return true
return false
# Returns true if the player is (almost) on the ground and that ground is a moving platform.
func is_on_moving_platform():
return is_on_ground() and $GroundCheckRay.get_collider().is_in_group("MovingPlatform")
# Set the current target velocity to the moving platform that is currently below the player.
# If this was the first collision frame, also inherit the platform's velocity.
func apply_moving_platform_velocity():
if not has_inherited_velocity:
velocity += $GroundCheckRay.get_collider().velocity
has_inherited_velocity = true
current_target_velocity = $GroundCheckRay.get_collider().velocity
# Reset the current target velocity and other variables relevant for moving platforms.
func reset_moving_platform_velocity():
current_target_velocity = Vector3.ZERO
has_inherited_velocity = false
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta):
var move_velocity := Vector3.ZERO
var move_acceleration := Vector3.ZERO
# Apply input
var input = DeadzoneInput.get_input("move", 0.65, 0.2, 0.0, false)
print(input)
move_acceleration.z += input.x * -move_accel
rotate(transform.basis.y, delta * rotate_speed * -input.y)
# Make movement local
move_acceleration = transform.basis * move_acceleration
# Get acceleration caused by gravity
var gravity_acceleration = get_gravity_acceleration()
# Apply both acceleration types
apply_acceleration((move_acceleration + gravity_acceleration) * delta)
# Handle jumping
if jumping:
# Continuously apply an impulse by adding velocity: a lot at first, then less until it's 0
# Use max() to avoid NaN from being applied once no more impulse should be added
var e_section = max(
exp(log(initial_jump_burst - 1 / jump_exponent * time_since_jump_start)),
0.0
)
velocity += -gravity_acceleration.normalized() * e_section
time_since_jump_start += delta
# Check for moving platforms and lerp towards that
if is_on_moving_platform():
apply_moving_platform_velocity()
else:
reset_moving_platform_velocity()
# Apply movement to position
velocity = move_and_slide(velocity)
# Clamp the velocity just to be save
velocity.x = clamp(velocity.x, -MAX_VEL, MAX_VEL)
velocity.y = clamp(velocity.y, -MAX_VEL, MAX_VEL)
velocity.z = clamp(velocity.z, -MAX_VEL, MAX_VEL)
# Rotate down vector to face center of gravity
var down = gravity_acceleration
var local_down = transform.basis * Vector3.DOWN
var angle = local_down.angle_to(down)
var axis = local_down.cross(down).normalized()
# An axis of 0 happens if we're perfectly aligned already (local_down and down are equal)
if axis != Vector3.ZERO:
rotate(axis, angle)