Skip to content

Instantly share code, notes, and snippets.

@Riordan-DC
Created September 20, 2023 03:31
Show Gist options
  • Save Riordan-DC/c2e174af71339a58c56f6430afdd3934 to your computer and use it in GitHub Desktop.
Save Riordan-DC/c2e174af71339a58c56f6430afdd3934 to your computer and use it in GitHub Desktop.
Precision Platformer Controller for Godot 3.x (coyote time + prejump)
class_name Actor
extends KinematicBody2D
# Input
var pressing_jump = false
var pressing_attack = false
var input_direction = Vector2.ZERO
# Movement constants
export var jump_height = 100
export var jump_time_to_peak = 0.5
export var jump_time_to_descent = 0.4
onready var jump_velocity = ((2.0 * jump_height) / jump_time_to_peak) * -1.0
onready var jump_gravity = ((-2.0 * jump_height) / (jump_time_to_peak * jump_time_to_peak)) * -1.0
onready var fall_gravity = ((-2.0 * jump_height) / (jump_time_to_descent * jump_time_to_descent)) * -1.0
var acceleration = 1.8
var acceleration_air = 0.8
var max_floor_angle = deg2rad(60.0)
var floor_drag = 20.0
var air_drag = 1.0
var max_landspeed = 300
var max_airspeed = 600
var coyote_time = 0.1
var prejump_time = 0.1
# Movement variables
var velocity = Vector2.ZERO
var friction = floor_drag
var coyote_timer = 0.0
var prejump_timer = 0.0
var prev_on_floor = false
enum MoveState {
IDLE,
RUN,
PIVOT,
AIR
}
const MoveStateNames = ["IDLE", "RUN", "PIVOT", "AIR"]
var move_state = MoveState.IDLE
func _ready():
pass
func _process(_delta):
update_input()
func get_gravity():
return jump_gravity if (velocity.y < 0.0 and Input.is_action_pressed("move_up")) else fall_gravity
func _physics_process(delta):
velocity.y += get_gravity() * delta
if not is_on_floor():
coyote_timer -= delta
if prev_on_floor: # Just fallen
coyote_timer = coyote_time
if is_on_floor() or coyote_timer > 0.0:
if pressing_jump:
velocity.y = jump_velocity
if abs(input_direction.x) == 0:
if is_on_floor():
friction = floor_drag
velocity.x = lerp(velocity.x, 0, friction * delta)
else:
if is_on_floor():
velocity.x = lerp(velocity.x, max_landspeed * input_direction.x, acceleration * delta)
else:
velocity.x = lerp(velocity.x, max_landspeed * input_direction.x, acceleration_air * delta)
if is_on_floor():
if not pressing_jump:
velocity.y = clamp(velocity.y, -max_landspeed, max_landspeed)
velocity.x = clamp(velocity.x, -max_landspeed, max_landspeed)
else:
velocity.y = clamp(velocity.y, -max_airspeed, max_airspeed)
velocity.x = clamp(velocity.x, -max_landspeed, max_landspeed)
if prejump_timer <= 0:
pressing_jump = false
else:
prejump_timer -= delta
prev_on_floor = is_on_floor()
velocity = move_and_slide(velocity, Vector2.UP, true, 4, max_floor_angle, false)
if abs(velocity.x) < 1.0:
velocity.x = 0
velocity = velocity.snapped(Vector2(1,1))
# Stop on slope
if is_on_floor():
if get_floor_angle(Vector2(0,-1)) != 0:
if input_direction.x == 0:
velocity.x = 0
update_move_state()
GUI.DebugUI.label(get_instance_id(), "velocity: %s" % [velocity])
func _input(_event):
input_direction = Input.get_vector(
"move_left",
"move_right",
"move_down",
"move_up")
pressing_jump = Input.is_action_just_pressed("move_up")
if pressing_jump:
prejump_timer = prejump_time
pressing_attack = Input.is_action_pressed("attack")
func update_input():
pass
func update_move_state():
if velocity.length() > 0.1:
# Moving
if is_on_floor():
move_state = MoveState.RUN
if is_zero_approx(get_floor_angle(Vector2(0,-1))):
if input_direction.x > 0 and velocity.x < 0:
move_state = MoveState.PIVOT
elif input_direction.x < 0 and velocity.x > 0:
move_state = MoveState.PIVOT
else:
move_state = MoveState.AIR
else:
move_state = MoveState.IDLE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment