Godot Version: 4.4
Hello everyone, I’m new to Godot and gamedev in general and right when I found I was very close to understanding AnimationStateMachines, I found some huge trouble.
I’ll attach a screenshot of my AnimationTree and do my best to explain what’s happening.
My goal: The player has two attack animations and it always starts with attack1, but if another attack is queued before the recovery animation ends, it will use the attack2 animation instead
When I left click I set a variable called is_attacking to true. This causes the StateMachine to switch from idle to attack1.
At the end of the attack1 animation, an animation track calls a method that sets is_attacking to false. attack1 always progresses unconditionally to attack_recovery_buffer, and since is_attacking is now false, the player can click again to set it back to true.
attack_recovery_buffer always progresses unconditionally to attack_recovery; from here it progresses immediately to attack2 if is_attacking == true (priority 0), otherwise it progresses to idle (priority 1).
From here the tree behaves the exact same as the previous part, except after recovery it goes to attack1 (instead of attack2) if the player attacked again.
Now here’s my issue: despite the fact that is_attacking seems to follow the intended state flow, my AnimationTree is not behaving as expected. When it reaches attack_recovery it goes to idle and then immediately to attack1, staying in a loop forever, indicating that is_attacking is still true. However, if is_attacking was true, attack_recovery should have progressed to attack2.
If I put the condition !is_attacking on the transition between attack_recovery and idle, the animation gets stuck on the last frame of attack_recovery, indicating that it can’t progress to attack2 (which requires is_attacking to be true) nor to idle (which requires is_attacking to be false, in this case). Given boolean variables only have two possible values, I guess this is a problem with how/when the StateMachine evaluates switch conditions, but I can’t for the life of me figure out what the problem is precisely.
This is the entirety of the code that handles my player movement:
extends Node2D
@export var player: CharacterBody2D
@export var speed: float = 8.0
@export var jump_power: float = 12
var speed_multiplier: float = 30.0
var jump_multiplier: float = -30.0
var direction: float = 0.0
var friction: float = 3.0
var is_attacking: bool = false
func _input(event: InputEvent) -> void:
if event.is_action_pressed('attack'):
is_attacking = true
elif event.is_action_pressed('jump') and player.is_on_floor():
player.velocity.y = jump_power * jump_multiplier
func _physics_process(delta: float) -> void:
if not player.is_on_floor():
player.velocity += player.get_gravity() * delta
direction = Input.get_axis("move_left", "move_right")
if direction:
player.velocity.x = direction * speed * speed_multiplier
else:
player.velocity.x = move_toward(player.velocity.x, 0, speed * friction)
player.move_and_slide()
func set_weapon_collision() -> void:
# collision logic
print('collision set')
func unset_weapon_collision() -> void:
# collision logic
print('collision unset')
func stop_attacking() -> void:
is_attacking = false
unset_weapon_collision()
The method called at the end of attack1/attack2 is stop_attacking. I tried putting the call anywhere, but the result doesn’t change.
Thanks in advance.