什么是有限状态机?
维基百科解释:有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机(英语:finite-state automaton,缩写:FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。
- 一般情况下每个状态的生命周期为 进入状态、状态持续、离开状态,然后再由一个控制器来进行状态注册以及控制各个状态的切换
- 同时只能存在一种状态
为什么用有限状态机?
在实际开发过程中,我们经常需要处理各种状态的切换,一般情况下我们用if…else if…或者switch case 就可以解决。但是在遇到一些比较复杂的逻辑,需要很多的状态,并且有一些状态还要处理一些复杂的逻辑,这个时候再用上面的方法就显得有点捉襟见肘了,严重影像后期的维护。有限状态机就是为了解决这种多状态切换的一种数学模型。
怎么用有限状态机?
根据定义,首先我定义一个状态基类或者接口,这里为了方便后期的扩展,可能有些状态需要多继承,例如同时继承状态接口和Monobehaviour,所以我选择使用接口来定义状态,如下:
1
2
3
4
5
6
7public interface IState { void OnEnter(params object[] param); void OnStay(params object[] param); void OnExit(params object[] param); }
可以看到,我定义的状态包含了三个方法体,分别是OnEnter(进入状态)、OnStay(状态持续)、OnExit(离开状态) 所以这个状态接口就可以模拟基本状态的生命周期。
接下来我定义一个状态机的控制对象,这里也用接口定义。如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public interface IFSMController { Dictionary<Type, IState> stateDict { get; } IState currentState { get; } void AddState(IState state); void RemoveState(Type type); void ClearState(); void ToState<T>()where T:IState; IState GetState<T>(); void OnUpdate(); }
列个表格来解释一下每个属性或者方法体的作用吧
字段或属性 | 解释 |
---|---|
stateDict | 状态字典 |
currentState | 当前状态 |
AddState | 增加一个状态 |
RemoveState | 移除一个状态 |
ClearState | 清空状态 |
ToState | 切换到目标状态 |
GetState | 根据类型得到状态对象 |
OnUpdate | 更新 |
其实常规的状态字典定义中,有使用唯一flag做key的,也有使用枚举做key的,我这里之所以用Type做key是因为,省去了每次增加状态时都需要增加flag或者枚举类型。
基础的有限状态机模型已经搭建完毕,下面做一个小测试吧。
假设我有一个人物,只有三种状态 Idle、Walk、Run初始状态为Idle,点击A键进入Walk状态,点击S键进入Run状态,点击D键重新进入Idle状态。
从状态定义开始写吧:
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
52
53
54
55
56
57
58
59
60
61using UnityEngine; namespace FSM_Test { public class Idle : IState { public void OnEnter(params object[] param) { Debug.Log("进入Idle状态"); } public void OnStay(params object[] param) { // Debug.Log("持续 Idle状态"); } public void OnExit(params object[] param) { Debug.Log("离开Idle状态"); } } public class Walk : IState { public void OnEnter(params object[] param) { Debug.Log("进入Walk状态"); } public void OnStay(params object[] param) { } public void OnExit(params object[] param) { Debug.Log("离开Walk状态"); } } public class Run : IState { public void OnEnter(params object[] param) { Debug.Log("进入Run状态"); } public void OnStay(params object[] param) { } public void OnExit(params object[] param) { Debug.Log("离开Run状态"); } } }
FSM控制器如下:
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66using System; using System.Collections.Generic; using UnityEngine; public class FSMController : IFSMController { public Dictionary<Type, IState> stateDict { get; private set; } public IState currentState { get; private set; } public FSMController() { stateDict=new Dictionary<Type, IState>(); } public void AddState(IState state) { if (state == null) return; Type type = state.GetType(); if (stateDict.ContainsKey(type)) { Debug.LogFormat("AddState 警告!【{0}】无需重复添加",type); return; } stateDict.Add(type,state); } public void RemoveState(Type type) { if (!stateDict.ContainsKey(type)) return; stateDict.Remove(type); } public void ClearState() { stateDict.Clear(); } public void ToState<T>()where T:IState { IState state = GetState<T>(); if (state == null) return; currentState?.OnExit(); state.OnEnter(); currentState = state; } public IState GetState<T>()where T:IState { if (stateDict == null || stateDict.Count == 0) return null; Type type = typeof(T); if (!stateDict.ContainsKey(type)) return null; return stateDict[type]; } public void OnUpdate() { currentState?.OnStay(); } public bool IsCurrentState<T>() where T:IState { return currentState.GetType() == typeof(T); } }
测试启动
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
27using UnityEngine; namespace FSM_Test { public class DemoTest : MonoBehaviour { private FSMController fsmController; void Start() { fsmController=new FSMController(); fsmController.AddState(new Idle()); fsmController.AddState(new Walk()); fsmController.AddState(new Run()); fsmController.ToState<Idle>(); } void Update() { fsmController?.OnUpdate(); if (Input.GetKeyDown(KeyCode.A))fsmController.ToState<Walk>(); if (Input.GetKeyDown(KeyCode.S))fsmController.ToState<Run>(); if (Input.GetKeyDown(KeyCode.D))fsmController.ToState<Idle>(); } } }
附加
根据定义我们知道,一个状态机我们只能同时存在一种状态,但是,有时候我们需要同时存在多个状态,例如,蹲走,跳走等这些多个状态混合的逻辑时,这个时候很显然一个状态机已经无法满足正常需求了。所以碰到这种情况,我们可以考虑用两个状态机,一个负责姿态,一个负责移动,这样就能解决多状态共存的问题。
后期准备抽时间写一个有限状态机做人物控制的Demo。
最后
以上就是强健手机最近收集整理的关于有限状态机什么是有限状态机?为什么用有限状态机?怎么用有限状态机?附加的全部内容,更多相关有限状态机什么是有限状态机内容请搜索靠谱客的其他文章。
发表评论 取消回复