How can I effectively resolve the issue of jump and jump animation being out of sync?

Godot Version

Godot Engine v4.5.dev4.official.209a446e3

Question

I implemented basic jump functionality for the player using the following code, but the jump and the animation are not well synchronized. How can I fix this issue? I suspect the problem is caused by a 0.36-second crouch anticipation phase at the beginning of the jump animation. This crouch makes the jump feel more dynamic, so I’d prefer not to modify the animation itself. Given this, is there any effective way to resolve the mismatch?

...
@export var skin_body_height: float = 1.85 # 身体高度
@export var skin_jump_height: float = 1.60 # 跳跃高度
@export var skin_jump_time_to_peak: float = 0.4 # 跳跃达到峰值时间
@export var skin_jump_time_to_descent: float = 0.3 # 跳跃下降时间
@onready var skin_jump_velocity: float = ((skin_body_height * skin_jump_height) / skin_jump_time_to_peak) * -1.0 # 跳跃速度
@onready var skin_jump_gravity: float = ((-skin_body_height * skin_jump_height) / (skin_jump_time_to_peak * skin_jump_time_to_peak)) * -1.0 # 跳跃重力
@onready var skin_jump_fall_gravity: float = ((-skin_body_height * skin_jump_height) / (skin_jump_time_to_descent * skin_jump_time_to_descent)) * -1.0 # 下落重力

func jump_logic(_delta: float) -> void:
	# 如果在地面时
	if is_on_floor():
		# 如果触发了跳跃输入事件
		if Input.is_action_just_pressed("jump"):
			set_move_state("ground_stand_start_f_jump_lfoot")
			velocity.y = -player_body_skin.skin_jump_velocity
	else:
		set_move_state("ground_stand_start_f_jump_lfoot")
	var jump_gravity = player_body_skin.skin_jump_gravity if velocity.y > 0.0 else player_body_skin.skin_jump_fall_gravity
	velocity.y -= jump_gravity * _delta

I’m assuming this is being called from _process() or _physics_process().

A few things:

  • Usually you prefix a function argument with underscore (like _delta) if you aren’t referencing the argument in the function; it tells Godot not to issue a warning. If you are actually using the argument, you might want to take the underscore off (ie: delta).
  • Where are you determining when/how to call jump_logic()? I don’t see what happens here when the jump ends.
  • You don’t have any animation code here, but you’ve asked about animation…
  • Some advice I got a long time ago: For enemies, transition animations (stand…crouch…jump, for example) makes the game look better. For the player, it makes the input laggy and the game feel unresponsive. Give your opponents full, fluid animations, but give the player instant transitions.

All that said, if you’ve got a 0.35s crouch animation for the player and you want to keep that, then what you want for logic is probably something more like:

# pseudocode

enum JumpState { not_jumping, crouch, leap, fall }

var jump_state: JumpState = JumpState.not_jumping

func jump_logic(delta: float) -> void:
    match(jump_state):
        JumpState.not_jumping:
            if Input.is_action_just_pressed("jump"):
                play_crouch_anim()
                jump_state = JumpState.crouch

        JumpState.crouch:
            if crouch_anim_is_done():
                play_air_anim()
                jump_gravity = UP_GRAV
                jump_state = JumpState.leap

        JumpState.leap:
            if at_jump_apex():
                jump_gravity = DOWN_GRAV
                jump_state = JumpState.fall

        JumpState.fall:
            if is_on_ground():
                play_landed_anim()
                jump_state = JumpState.not_jumping