Godot Version
4.7
Question
So the normal 2D movement Godot already has is cool but it doesn’t really feel good. Usually in games there’s acceleration, jump time(based on how long button is pressed), and other stuff. How can I make better movement?
Well basic movement template is just that - a basic template to get you started. It’s up to you to build upon that, code and finetune all of the specifics of your movement system. The movement feel can vary greatly between games. It just would be impractical and in fact impossible to have a complicated built in template that caters to every finesse a movement can have. That’s what scripting is for.
This is incredibly vague.
What is “better”?
What kind of game are you making?
“Better” will heavily depend on the type of game you’re trying to create, and even then, what feels perfect for one person might feel awful to another.
Do you have a specific problem you’re trying to solve?
If it’s a platformer, these are the pieces I’d add on top of the default template.
move_toward on velocity.x covers it.velocity.y by something like 0.5.The other thing I’d do is define the jump as a height in pixels and a time to the peak in seconds and derive gravity from those, rather than picking gravity and jump velocity by feel. Tuning in numbers you can actually see on screen is a lot easier.
Here’s an example script with all of that in it (you will need to customize it to your state machine of course):
extends CharacterBody2D
@export var jump_height := 64.0 # pixels
@export var jump_time_to_peak := 0.4 # seconds going up
@export var jump_time_to_fall := 0.3 # seconds coming down
@export var jump_release_cut := 0.5 # velocity kept if you let go early
@export var coyote_time := 0.1
@export var jump_buffer_time := 0.12
@export var max_fall_speed := 700.0
@export var apex_threshold := 40.0 # below this counts as being at the apex
@export var apex_gravity_scale := 0.5
@export var max_speed := 200.0
@export var ground_accel_time := 0.08
@export var ground_stop_time := 0.06
@export var air_accel_time := 0.15
@export var air_stop_time := 0.25
@export var turn_multiplier := 2.0 # extra grip when reversing
var jump_velocity: float
var jump_gravity: float
var fall_gravity: float
var _coyote_timer := 0.0
var _buffer_timer := 0.0
var _jumping := false
func _ready() -> void:
jump_velocity = -2.0 * jump_height / jump_time_to_peak
jump_gravity = 2.0 * jump_height / (jump_time_to_peak * jump_time_to_peak)
fall_gravity = 2.0 * jump_height / (jump_time_to_fall * jump_time_to_fall)
func _physics_process(delta: float) -> void:
var on_floor := is_on_floor()
if on_floor:
_coyote_timer = coyote_time
_jumping = false
else:
_coyote_timer -= delta
if Input.is_action_just_pressed("jump"):
_buffer_timer = jump_buffer_time
else:
_buffer_timer -= delta
if _buffer_timer > 0.0 and _coyote_timer > 0.0:
velocity.y = jump_velocity
_buffer_timer = 0.0
_coyote_timer = 0.0
_jumping = true
if _jumping and Input.is_action_just_released("jump") and velocity.y < 0.0:
velocity.y *= jump_release_cut
if not on_floor:
var g := jump_gravity if velocity.y < 0.0 else fall_gravity
if absf(velocity.y) < apex_threshold:
g *= apex_gravity_scale
velocity.y = minf(velocity.y + g * delta, max_fall_speed)
var dir := Input.get_axis("move_left", "move_right")
var accel_time: float
if is_zero_approx(dir):
accel_time = ground_stop_time if on_floor else air_stop_time
else:
accel_time = ground_accel_time if on_floor else air_accel_time
if not is_zero_approx(velocity.x) and signf(dir) != signf(velocity.x):
accel_time /= turn_multiplier
velocity.x = move_toward(velocity.x, dir * max_speed, (max_speed / accel_time) * delta)
move_and_slide()
It expects move_left, move_right and jump in your input map, or you can swap those for the ui_ actions to try it right away. Everything is exported so you can tune it while the game is running.
Wow thanks