Standard Finite State Machines (FSMs) work great for simple logic, but as your player character grows, you run into “State Explosion.” HSMs solve this by allowing states to exist inside other states, inheriting logic from their parents.
In a platformer, if you want to allow “Attacking” while “Jumping,” “Falling,” or “Running,” you suddenly need states like JumpAttack, FallAttack, and RunAttack. This leads to a messy web of duplicate logic and complex transitions.
In an HSM, you define a parent state like Airborne.
Airborne (Handles gravity, air-drifting, and looking for ground).Jumping, Falling, Gliding.If the player is in the Jumping state, they are also in the Airborne state. They automatically execute the Airborne logic first, then their specific Jumping logic.
public abstract class State {
protected State parent;
public virtual void Enter() {}
public virtual void Update() {
// Execute parent logic first (up the hierarchy)
parent?.Update();
}
public virtual void Exit() {}
}
public class AirborneState : State {
public override void Update() {
ApplyGravity(); // Shared logic for all airborne children
if (IsGrounded()) TransitionTo(GroundedState);
}
}
public class JumpingState : AirborneState {
public override void Update() {
base.Update(); // Runs Gravity from AirborneState
if (velocity.y < 0) TransitionTo(FallingState);
}
}
LedgeHang) is trivial and inherits the safety of the parent.Falling or Jumping) the player was in.HSMs turn a “Spaghetti” FSM into a clean, logical tree. They are the backbone of professional character controllers in games like Assassin’s Creed and God of War.