Skip to content

leventeren/State-Manager

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐ŸŽฎ Unity Generic State Machine System

A flexible, modular, and easy-to-use state machine system for Unity that works with any enum-based state structure.

โœจ Features

  • ๐Ÿ”„ Fully Generic - Works with any enum type
  • ๐ŸŽฏ Type-Safe - Compile-time type checking for states
  • ๐Ÿ”— Fluent API - Chain methods for easy setup
  • โšก Three Callback Types - OnEnter, OnExit, and OnUpdate
  • ๐Ÿ“ก Event System - Subscribe to state transitions
  • ๐Ÿงฉ Modular Design - Reusable across any game object or system
  • ๐Ÿ› ๏ธ Unity Integration - Seamless MonoBehaviour integration
  • ๐Ÿงช Test Tools Included - Ready-to-use testing utilities

๐Ÿ“ฆ Installation

  1. Copy the following files to your Unity project:

    • StateMachine.cs โ†’ Core state machine logic
    • CharacterStateManager.cs โ†’ Example implementation
    • CharacterStateTester.cs โ†’ Testing utility
  2. Place them in your project's scripts folder (e.g., Assets/_Project/Scripts/)

๐Ÿš€ Quick Start

1. Define Your States

public enum CharacterState
{
    Idle,
    Walking,
    Running,
    Jumping
}

2. Create Your State Manager

using UnityEngine;

public class CharacterController : MonoBehaviour
{
    private StateMachine<CharacterState> stateMachine;

    private void Awake()
    {
        // Initialize with default state
        stateMachine = new StateMachine<CharacterState>(CharacterState.Idle);

        // Register states with callbacks
        stateMachine.RegisterState(CharacterState.Idle)
            .OnEnter(() => Debug.Log("Character is now idle"))
            .OnExit(() => Debug.Log("Character stopped being idle"))
            .OnUpdate(() => CheckForInput());

        stateMachine.RegisterState(CharacterState.Walking)
            .OnEnter(() => StartWalkAnimation())
            .OnUpdate(() => MoveCharacter(walkSpeed));

        stateMachine.RegisterState(CharacterState.Running)
            .OnEnter(() => StartRunAnimation())
            .OnUpdate(() => MoveCharacter(runSpeed));
    }

    private void Update()
    {
        // Update current state
        stateMachine.UpdateState();
    }

    // Change state from anywhere
    public void StartWalking()
    {
        stateMachine.ChangeState(CharacterState.Walking);
    }
}

3. Use State Queries

// Check single state
if (stateMachine.IsInState(CharacterState.Idle))
{
    // Do something
}

// Check multiple states
if (stateMachine.IsInAnyState(CharacterState.Walking, CharacterState.Running))
{
    // Character is moving
}

// Access current state
CharacterState current = stateMachine.CurrentState;
CharacterState previous = stateMachine.PreviousState;

๐Ÿ“š API Reference

StateMachine

Constructor

StateMachine<TState>(TState initialState)

Properties

  • CurrentState - Get the current active state
  • PreviousState - Get the last active state

Methods

  • RegisterState(TState state) - Register a state and return a StateNode for configuration
  • ChangeState(TState newState, bool force = false) - Transition to a new state
  • UpdateState() - Update the current state (call in Update/FixedUpdate)
  • IsInState(TState state) - Check if currently in a specific state
  • IsInAnyState(params TState[] states) - Check if in any of the provided states

Events

  • OnStateChanged - Triggered when state changes (provides previous and new state)

StateNode

Fluent API methods for configuring state callbacks:

RegisterState(MyState.Example)
    .OnEnter(() => { /* Called once when entering state */ })
    .OnExit(() => { /* Called once when exiting state */ })
    .OnUpdate(() => { /* Called every frame while in state */ });

๐ŸŽฏ Usage Examples

Example 1: AI Enemy States

public enum EnemyState { Idle, Patrol, Chase, Attack, Dead }

public class EnemyAI : MonoBehaviour
{
    private StateMachine<EnemyState> ai;
    private Transform player;

    private void Awake()
    {
        ai = new StateMachine<EnemyState>(EnemyState.Idle);

        ai.RegisterState(EnemyState.Patrol)
            .OnEnter(() => SetRandomPatrolPoint())
            .OnUpdate(() => PatrolBehavior());

        ai.RegisterState(EnemyState.Chase)
            .OnEnter(() => audioSource.Play(chaseSound))
            .OnUpdate(() => ChasePlayer())
            .OnExit(() => audioSource.Stop());

        ai.RegisterState(EnemyState.Attack)
            .OnEnter(() => animator.SetTrigger("Attack"))
            .OnUpdate(() => AttackBehavior());
    }

    private void Update()
    {
        ai.UpdateState();

        // State transition logic
        if (ai.IsInState(EnemyState.Patrol) && PlayerInRange())
        {
            ai.ChangeState(EnemyState.Chase);
        }
    }
}

Example 2: UI Menu System

public enum MenuState { MainMenu, Settings, Gameplay, Paused }

public class MenuManager : MonoBehaviour
{
    private StateMachine<MenuState> menu;

    private void Awake()
    {
        menu = new StateMachine<MenuState>(MenuState.MainMenu);

        menu.RegisterState(MenuState.MainMenu)
            .OnEnter(() => ShowPanel(mainMenuPanel))
            .OnExit(() => HidePanel(mainMenuPanel));

        menu.RegisterState(MenuState.Settings)
            .OnEnter(() => ShowPanel(settingsPanel))
            .OnExit(() => HidePanel(settingsPanel));

        menu.RegisterState(MenuState.Gameplay)
            .OnEnter(() => Time.timeScale = 1f)
            .OnUpdate(() => CheckForPause());

        menu.RegisterState(MenuState.Paused)
            .OnEnter(() => {
                Time.timeScale = 0f;
                ShowPanel(pausePanel);
            })
            .OnExit(() => Time.timeScale = 1f);
    }
}

Example 3: Vehicle System

public enum VehicleState { Parked, Driving, Crashed, Repairing }

public class VehicleController : MonoBehaviour
{
    private StateMachine<VehicleState> vehicle;

    private void Awake()
    {
        vehicle = new StateMachine<VehicleState>(VehicleState.Parked);

        vehicle.OnStateChanged += (from, to) => 
        {
            Debug.Log($"Vehicle: {from} โ†’ {to}");
        };

        vehicle.RegisterState(VehicleState.Driving)
            .OnEnter(() => StartEngine())
            .OnUpdate(() => HandleDriving())
            .OnExit(() => StopEngine());

        vehicle.RegisterState(VehicleState.Crashed)
            .OnEnter(() => {
                DisableControls();
                SpawnSmokeEffect();
            });
    }
}

๐Ÿงช Testing

The system includes a comprehensive testing utility:

  1. Add CharacterStateTester component to your GameObject
  2. Reference your state machine manager
  3. Use multiple test methods:

Testing Methods

  • ๐ŸŽฎ In-Game UI - Click buttons during play mode
  • โŒจ๏ธ Keyboard Shortcuts - 1-4 for states, Space to cycle
  • ๐Ÿ” Inspector Context Menu - Right-click component for manual controls
  • โฐ Auto Test Mode - Enable automatic state cycling

Keyboard Shortcuts

1 - Go to first state
2 - Go to second state
3 - Go to third state
4 - Go to fourth state
Space - Cycle through states

๐Ÿ—๏ธ Architecture

StateMachine<TState>
โ”œโ”€โ”€ StateNode (per state)
โ”‚   โ”œโ”€โ”€ OnEnter (Action)
โ”‚   โ”œโ”€โ”€ OnExit (Action)
โ”‚   โ””โ”€โ”€ OnUpdate (Action)
โ”œโ”€โ”€ CurrentState (TState)
โ”œโ”€โ”€ PreviousState (TState)
โ””โ”€โ”€ OnStateChanged (Event)

๐Ÿ’ก Best Practices

  1. Initialize in Awake - Set up state machine before other components need it
  2. Call UpdateState() - Don't forget to call this in Update/FixedUpdate
  3. Use Events - Subscribe to OnStateChanged for global state tracking
  4. Validate Transitions - Check current state before changing
  5. Clean Callbacks - Keep OnEnter/OnExit/OnUpdate methods focused and small

โš ๏ธ Important Notes

  • State changes during OnEnter or OnExit callbacks are supported but use carefully
  • UpdateState() must be called manually in your Update loop
  • Changing to the same state does nothing (unless force = true)
  • All callbacks are optional - register only what you need

๐Ÿค Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

๐Ÿ“„ License

This project is available under the MIT License.

๐Ÿ™ Credits

Designed for modular and scalable Unity game development.


๐Ÿ“ž Support

If you encounter any issues or have questions, please open an issue on GitHub.

Made with โค๏ธ for the Unity community

About

Basic Unity State Manager

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages