Checking if player is moving using is_zero_approx() help

Godot Version

Godot-4

Question

I am having trouble when I need to go from Walk to Idle state. Currently I am checking zero approx however, the player stops moving and the walk animation continues going for a few seconds before switching to idle. This happens because is_zero_approx has not evaluated to true and eventually does but many frames after stopping so the walk animation keeps playing as seen in the video below.

animation_state_issue
This is how I am currently transitioning between the two states:

if Global.player.velocity.is_zero_approx():
    transition.emit("IdlePlayerState")

# Use some epsilon value for transitioning to walking state ( this has some issues )
if Global.player.velocity.length() > 0.00001 and Global.player.is_on_floor():
    transition.emit("WalkingPlayerState")

This is my entire player controller code:

func _physics_process(delta):

# Add the gravity.
if not is_on_floor():
    velocity.y -= gravity * delta

# Handle jump.
if Input.is_action_just_pressed("jump"):
    print("Jumping")

    isJumping = true
    velocity.y = JUMP_VELOCITY
    if !jump_anim.is_playing():
        jump_anim.play("jump_up")

    if Input.is_action_pressed("sprint"):
        camera.transform.origin = _headbob(t_bob, SPRINT_BOB_FREQ)
        speed = SPRINT_SPEED
        print("We are now sprinting")
    else:
        speed = WALK_SPEED


# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
var input_dir = Input.get_vector("left", "right", "up", "down")
var direction = (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()

if(is_on_floor()):
    if isJumping:
    jump_anim.play(("jump_down"))
     isJumping = false
if direction:
    velocity.x = direction.x * speed
    velocity.z = direction.z * speed
else:
    velocity.x = lerp(velocity.x, direction.x * speed, ACCELERATION)
    velocity.z = lerp(velocity.z, direction.z * speed, ACCELERATION)
else:
    velocity.x = move_toward(velocity.x, 0, DECELERATION)
    velocity.z = move_toward(velocity.z, 0, DECELERATION)

t_bob += delta * velocity.length() * float(is_on_floor())

camera.transform.origin = _headbob(t_bob, WALK_BOB_FREQ)

var velocity_clamped = clamp(velocity.length(), 0.5, SPRINT_SPEED * 2)
var target_fov = BASE_FOV + FOV_CHANGE * velocity_clamped
camera.fov = lerp(camera.fov, target_fov, delta * 8.0)

if Input.is_action_pressed("shoot"):
    print("shooting")
    _shoot()

move_and_slide()

Would appreciate any help or guidance on how I can handle something like this. Thanks in advance.

Can you print out the actual length of the velocity? Maybe instead of using is_zero_approx you need to just check if it’s less than an arbitrary small number.

1 Like

Thanks for the reply!
So when I print the state and the velocity vector, this is what I am getting. It remains in walking state even when the number is incredibly small and close to zero, only to switch to Idle eventually:
This is my debug print of state and velocity

State: WalkingPlayerState player velocity: (-0.000004, 0, -0.000013)
State: WalkingPlayerState player velocity: (-0.000004, 0, -0.000011)
State: WalkingPlayerState player velocity: (-0.000004, 0, -0.000011)
State: WalkingPlayerState player velocity: (-0.000004, 0, -0.000011)
State: WalkingPlayerState player velocity: (-0.000003, 0, -0.00001)
State: WalkingPlayerState player velocity: (-0.000003, 0, -0.00001)
State: IdlePlayerState player velocity: (-0.000003, 0, -0.000009)
State: IdlePlayerState player velocity: (-0.000003, 0, -0.000009)