While those are important for finetuning, this list completely forgot to include two absolute essentials that really define the feel:
the values and ratio of launch force and gravity - this is the main thing that’ll define how the jump feels. Low force and low gravity will make it floaty while high force and high gravity results in something very springy.
The three you listed are the right base. What makes the jump-to-walk transition feel robotic in your snippet is velocity.x = input_dir * SPEED: horizontal speed snaps instantly, in the air and on the ground. Here is a small set that covers it:
const SPEED := 300.0
const ACCEL := 1800.0 # ground
const AIR_ACCEL := 1100.0 # lower in air = more momentum
const FRICTION := 2200.0
const JUMP_V := -520.0
const GRAV := 1500.0
const FALL_MULT := 1.5 # fall faster than you rise
const CUT := 0.45 # let go early = short hop
const APEX_V := 80.0
const APEX_MULT := 0.5 # half gravity near the top = hang time
const COYOTE := 0.1
const BUFFER := 0.12
var coyote := 0.0
var buf := 0.0
func _physics_process(d: float) -> void:
var dir := Input.get_axis("player_2_left", "player_2_right")
coyote = COYOTE if is_on_floor() else maxf(coyote - d, 0.0)
buf = BUFFER if Input.is_action_just_pressed("jump") else maxf(buf - d, 0.0)
var g := GRAV
if velocity.y > 0.0: g *= FALL_MULT
if absf(velocity.y) < APEX_V and not is_on_floor(): g *= APEX_MULT
velocity.y += g * d
if buf > 0.0 and coyote > 0.0:
velocity.y = JUMP_V
buf = 0.0
coyote = 0.0
if Input.is_action_just_released("jump") and velocity.y < JUMP_V * 0.3:
velocity.y *= CUT
var a := ACCEL if is_on_floor() else AIR_ACCEL
if dir != 0.0:
velocity.x = move_toward(velocity.x, dir * SPEED, a * d)
else:
velocity.x = move_toward(velocity.x, 0.0, (FRICTION if is_on_floor() else AIR_ACCEL * 0.5) * d)
move_and_slide()
What each piece does: the move_toward lines replace the snap with acceleration (this is the “robotic” fix); apex hang time and the faster fall give the arc a weight curve without needing a Curve resource; the cut multiplier gives variable height; coyote and buffer forgive timing. For the Hollow Knight style wall bounce, add a short “no input” lock (about 0.15 s) after a wall jump so the push-off velocity is not instantly overwritten by held input.
I ran the timing logic (coyote window, an expired-window negative control, buffered landing, tap vs held height, eased air speed) as a headless test in Godot 4.7 and all checks passed. I did not play-test the feel, so treat the numbers as a starting point and tune them. Written with AI assistance and checked by running it.