Last active
August 29, 2015 14:16
-
-
Save tiagosr/9b04d183d4c099e5eb1b to your computer and use it in GitHub Desktop.
Unity Event/Delegate-based State Machine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityEngine.Events; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class State { | |
public delegate void EnterStateEvent(State s, State from); | |
public delegate void ExitStateEvent(State s, State to); | |
public delegate void UpdateStateEvent(State s); | |
public delegate void Constructor(State s); | |
public event EnterStateEvent onEnter, onResume; | |
public event ExitStateEvent onExit, onSuspend; | |
public event UpdateStateEvent onUpdate; | |
public State(Constructor c) { | |
c(this); | |
} | |
public void Push(State state) { currentMachine.PushState(state); } | |
public void Pop() { currentMachine.PopState(); } | |
public void SwitchTo(State state) { currentMachine.SwitchState(state); } | |
internal void Enter(State s) { if(onEnter != null) onEnter(this, s); } | |
internal void Resume(State s) { if(onResume != null) onResume(this, s); } | |
internal void Exit(State s) { if(onExit != null) onExit(this, s); } | |
internal void Suspend(State s) { if(onSuspend != null) onSuspend(this, s); } | |
internal void Update() { if(onUpdate != null) onUpdate(this); } | |
internal StateMachine currentMachine; | |
} | |
public class StateMachine { | |
public Stack<State> states = new Stack<State>(); | |
public StateMachine(State s) { | |
s.currentMachine = this; | |
states.Push(s); | |
} | |
public void PushState(State state) { | |
//Debug.Log("push! "+states.Count + " "+ states.Peek()); | |
State current = states.Peek(); | |
state.currentMachine = this; | |
states.Push(state); | |
if(current != null) current.Suspend(state); | |
state.Enter(current); | |
} | |
public void PopState() { | |
//Debug.Log("pop! "+states.Count + " "+ states.Peek()); | |
State exited = states.Pop(); | |
State s = states.Peek(); | |
if(exited != null) exited.Exit(s); | |
if(s != null) { | |
s.currentMachine = this; | |
s.Resume(exited); | |
} | |
} | |
public void SwitchState(State state) { | |
//Debug.Log("switch! "+states.Count + " "+ states.Peek()); | |
State exited = states.Pop(); | |
states.Push(state); | |
state.currentMachine = this; | |
if(exited != null) exited.Exit(state); | |
state.Enter(exited); | |
} | |
public void Update() { | |
State s = states.Peek(); | |
if(s != null) s.Update(); | |
else Debug.LogError("no state!"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Usage example */ | |
using UnityEngine; | |
using System.Collections; | |
public class ExampleStateMachineBehaviour: MonoBehaviour { | |
public string status; | |
public StateMachine sm; | |
void Start() { | |
// declare variables that will be accessed from within a delegate with an initial value, | |
// or the .NET compiler will "optimize" them away | |
State start_state = null, idle = null, jumping = null; | |
start_state = new State((st) => { | |
float timer = 5f; // will stay 5 seconds in this state. | |
st.onEnter += (s, from) => { | |
status = "start state"; | |
}; | |
st.onUpdate += (s) => { | |
timer -= Time.deltaTime; | |
if(timer <= 0) { | |
s.SwitchTo(idle); | |
} | |
}; | |
}); | |
idle = new State((st) => { | |
var enter = (s, from) => { | |
status = "idle"; | |
}; | |
st.onEnter += enter; | |
st.onResume += enter; | |
st.onUpdate += (s) => { | |
if(Input.GetKeyDown(KeyCode.Space)) { | |
s.Push(jumping); // thunk into the jumping state | |
} | |
} | |
}); | |
jumping = new State((st) => { | |
float y = 0; | |
float yspeed = 0; | |
float yaccel = -1; | |
st.onEnter += (s, from) => { | |
status = "jumping"; | |
// reset yspeed every time it enters the state | |
yspeed = 10; | |
}; | |
st.onUpdate += (s) => { | |
y += yspeed * Time.deltaTime; | |
yspeed += yaccel * Time.deltaTime; | |
status = "jumping: y="+y; | |
if(y < 0) { | |
s.Pop(); // go back to idle (or whoever pushed this state in) | |
} | |
}; | |
}); | |
sm = new StateMachine(start_state); | |
} | |
void Update() { | |
sm.Update(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment