# State Machine

> Generic state machine patterns — IState interface, StateMachine<T>, game state management (menu/gameplay/pause), enemy AI states, hierarchical FSM. Load when implementing state-driven behavior.

- **Type:** Skill
- **Install:** `agentstack add skill-xeldaralz-everything-claude-unity-state-machine`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [XeldarAlz](https://agentstack.voostack.com/s/xeldaralz)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [XeldarAlz](https://github.com/XeldarAlz)
- **Source:** https://github.com/XeldarAlz/everything-claude-unity/tree/main/.claude/skills/gameplay/state-machine
- **Website:** https://github.com/XeldarAlz/everything-claude-unity#readme

## Install

```sh
agentstack add skill-xeldaralz-everything-claude-unity-state-machine
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# State Machine Patterns

A generic, reusable finite state machine for Unity. Covers player states, enemy AI, game flow (menu/gameplay/pause), hierarchical FSMs, and ScriptableObject-driven states for designer configuration.

## IState Interface

The contract every state must fulfill. Keep it minimal: enter, exit, tick, and physics tick.

```csharp
public interface IState
{
    /// Called once when entering this state.
    void Enter();

    /// Called once when leaving this state.
    void Exit();

    /// Called every frame while this state is active.
    void Update();

    /// Called every fixed timestep while this state is active.
    void FixedUpdate();
}
```

If your game does not need `FixedUpdate` in states (e.g., turn-based game), drop it from the interface. Keep the interface as lean as your project requires.

---

## StateMachine Generic Class

A generic state machine that can be used with any state type. The type parameter `T` is typically the owner (player, enemy, game manager) so states can access it.

```csharp
using System;
using System.Collections.Generic;
using UnityEngine;

public class StateMachine
{
    public IState CurrentState { get; private set; }
    public IState PreviousState { get; private set; }

    private T _owner;
    private Dictionary _states = new();

    public StateMachine(T owner)
    {
        _owner = owner;
    }

    /// 
    /// Register a state instance. Call during initialization.
    /// 
    public void AddState(IState state)
    {
        _states[state.GetType()] = state;
    }

    /// 
    /// Transition to a new state by type. Calls Exit on current, Enter on new.
    /// 
    public void ChangeState() where TState : IState
    {
        var type = typeof(TState);

        if (!_states.TryGetValue(type, out var newState))
        {
            Debug.LogError($"State {type.Name} not registered in state machine.");
            return;
        }

        if (CurrentState == newState) return; // Already in this state

        PreviousState = CurrentState;
        CurrentState?.Exit();
        CurrentState = newState;
        CurrentState.Enter();
    }

    /// 
    /// Change state by instance (useful when states are not unique by type,
    /// e.g., ScriptableObject states).
    /// 
    public void ChangeState(IState newState)
    {
        if (newState == null || CurrentState == newState) return;

        PreviousState = CurrentState;
        CurrentState?.Exit();
        CurrentState = newState;
        CurrentState.Enter();
    }

    /// 
    /// Return to the previous state.
    /// 
    public void RevertToPreviousState()
    {
        if (PreviousState != null)
            ChangeState(PreviousState);
    }

    /// 
    /// Call from the owner's Update().
    /// 
    public void Update()
    {
        CurrentState?.Update();
    }

    /// 
    /// Call from the owner's FixedUpdate().
    /// 
    public void FixedUpdate()
    {
        CurrentState?.FixedUpdate();
    }

    /// 
    /// Check if the current state is of a given type.
    /// 
    public bool IsInState() where TState : IState
    {
        return CurrentState is TState;
    }

    public T Owner => _owner;
}
```

---

## Player State Example

A concrete example: player states for a 2D platformer. Each state is a class that holds a reference to the player controller.

### Base Player State

```csharp
public abstract class PlayerState : IState
{
    protected PlayerController Player { get; }
    protected StateMachine StateMachine { get; }

    protected PlayerState(PlayerController player, StateMachine stateMachine)
    {
        Player = player;
        StateMachine = stateMachine;
    }

    public virtual void Enter() { }
    public virtual void Exit() { }
    public virtual void Update() { }
    public virtual void FixedUpdate() { }
}
```

### Idle State

```csharp
public class PlayerIdleState : PlayerState
{
    public PlayerIdleState(PlayerController player, StateMachine sm)
        : base(player, sm) { }

    public override void Enter()
    {
        Player.Animator.Play("Idle");
        Player.Rb.velocity = new Vector2(0f, Player.Rb.velocity.y);
    }

    public override void Update()
    {
        if (!Player.IsGrounded)
        {
            StateMachine.ChangeState();
            return;
        }

        if (Player.JumpRequested)
        {
            StateMachine.ChangeState();
            return;
        }

        if (Mathf.Abs(Player.MoveInput.x) > 0.1f)
        {
            StateMachine.ChangeState();
            return;
        }

        if (Player.DashRequested)
        {
            StateMachine.ChangeState();
            return;
        }
    }
}
```

### Jump State

```csharp
public class PlayerJumpState : PlayerState
{
    public PlayerJumpState(PlayerController player, StateMachine sm)
        : base(player, sm) { }

    public override void Enter()
    {
        Player.Animator.Play("Jump");
        Player.ExecuteJump();
    }

    public override void Update()
    {
        // Transition to fall when velocity turns downward
        if (Player.Rb.velocity.y ();
            return;
        }

        // Variable jump height: cut velocity on release
        if (Player.JumpReleased && Player.Rb.velocity.y > 0f)
        {
            Player.CutJumpShort();
        }

        // Allow wall slide if touching wall
        if (Player.IsTouchingWall && !Player.IsGrounded)
        {
            StateMachine.ChangeState();
            return;
        }
    }

    public override void FixedUpdate()
    {
        Player.ApplyHorizontalMovement();
    }
}
```

### Wiring It Up in the Player Controller

```csharp
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // Public properties that states read
    public Rigidbody2D Rb { get; private set; }
    public Animator Animator { get; private set; }
    public Vector2 MoveInput { get; private set; }
    public bool IsGrounded { get; private set; }
    public bool IsTouchingWall { get; private set; }
    public bool JumpRequested { get; set; }
    public bool JumpReleased { get; set; }
    public bool DashRequested { get; set; }

    private StateMachine _stateMachine;

    private void Awake()
    {
        Rb = GetComponent();
        Animator = GetComponent();

        _stateMachine = new StateMachine(this);
        _stateMachine.AddState(new PlayerIdleState(this, _stateMachine));
        _stateMachine.AddState(new PlayerRunState(this, _stateMachine));
        _stateMachine.AddState(new PlayerJumpState(this, _stateMachine));
        _stateMachine.AddState(new PlayerFallState(this, _stateMachine));
        _stateMachine.AddState(new PlayerWallSlideState(this, _stateMachine));
        _stateMachine.AddState(new PlayerDashState(this, _stateMachine));

        _stateMachine.ChangeState();
    }

    private void Update()
    {
        ReadInput();
        CheckGround();
        CheckWall();
        _stateMachine.Update();
    }

    private void FixedUpdate()
    {
        _stateMachine.FixedUpdate();
    }

    // Movement methods called by states
    public void ExecuteJump() { /* see character-controller skill */ }
    public void CutJumpShort() { /* see character-controller skill */ }
    public void ApplyHorizontalMovement() { /* see character-controller skill */ }

    private void ReadInput() { /* read from Input System */ }
    private void CheckGround() { /* overlap circle check */ }
    private void CheckWall() { /* raycast check */ }
}
```

---

## Game State Management

Use the same state machine pattern for high-level game flow.

### Game States

```
MainMenu → Loading → Gameplay → Pause → GameOver
                       ↑          |
                       +----------+  (resume)
```

```csharp
public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    private StateMachine _stateMachine;

    private void Awake()
    {
        Instance = this;
        DontDestroyOnLoad(gameObject);

        _stateMachine = new StateMachine(this);
        _stateMachine.AddState(new MainMenuState(this, _stateMachine));
        _stateMachine.AddState(new LoadingState(this, _stateMachine));
        _stateMachine.AddState(new GameplayState(this, _stateMachine));
        _stateMachine.AddState(new PauseState(this, _stateMachine));
        _stateMachine.AddState(new GameOverState(this, _stateMachine));

        _stateMachine.ChangeState();
    }

    private void Update()
    {
        _stateMachine.Update();
    }

    // Convenience methods for external callers
    public void StartGame() => _stateMachine.ChangeState();
    public void PauseGame() => _stateMachine.ChangeState();
    public void ResumeGame() => _stateMachine.RevertToPreviousState();
    public void GameOver() => _stateMachine.ChangeState();
    public void ReturnToMenu() => _stateMachine.ChangeState();
}
```

### Pause State Example

```csharp
public class PauseState : IState
{
    private GameManager _owner;
    private StateMachine _sm;

    public PauseState(GameManager owner, StateMachine sm)
    {
        _owner = owner;
        _sm = sm;
    }

    public void Enter()
    {
        Time.timeScale = 0f;
        // Show pause menu UI
    }

    public void Exit()
    {
        Time.timeScale = 1f;
        // Hide pause menu UI
    }

    public void Update() { }

    // Called by InputView when the Pause action fires (UI action map while paused).
    public void OnPausePressed() => _sm.RevertToPreviousState();

    public void FixedUpdate() { }
}
```

---

## Enemy AI States

A patrol/chase/attack AI using the same state machine.

```
Idle → Patrol → Detect → Chase → Attack → Flee → Dead
```

### Patrol State

```csharp
public class EnemyPatrolState : IState
{
    private EnemyController _enemy;
    private StateMachine _sm;
    private int _waypointIndex;

    public EnemyPatrolState(EnemyController enemy, StateMachine sm)
    {
        _enemy = enemy;
        _sm = sm;
    }

    public void Enter()
    {
        _enemy.Animator.Play("Walk");
        _enemy.MoveSpeed = _enemy.PatrolSpeed;
    }

    public void Exit() { }

    public void Update()
    {
        // Check for player detection
        if (_enemy.CanSeePlayer())
        {
            _sm.ChangeState();
            return;
        }

        // Move toward current waypoint
        Vector3 target = _enemy.Waypoints[_waypointIndex].position;
        _enemy.MoveToward(target);

        // Advance to next waypoint when close enough
        if (Vector3.Distance(_enemy.transform.position, target)  _sm;
    private float _losePlayerTimer;

    public EnemyChaseState(EnemyController enemy, StateMachine sm)
    {
        _enemy = enemy;
        _sm = sm;
    }

    public void Enter()
    {
        _enemy.Animator.Play("Run");
        _enemy.MoveSpeed = _enemy.ChaseSpeed;
        _losePlayerTimer = 0f;
    }

    public void Exit() { }

    public void Update()
    {
        float distToPlayer = Vector3.Distance(
            _enemy.transform.position, _enemy.Player.position);

        // Close enough to attack
        if (distToPlayer ();
            return;
        }

        // Lost sight of player
        if (!_enemy.CanSeePlayer())
        {
            _losePlayerTimer += Time.deltaTime;
            if (_losePlayerTimer > _enemy.LosePlayerDelay)
            {
                _sm.ChangeState();
                return;
            }
        }
        else
        {
            _losePlayerTimer = 0f;
        }

        // Low health: flee
        if (_enemy.HealthPercent ();
            return;
        }

        _enemy.MoveToward(_enemy.Player.position);
    }

    public void FixedUpdate() { }
}
```

---

## Hierarchical FSM

A hierarchical state machine is a state that contains its own sub-state machine. This avoids state explosion when behaviors have nested phases.

Example: A "Combat" state that internally has Approach, Attack, and Retreat sub-states.

```csharp
public class EnemyCombatState : IState
{
    private EnemyController _enemy;
    private StateMachine _parentSM;
    private StateMachine _subSM;

    public EnemyCombatState(EnemyController enemy, StateMachine parentSM)
    {
        _enemy = enemy;
        _parentSM = parentSM;

        _subSM = new StateMachine(enemy);
        _subSM.AddState(new CombatApproachSubState(enemy, _subSM));
        _subSM.AddState(new CombatAttackSubState(enemy, _subSM));
        _subSM.AddState(new CombatRetreatSubState(enemy, _subSM));
    }

    public void Enter()
    {
        _subSM.ChangeState();
    }

    public void Exit()
    {
        _subSM.CurrentState?.Exit();
    }

    public void Update()
    {
        // Parent-level transitions (flee, death) take priority
        if (_enemy.HealthPercent ();
            return;
        }

        // Delegate to sub-state
        _subSM.Update();
    }

    public void FixedUpdate()
    {
        _subSM.FixedUpdate();
    }
}
```

This keeps the parent state machine simple (Patrol, Combat, Flee, Dead) while Combat handles its own complexity internally.

---

## Transition Conditions

For more complex state machines, define explicit transition rules rather than embedding transition logic in each state.

```csharp
using System;
using System.Collections.Generic;

public class TransitionRule
{
    public Type FromState;
    public Type ToState;
    public Func Condition;
}

public class StateMachineWithTransitions
{
    private StateMachine _inner;
    private List _transitions = new();

    public StateMachineWithTransitions(T owner)
    {
        _inner = new StateMachine(owner);
    }

    public void AddState(IState state) => _inner.AddState(state);

    public void AddTransition(Func condition)
        where TFrom : IState where TTo : IState
    {
        _transitions.Add(new TransitionRule
        {
            FromState = typeof(TFrom),
            ToState = typeof(TTo),
            Condition = condition
        });
    }

    /// 
    /// Add a transition that can fire from ANY state.
    /// 
    public void AddAnyTransition(Func condition) where TTo : IState
    {
        _transitions.Add(new TransitionRule
        {
            FromState = null, // null means "any"
            ToState = typeof(TTo),
            Condition = condition
        });
    }

    public void Update()
    {
        // Check transitions before updating current state
        foreach (var rule in _transitions)
        {
            if (rule.FromState != null &&
                _inner.CurrentState?.GetType() != rule.FromState)
                continue;

            if (rule.Condition())
            {
                // Use reflection or a lookup to change state by Type
                // (Simplified here; in production, store state instances by Type)
                break;
            }
        }

        _inner.Update();
    }
}
```

This approach centralizes transition logic and makes it easier to visualize the state graph.

---

## Event-Driven Transitions

Some transitions are triggered by external events (taking damage, picking up an item) rather than polled conditions.

```csharp
// In the state machine owner:
public void OnDamageTaken(int amount)
{
    if (amount > _staggerThreshold)
    {
        _stateMachine.ChangeState();
    }
}

public void OnHealthDepleted()
{
    _stateMachine.ChangeState();
}
```

Prefer event-driven transitions for one-shot triggers (damage, death, interaction). Use polled transitions for continuous conditions (grounded check, distance to enemy).

---

## ScriptableObject States

For designer-configurable behavior, define state parameters in ScriptableObjects. The SO holds data; a runtime state class reads it.

```csharp
[CreateAssetMenu(menuName = "AI/Patrol Config")]
public class PatrolStateConfig : ScriptableObject
{
    public float moveSpeed = 2f;
    public float waitTimeAtWaypoint = 1.5f;
    public float detectionRange = 8f;
    public string animationName = "Walk";
}

public class ConfigurablePatrolState

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [XeldarAlz](https://github.com/XeldarAlz)
- **Source:** [XeldarAlz/everything-claude-unity](https://github.com/XeldarAlz/everything-claude-unity)
- **License:** MIT
- **Homepage:** https://github.com/XeldarAlz/everything-claude-unity#readme

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-xeldaralz-everything-claude-unity-state-machine
- Seller: https://agentstack.voostack.com/s/xeldaralz
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
