[参考于Bob Nystrom的游戏编程模式一书](Table of Contents · Game Programming Patterns)
引子
一个玩家有一个空中劈砍的动作,输入为J键
但有如下限定条件
- 必须在空中
- 必须有处于未受击状态
- 必须必须必须处于猛男状态
用最简单的分支语句写就是这样
1 2 3
| if(在空中&&未受击&&我是猛男&&输入为j)
空中劈砍();
|
再加一个动作 释放条件如下
- 必须在空中
- 必须处于受击状态
- 必须必须必须处于御女状态
就是这样写了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| if(在空中)
{ if(未受击) { if(猛男) { } else if(御女) { 释放动作 } } else if(受击) { ... }
}
|
显然恶心的一批,条件繁杂,但这还只是两个动作,四个状态,如果动作更多,状态更多,那直接摆烂得了。
是这样的,摆呗;
解释状态机
从逻辑方面来讲:实现状态机的目的就是解耦,解除繁杂冗余的的条件判定,状态机是一个角色所有状态的总和,但同时只能允许一个状态(并发状态机可以算一个例外,但其实并发也可以算一个,因为并发状态是由主状态和其他状态组成,其他状态依附于主状态而生,是一体的),它像一个机器一样在不同状态之间来回切换以及运转,实现方法有多种,例如最简单的枚举式状态机,和其他诸如有限状态机,并发状态机,分层状态机,下推状态机
枚举式(状态机)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| enum 状态 { 状态1, 状态2. 状态3. 状态4. } swicth(枚举状态) { case 状态1: break; case 状态2: break; case 状态3: break; case 状态4: break; }
|
优点:一定程度上缓解了冗余感,将处理不同状态的代码聚合在了一起,直观就是看起来舒服,写起来更加明了。
缺点:处理逻辑的效果有限,没有真正的抽象化。
因此,Here comes THE FSM
有限状态机

最简单FSM实现,首先定义一状态接口,每一个状态都要实现其接口,
伪代码
1 2 3 4 5 6 7 8
| public interface IState { int StateID { get; } string StateName { get; } void OnStateEnter(); void OnStateUpdate(); void OnStateExit(); }
|
定义一个FSM类 收集所有状态,在内部实现状态转换功能等
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class FSM { IState currentState; Dictionary<int, IState> myStates; int statesCount=>myStates.Count; void ChangeState(IState newState) { currentState.OnStateExit(); currentState=newState; currentState.OnStateEnter(); } void AddState(); void RemoveState();
|
然后在对应生命周期函数允许就好了
1 2 3 4 5 6 7 8
| public class Player:MonoBehavior { FSM fsm; void Update() { fsm.currentState.OnStateUpdate(); } }
|
大抵就是这样,可以不断地根据需求继续优化和封装。
个人觉得每一个状态都需要重写一个类非常麻烦,就自己练了下
大致想法:“希望通过玩家定义的状态枚举,通过泛型传参的方式创建泛型状态机,在构造状态机的同时,根据状态枚举,自动创建不同的状态,我再通过委托传递,设置不同状态的函数即可”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| namespace CustomFsm { public class StateBase<TState> where TState : struct,Enum { private FSM<TState> fsm; private TState state; public FSM<TState> Fsm { get { return fsm; } } public TState State { get { return state; } } public int StateID => state.GetHashCode(); public ValueTuple<Action, Action, Action> actionTuple; public StateBase(TState state, FSM<TState> fsm) { this.state = state; this.fsm = fsm; } public StateBase(TState state,FSM<TState> fsm, ValueTuple<Action, Action, Action> actionTuple):this(state,fsm) => this.actionTuple = actionTuple; public void InitEvent(Action Enter=null,Action Update=null, Action Exist=null) { actionTuple.Item1 += Enter; actionTuple.Item2 += Update; actionTuple.Item3 += Exist; } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| namespace CustomFsm { public class FSM<TState> where TState : Enum { StateBase<TState> currentState; Dictionary<TState, StateBase<TState>> myStates; public int statesCount => myStates.Count; public FSM() { Array enumsValue = Enum.GetValues(typeof(TState)); CreateStatesDic(enumsValue); } public void ChangeState(TState state) { currentState.actionTuple.Item3.Invoke(); currentState = myStates[state]; } public void AddState(StateBase<TState> state) { myStates.Add(state.State, state); } public bool RemoveState(TState state) { return myStates.Remove(state); } public void ClearState(){} public void InitStateEvent(TState state, Action Enter = null, Action Update = null, Action Exist = null) { myStates[state].ActionTuple.Item1 += Enter; myStates[state].ActionTuple.Item2 += Update; myStates[state].ActionTuple.Item3 += Exist; } private Dictionary<int, StateBase<TState>> CreateStatesDic(Array enums) { Dictionary<int, StateBase<TState>> dic = new Dictionary<int, StateBase<TState>>(); for (int i = 0; i < enums.Length; i++) { var state = new StateBase<TState>((TState)enums.GetValue(i), this); AddState(state); } return dic; } } }
|
这样用即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public enum PlayerState { k1, k2, k3 } class Test { static void Main(string[] args) { FSM<PlayerState> fSM = new FSM<PlayerState>(); fSM.InitStateEvent(PlayerState.k1, A, B, C);
} void A() { } void B() { } void C() { } }
|
还有许多有意思的地方可以加,比如:给状态间的转换增加异步的模式;让状态机成为大脑,游戏角色成为他的组件,写一个Ai;又或者更加傻瓜式,像Unity那样通过反射的方式实现自动化(反射获取方法后再检索函数名,规范命名如“状态1_Update”,有就自动添加进状态函数里)等等等
未完待续。。。🙅
并发状态机
分层状态机
下推状态机