Skip to content

Instantly share code, notes, and snippets.

@SteveSwink
Created March 8, 2013 22:51
Show Gist options
  • Save SteveSwink/5120612 to your computer and use it in GitHub Desktop.
Save SteveSwink/5120612 to your computer and use it in GitHub Desktop.
Statemachine C# for unity using mad delegatessss
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/* State Template
*
// IDLE STATE
bool enterIDLE ()
{
return true;
}
bool updateIDLE ()
{
float x = Input.GetAxisRaw("Horizontal");
if(x != 0)
stateMachine.ChangeState(enterWALK, updateWALK, exitWALK);
return false;
}
bool exitIDLE ()
{
return true;
}
*/
// Defines the required parameters and return value type for a StateMachine state.
// Returns a bool representing whether or not the state has finished running.
public delegate bool StateDelegate ();
// To use, create an instance of StateMachine inside of a MonoBehaviour, load it up with
// references to state methods with ChangeState(), then call its Execute() method during the
// MonoBehaviour's Update cycle. An example MonoBehaviour is included at the bottom of this file.
public class StateMachine
{
// Keep track of the currently running state
enum MState {
None,
Entering,
Updating,
Exiting
}
MState mState = MState.None;
List<State> stateList = new List<State>();
// Hashtable<int, State> stateList = new HashSet
// IDictionary<int, State> stateList = new IDictionary<int, State>();
// These states will be cached when calling ChangeState(). They'll be copied into
// currentStateMethod in succession as each state finishes running. The Execute()
// method will execute currentStateMethod on each update, running whatever method
// is stored there.
StateDelegate enter;
StateDelegate update;
StateDelegate exit;
// After being called by the Execute() method, the current state will be replaced with
// the next most appropriate state if the current state returns 'true', signifying that
// it has finished running.
StateDelegate currentStateMethod;
public void AddState(State newState){
stateList.Insert(newState.key, newState);
}
// A single state may be stored at any given time. If you need to queue more than
// one state, you could conceivably replace ChangeState() with AddState() and append
// the three state parameters to a list. As its currently written, changing the state
// immediatley calls the current 'exit' state, then overwrites the cached enter/run/exit
// states with these new states.
public void ChangeState (StateDelegate enter, StateDelegate update, StateDelegate exit)
{
bool isStateCurrentlyRunning = currentStateMethod != null;
// If a state is currently running, it should be allowed to gracefully exit
// before the next state takes over
if (isStateCurrentlyRunning) {
SwitchCurrentState (MState.Exiting);
}
// Cache the given state values
this.enter = enter;
this.update = update;
this.exit = exit;
// If a state isn't currently running, we can immediately switch to our entering
// state using the state delegates we cached a few lines above
if (!isStateCurrentlyRunning) {
SwitchCurrentState (MState.Entering);
}
}
public void ChangeState(State state){
bool isStateCurrentlyRunning = currentStateMethod != null;
if (isStateCurrentlyRunning) {
SwitchCurrentState (MState.Exiting);
}
this.enter = state.enter;
this.update = state.update;
this.exit = state.exit;
if (!isStateCurrentlyRunning) {
SwitchCurrentState (MState.Entering);
}
}
// Call this during
public void Execute ()
{
if (currentStateMethod == null)
return;
// Execute the current state method
bool finished = currentStateMethod ();
// If we've reached the end of the current enter/run/exit, advance to the next one
if (finished) {
switch (mState) {
case MState.None:
SwitchCurrentState (MState.Entering);
break;
case MState.Entering:
SwitchCurrentState (MState.Updating);
enter = null;
break;
case MState.Updating:
SwitchCurrentState (MState.Exiting);
update = null;
break;
case MState.Exiting:
// If an Enter behavior exists, it must have been added by ChangeState. We should
// start running again from the top instead of coming to a halt.
if (enter != null) {
SwitchCurrentState (MState.Entering);
} else {
SwitchCurrentState (MState.None);
exit = null;
}
break;
}
}
}
// Utility method for performing the state delegate swapping logic based on an enum value
void SwitchCurrentState (MState state)
{
this.mState = state;
switch (mState) {
case MState.None:
currentStateMethod = null;
break;
case MState.Entering:
currentStateMethod = this.enter;
break;
case MState.Exiting:
currentStateMethod = this.exit;
break;
case MState.Updating:
currentStateMethod = this.update;
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment