我目前正在基于这个例子将游戏中的人工智能重构为有限状态机,我选择这个例子是因为它的可读性和高度可视化的定义格式:
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; }
}
这个状态机类是由父对象实例化的,为了匹配这个简单的例子,我将其称为"Device
"。
我现在想弄清楚的是,如何最好地扩展它来实现各种状态和过渡行为。想象一下,Device
类有一个计数器,每当我们打开电源时,我们都想增加它。我们如何增加这个计数器?
这里用于转换行为的Action
委托不接受FiniteStateMachine
类之外的参数,我敢肯定,无论如何,从这里对包含类进行更改都是非常糟糕的做法。
但是,如果我不打算实现这个类中的任何实际行为,我觉得我一开始就错过了实现有限状态机的要点。当我试图执行非法状态更改时,我理解它抛出空引用异常的好处,但计划是让它也包含行为。
如果没有,那么我实际上并没有更改任何现有的行为代码:我只是重构它以调用ProcessEvent()
,而不是直接从调用代码中设置State
。更安全,但不干净。
所以我的问题是:
1.实现影响其包含类的有限状态机的行为元素的最干净的方法是什么
2.为了实现这一点,我是否应该放弃上述FSM模型,而选择不同的FSM模型?
您应该检查状态模式(如zerkms注释中所建议的)。
这将允许您轻松地对状态转换执行其他操作。
在使用状态模式时,您仍然需要决定如何允许各个状态共享数据。如果将共享数据放在上下文类中,那么很难在不公开的情况下将其公开给状态。C++实现通常使用friend
来允许状态访问上下文中的私有数据。在C#中,我有时会将共享数据封装在一个单独的类中,该类从上下文传递到状态,但不会由上下文或状态公开。
如果你在摸索了状态模式后还不清楚,那么添加一条注释,我就可以发布一些代码了。