学到了凉鞋大佬的FSM简易写法。

今天在编写2024CUSGA的作品的时候,用到了刚学到的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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

//FSM要实现的接口
public interface IState
{
void Enter();
void Update();
void FixedUpdate();
void Exit();
}

//FSM获取方法的方法
public class CustomState : IState
{
private Action mOnEnter;
private Action mOnUpdate;
private Action mOnFixedUpdate;
private Action mOnExit;
public CustomState OnEnter(Action OnEnter)
{
mOnEnter = OnEnter;
return this;
}

public CustomState OnUpdate(Action OnUpdate)
{
mOnUpdate = OnUpdate;
return this;
}

public CustomState OnFixedUpdate(Action OnFixedUpdate)
{
mOnFixedUpdate = OnFixedUpdate;
return this;
}

public CustomState OnExit(Action OnExit)
{
mOnExit = OnExit;
return this;
}

public void Enter()
{
mOnEnter?.Invoke();
}
public void Update()
{
mOnUpdate?.Invoke();
}
public void FixedUpdate()
{
mOnFixedUpdate?.Invoke();
}
public void Exit()
{
mOnExit?.Invoke();
}

}


public class FSM<T>
{
//获取对象的各种状态(一般是枚举类型)
public Dictionary<T,IState> mStates = new Dictionary<T,IState>();

//填各种状态的OnEnter等等
public CustomState State(T t)
{
if (mStates.ContainsKey(t))
{
return mStates[t] as CustomState;
}

var state = new CustomState();

mStates.Add(t, state);

return state;
}


private IState mCurrentState;
private T mCurrentStateId;

public IState CurrentState => mCurrentState;
public T CurrentStateId => mCurrentStateId;

//改变状态
public void ChangeState(T t)
{
if(mStates.TryGetValue(t, out var state))
{
if(mCurrentState != null)
{
mCurrentState.Exit();
mCurrentState = state;
mCurrentStateId = t;
mCurrentState.Enter();
}
}
}

//开始的状态
public void StartState(T t)
{
if(mStates.TryGetValue(t,out var state))
{
mCurrentState = state;
mCurrentStateId = t;
state.Enter();
}
}

//直接在对象的FixedUpdate或者Update中使用
public void FixedUpdate()
{
mCurrentState?.FixedUpdate();
}

public void Update()
{
mCurrentState?.Update();
}

//在函数的Destory的时候调用
public void Clear()
{
mCurrentState = null;
mCurrentStateId = default;
mStates.Clear();
}
}


/// <summary>
/// 实例用法,写在脚本的Awake里
/// </summary>
public class StateExample
{
public enum States
{
A,
B,
C
}

void Example()
{
var fsm=new FSM<States>();
fsm.State(States.A)
.OnEnter(() =>
{
//ToDO
})
.OnUpdate(() =>
{
//ToDO
})
.OnFixedUpdate(() =>
{
//ToDO
})
.OnExit(() =>
{
//ToDO
});

fsm.StartState(States.A);
fsm.ChangeState(States.B);
}
}

也能处理物理层面的改变,毕竟是有FixedUpdate在的。

在使用了两天之后,我发现这个FSM的写法确实非常的清楚,能够看出每个代码的使用结果。就好像我在写的实例程序一样,只要在程序的开头写上这么一段就能获取到整个状态的变化,虽然看着比较多,但是代码辨识度很高。

其实每一个FSM的写法重点都在于怎么把方法传进状态机,并且调用起来非常简单,除了这一种写法之外,还有一种写法是在各个函数的的构造函数中传入FSM,当时相比于那种方式,这种方式的泛型很明显更加自由,我会在下一篇博客写到另一个状态机的写法。