I'm currently refactoring an in-game AI into a Finite State Machine based on this example, which I chose for its readable and highly visual definition format:
class FiniteStateMachine
{
public enum States { Start, Standby, On };
public States State { get; set; }
public enum Events { PlugIn, TurnOn, TurnOff, RemovePower };
private Action[,] fsm;
public FiniteStateMachine()
{
this.fsm = new Action[3, 4] {
PlugIn, TurnOn, TurnOff, RemovePower
{this.PowerOn, null, null, null}, //start
{null, this.StandbyWhenOff, null, this.PowerOff}, //standby
{null, null, this.StandbyWhenOn, this.PowerOff} }; //on
}
public void ProcessEvent(Events theEvent)
{
this.fsm[(int)this.State, (int)theEvent].Invoke();
}
private void PowerOn() { this.State = States.Standby; }
private void PowerOff() { this.State = States.Start; }
private void StandbyWhenOn() { this.State = States.Standby; }
private void StandbyWhenOff() { this.State = States.On; }
}
This state machine class is instantiated by the parent object, which to match this simple example I'll call "Device
".
What I'm trying to work out now is how best to extend this to implement the various State and Transition behaviors. Imagine the Device
class has a counter, which we want to increment whenever we power on. How do we increment that counter?
The Action
delegates used for Transition behaviors here do not take parameters from outside the FiniteStateMachine
class, and I'm pretty sure it would be terrible practice to be making changes to the containing class from in here anyway.
But if I'm not going to be implementing any of the actual behavior in this class, I feel like I've missed the point of implementing a Finite State Machine in the first place. I understand the benefit of it throwing a null reference exception when I try to perform an illegal state change, but the plan was to have it encompass behavior as well.
If it doesn't, then I'm not actually changing any of the existing behavioral code: I'm just refactoring it to call ProcessEvent()
instead of setting the State
directly from the calling code. Safer, but not cleaner.
So my questions are:
1. What would be cleanest way to implement the behavioral elements of a finite state machine that affects it's containing class?
2. Should I forgo the above FSM model in favor of a different one to achieve this?