139 lines
4.3 KiB
GDScript
139 lines
4.3 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
|
|
|
|
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
|
|
|
|
|
|
# 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
|
|
|
|
# Movement and rotation
|
|
if Input.is_action_pressed("move_up"):
|
|
move_acceleration.z -= move_accel
|
|
if Input.is_action_pressed("move_down"):
|
|
move_acceleration.z += move_accel
|
|
if Input.is_action_pressed("move_left"):
|
|
rotate(transform.basis.y, delta * rotate_speed)
|
|
if Input.is_action_pressed("move_right"):
|
|
rotate(transform.basis.y, -delta * rotate_speed)
|
|
|
|
# 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
|
|
|
|
# Apply movement to position
|
|
velocity = move_and_slide(velocity, -gravity_acceleration.normalized(), true)
|
|
|
|
# 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)
|