What I learned building an enemy state machine in Godot 4
I wrote "just use a match statement, it's fine" three times before I stopped saying it. It is fine, right up until an enemy needs a fourth state and two of the transitions start depending on each other. Here is what actually cost time building enemy AI for a wave-based game, in the order it bit me. Lesson 1: the match statement is fine until state 4 A two-state enemy — chase, attack — is genuinely not worth a framework: func _physics_process ( delta : float ) -> void : match state : State . CHASE : velocity = ( player . global_position - global_position ) . normalized () * speed if global_position . distance_to ( player . global_position ) < attack_range : state = State . ATTACK State . ATTACK : attack_timer -= delta if attack_timer <= 0.0 : do_attack () state = State . CHASE The moment a third and fourth state show up — hurt, dead, stagger, windup — the match block stops being one enemy's logic and becomes a grid of every state times every other state it might transition to. That grid is where the bugs live, not in any single state. Lesson 2: the bug is never inside a state, it's in the transition Every state-machine bug I actually spent time on was the same shape: state A left some flag or timer set that state C didn't know to check. An enemy stuck mid-attack-animation forever, still receiving hits, was not a bug in the attack state — it was the hurt state interrupting attack without cleaning up attack_timer or resetting the animation. The fix that made these bugs findable is giving every state an explicit enter and exit , and never mutating another state's data directly: func change_state ( new_state : State ) -> void : if new_state == state : return _exit_state ( state ) state = new_state _enter_state ( new_state ) func _exit_state ( s : State ) -> void : match s : State . ATTACK : attack_timer = 0.0 sprite . stop () func _enter_state ( s : State ) -> void : match s : State . HURT : velocity = Vector2 . ZERO hurt_timer = HURT_DURATION sprite . play ( "hurt" ) On