extends KinematicBody const MAX_VEL = 500.0 var acceleration := Vector3(0.0, -9.81, 0.0) var velocity := Vector3(0.0, 0.0, 0.0) var move_accel = 60.0 var rotate_speed = 2.0 var drag = 0.05 # Jumping var jumping := false var time_since_jump_start := 0.0 var initial_jump_burst = 10.0 var jump_exponent = 0.05 var current_target_velocity := Vector3.ZERO export(NodePath) var solar_system 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(): # 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(): return get_node(solar_system).get_closest_gravity_acceleration(transform.origin) else: return get_node(solar_system).get_gravity_acceleration(transform.origin) # 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)