A simple state machine class for Unity. Use it to run regular methods or coroutines as states.
using UnityEngine; | |
using System; | |
using System.Collections; | |
/// <summary> | |
/// A simple state machine class. Use it to run regular methods or coroutines as states. | |
/// Steps to use: | |
/// 1. Define a private field for the StateMachine object. | |
/// private StateMachine stateMachine; | |
/// 2. Initialize the object in Awake. Pass the MonoBehaviour object to the constructor. | |
/// stateMachine = new StateMachine(this); | |
/// 3. Use SetNextState to pass either a void method or a coroutine as a state to the statemachine. | |
/// The state will run in a loop until SetNextSate is called. | |
/// The current state will come to an end, and the next state will be started in a loop afterwards. | |
/// </summary> | |
public class StateMachine | |
{ | |
private MonoBehaviour owner; | |
private Action nextState; | |
private Func<IEnumerator> nextCState; | |
private bool nextStateIsCoroutine; | |
private bool running = false; | |
public bool isInThisState { get; private set; } | |
#region Constructors | |
public StateMachine(MonoBehaviour owner) | |
{ | |
if (!owner) | |
{ | |
throw new ArgumentException("You need to assign a MonoBehaviour to this StateMachine!"); | |
} | |
this.owner = owner; | |
} | |
#endregion | |
#region State Setters and Helper Methods | |
public void SetNextState(Action state) | |
{ | |
nextState = state; | |
nextCState = null; | |
nextStateIsCoroutine = false; | |
StateSet(); | |
} | |
public void SetNextState(Func<IEnumerator> state) | |
{ | |
nextCState = state; | |
nextState = null; | |
nextStateIsCoroutine = true; | |
StateSet(); | |
} | |
private void StateSet() | |
{ | |
isInThisState = false; | |
if (!running && HasNextState()) | |
{ | |
owner.StartCoroutine(Run()); | |
} | |
} | |
private bool HasNextState() | |
{ | |
return nextStateIsCoroutine ? (nextCState != null) : (nextState != null); | |
} | |
#endregion | |
private IEnumerator Run() | |
{ | |
running = true; | |
while (HasNextState()) | |
{ | |
if (!owner) | |
{ | |
Debug.LogError("The MonoBehaviour assigned to this StateMachine has been destroyed!"); | |
running = false; | |
yield break; | |
} | |
isInThisState = true; | |
if (nextStateIsCoroutine) | |
{ | |
yield return owner.StartCoroutine(nextCState()); | |
} | |
else | |
{ | |
nextState(); | |
yield return null; | |
} | |
} | |
running = false; | |
isInThisState = false; | |
} | |
} |
using UnityEngine; | |
using System.Collections; | |
/// <summary> | |
/// Example class for StateMachine.cs. | |
/// When in the idle state, pressing space triggers the fly state, which lasts for two seconds before returning to the idle state. | |
/// </summary> | |
public class StateMachineDemo : MonoBehaviour | |
{ | |
private StateMachine stateMachine; | |
private void Awake() | |
{ | |
stateMachine = new StateMachine(this); | |
} | |
private void Start() | |
{ | |
stateMachine.SetNextState(IdleState); | |
} | |
private void IdleState() | |
{ | |
if(Input.GetKeyDown(KeyCode.Space)) | |
{ | |
stateMachine.SetNextState(FlyState); | |
} | |
} | |
private IEnumerator FlyState() | |
{ | |
// TODO Start upwards Movement | |
yield return new WaitForSeconds(2); | |
// TODO Stop upwards movement | |
stateMachine.SetNextState(IdleState); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment