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) # Either do velocity-based movement or force-based movement if Input.is_action_pressed("movement_modifier"): move_velocity = transform.basis * Vector3.FORWARD * 15.0 * input.x else: move_acceleration += transform.basis * Vector3.FORWARD * move_accel * input.x rotate(transform.basis.y, delta * rotate_speed * -input.y) # Make movement local move_acceleration = 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 # Add and subtract the move_velocity (used for velocity-based movement) because we do want to apply it, but not remember it for next frame velocity = move_and_slide(velocity + move_velocity) - move_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)