Unity3D架构系列之- FSM有限状态机设计四
接下来,我们继续我们的FSM有限状态机的设计,在设计三中我们实现了FSState这个类,我们继续实现FSEvent事件处理类。在FSEvent类里面我们声明了FiniteStateMachine里面定义的委托函数。代码如下:
protected FiniteStateMachine.EnterState mEnterDelegate;protected FiniteStateMachine.PushState mPushDelegate;
protected FiniteStateMachine.PopState mPopDelegate;
同时我们定义了一个枚举用于处理有限状态机的状态,使用了System里面自带的封装的一个具有三个参数并返回TResult参数制定的雷兴志的方法。
public Func<object,object,object,bool> mAction = null;
这个FSEvent事件类也是独立的,不继承Mono。它的核心功能主要是处理FSState的Enter,Push,Pop。代码如下:
public FSState Enter(string stateName) {
mTargetState = stateName;
eType = EventType.ENTER;
return mStateOwner;
}
public FSState Push(string stateName) {
mTargetState = stateName;
eType = EventType.PUSH;
return mStateOwner;
}
public void Pop() {
eType = EventType.POP;
}
public void Execute(object o1,object o2,object o3) {
if (eType == EventType.POP) {
mPopDelegate();
}
else if (eType == EventType.PUSH) {
mPushDelegate(mTargetState, mOwner.CurrentState.StateName);
}
else if (eType == EventType.ENTER) {
mEnterDelegate(mTargetState);
}
else if (mAction != null) {
mAction(o1, o2, o3);
}
}
总的来说,FSEvent是通过调用Enter,Push,Pop,Execute执行状态的切换。下面把整个代码给大家展现一下:
01
02
03
04
05
06
07
08
09
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
|
using
System;
public
class
FSEvent {
protected
FiniteStateMachine.EnterState mEnterDelegate;
protected
FiniteStateMachine.PushState mPushDelegate;
protected
FiniteStateMachine.PopState mPopDelegate;
protected
enum
EventType { NONE, ENTER, PUSH, POP };
protected
string
mEventName;
protected
FSState mStateOwner;
protected
string
mTargetState;
protected
FiniteStateMachine mOwner;
protected
EventType eType;
public
Func<
object
,
object
,
object
,
bool
> mAction =
null
;
public
FSEvent(
string
name,
string
target, FSState state, FiniteStateMachine owner, FiniteStateMachine.EnterState e, FiniteStateMachine.PushState pu, FiniteStateMachine.PopState po) {
mStateOwner = state;
mEventName = name;
mTargetState = target;
mOwner = owner;
eType = EventType.NONE;
mEnterDelegate = e;
mPushDelegate = pu;
mPopDelegate = po;
}
public
FSState Enter(
string
stateName) {
mTargetState = stateName;
eType = EventType.ENTER;
return
mStateOwner;
}
public
FSState Push(
string
stateName) {
mTargetState = stateName;
eType = EventType.PUSH;
return
mStateOwner;
}
public
void
Pop() {
eType = EventType.POP;
}
public
void
Execute(
object
o1,
object
o2,
object
o3) {
if
(eType == EventType.POP) {
mPopDelegate();
}
else
if
(eType == EventType.PUSH) {
mPushDelegate(mTargetState, mOwner.CurrentState.StateName);
}
else
if
(eType == EventType.ENTER) {
mEnterDelegate(mTargetState);
}
else
if
(mAction !=
null
) {
mAction(o1, o2, o3);
}
}
}
|
设计四把FSM常用的类已经封装好了,接下来我们会在 设计五 中去封装一个EventSystem类。用于处理事件的注册,分发等功能。
Unity3D架构系列之- FSM有限状态机设计五
在设计五中,我们把事件系统EventSystem实现一下。这个EventSystem类主要实现的是事件的注册,触发,以及分发Event消息的作用。提供接口对外使用的。在这里面为了功能扩展方便用了好多模板函数,方便大家调用。
分发消息的时候,我们采用的是队列的方式,就是先进先出原则。
首先要把消息事件注册一下,就是将其放到我们预先定义的表里面。函数代码如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
|
<font face=
"微软雅黑"
><font size=
"3"
><font color=
"#000000"
>
int
Register(
string
eventName, Func<
object
,
object
,
object
,
bool
> action) {
int
listenID = ++mNextListenID;
Listen listen = (mFreeListen.Count == 0) ?
new
Listen() : mFreeListen.Pop();
listen.mID = listenID;
listen.mAction = action;
mRegistered.Add(listenID, listen);
List<
int
> eventList;
if
(!mRegisteredEvents.TryGetValue(eventName,
out
eventList))
eventList = mRegisteredEvents[eventName] =
new
List<
int
>();
eventList.Add(listenID);
return
listenID;
}</font></font></font>
|
接下来就是 消息事件的触发代码如下:
public void Trigger(string eventName) {
Call(eventName, null, null, null);
}
void Call(string eventName, object arg1, object arg2, object arg3) {
List<int> listenerList;
if(mRegisteredEvents.TryGetValue(eventName, out listenerList)) {
for(int i = listenerList.Count - 1; i >= 0; --i) {
Listen listener;
if(mRegistered.TryGetValue(listenerList, out listener)) {
if(!listener.mAction(arg1, arg2, arg3)) {
mRegistered.Remove(listenerList);
mFreeListen.Push(listener);
listenerList.RemoveAt(i);
}
}
else {
listenerList.RemoveAt(i);
}
}
if(listenerList.Count == 0) {
mRegisteredEvents.Remove(eventName);
}
}
}
遍历查找消息列表是否在mRegistererdEvents这个字典里面,mRegisteredEvents的定义如下:
Dictionary<string,List<int>> mRegisteredEvents = new Dictionary<string, List<int>>();
Listen的定义如下:
class Listen {
public int mID;
public Func<object,object,object,bool> mAction;
}
接下来就是最核心的功能了,状态机的切换功能代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
<i><i>
public
int
On<T1,T2>(
string
eventName, Func<T1,T2,
bool
> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
T1 param1;
T2 param2;
try
{ param1 = (T1)arg1; }
catch
{ param1 =
default
(T1); }
try
{ param2 = (T2)arg2; }
catch
{ param2 =
default
(T2); }
return
(action(param1, param2));
});
}
public
int
On<T1,T2>(
string
eventName, Action<T1,T2> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
T1 param1;
T2 param2;
try
{ param1 = (T1)arg1; }
catch
{ param1 =
default
(T1); }
try
{ param2 = (T2)arg2; }
catch
{ param2 =
default
(T2); }
action(param1, param2);
return
true
;
});
}</i></i>
|
整个的EventSystem事件系统的代码如下所示:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
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
178
179
180
181
182
183
184
185
186
187
188
189
190
|
<i><i>
using
System;
using
System.Collections;
using
System.Collections.Generic;
namespace
EventSystem {
public
class
Dispatcher {
public
void
Trigger(
string
eventName) {
Call(eventName,
null
,
null
,
null
);
}
public
void
Dispatch(
string
eventName) {
Dispatched d = (mFreeDispatch.Count == 0) ?
new
Dispatched() : mFreeDispatch.Pop();
mDispatched.Enqueue(d.Set(eventName));
}
public
int
On(
string
eventName, Func<
bool
> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
return
(action());
});
}
public
int
On(
string
eventName, Action action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
action();
return
(
true
);
});
}
public
void
Trigger(
string
eventName,
object
param1) {
Call(eventName, param1,
null
,
null
);
}
public
void
Dispatch(
string
eventName,
object
param1) {
Dispatched d = (mFreeDispatch.Count == 0) ?
new
Dispatched() : mFreeDispatch.Pop();
mDispatched.Enqueue(d.Set(eventName, param1));
}
public
int
On<T>(
string
eventName, Func<T,
bool
> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
T param1;
try
{ param1 = (T)arg1; }
catch
{ param1 =
default
(T); }
return
(action(param1));
});
}
public
int
On<T>(
string
eventName, Action<T> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
T param1;
try
{ param1 = (T)arg1; }
catch
{ param1 =
default
(T); }
action(param1);
return
true
;
});
}
public
void
Trigger(
string
eventName,
object
param1,
object
param2) {
Call(eventName, param1, param2,
null
);
}
public
void
Dispatch(
string
eventName,
object
param1,
object
param2) {
Dispatched d = (mFreeDispatch.Count == 0) ?
new
Dispatched() : mFreeDispatch.Pop();
mDispatched.Enqueue(d.Set(eventName, param1, param2));
}
public
int
On<T1,T2>(
string
eventName, Func<T1,T2,
bool
> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
T1 param1;
T2 param2;
try
{ param1 = (T1)arg1; }
catch
{ param1 =
default
(T1); }
try
{ param2 = (T2)arg2; }
catch
{ param2 =
default
(T2); }
return
(action(param1, param2));
});
}
public
int
On<T1,T2>(
string
eventName, Action<T1,T2> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
T1 param1;
T2 param2;
try
{ param1 = (T1)arg1; }
catch
{ param1 =
default
(T1); }
try
{ param2 = (T2)arg2; }
catch
{ param2 =
default
(T2); }
action(param1, param2);
return
true
;
});
}
public
void
Trigger(
string
eventName,
object
param1,
object
param2,
object
param3) {
Call(eventName, param1, param2, param3);
}
public
void
Dispatch(
string
eventName,
object
param1,
object
param2,
object
param3){
Dispatched d = (mFreeDispatch.Count == 0) ?
new
Dispatched() : mFreeDispatch.Pop();
mDispatched.Enqueue(d.Set(eventName, param1, param2, param3));
}
public
int
On<T1,T2,T3>(
string
eventName, Func<T1,T2,T3,
bool
> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
T1 param1;
T2 param2;
T3 param3;
try
{ param1 = (T1)arg1; }
catch
{ param1 =
default
(T1); }
try
{ param2 = (T2)arg2; }
catch
{ param2 =
default
(T2); }
try
{ param3 = (T3)arg3; }
catch
{ param3 =
default
(T3); }
return
(action(param1, param2, param3));
});
}
public
int
On<T1,T2,T3>(
string
eventName, Action<T1,T2,T3> action) {
return
Register(eventName,
delegate
(
object
arg1,
object
arg2,
object
arg3) {
T1 param1;
T2 param2;
T3 param3;
try
{ param1 = (T1)arg1; }
catch
{ param1 =
default
(T1); }
try
{ param2 = (T2)arg2; }
catch
{ param2 =
default
(T2); }
try
{ param3 = (T3)arg3; }
catch
{ param3 =
default
(T3); }
action(param1, param2, param3);
return
true
;
});
}
public
bool
Cancel(
int
listenerID) {
return
mRegistered.Remove(listenerID);
}
public
void
DispatchPending() {
while
(mDispatched.Count > 0) {
Dispatched d = mDispatched.Dequeue();
Call(d.mEventName, d.mArg1, d.mArg2, d.mArg3);
mFreeDispatch.Push(d);
}
}
int
Register(
string
eventName, Func<
object
,
object
,
object
,
bool
> action) {
int
listenID = ++mNextListenID;
Listen listen = (mFreeListen.Count == 0) ?
new
Listen() : mFreeListen.Pop();
listen.mID = listenID;
listen.mAction = action;
mRegistered.Add(listenID, listen);
List<
int
> eventList;
if
(!mRegisteredEvents.TryGetValue(eventName,
out
eventList))
eventList = mRegisteredEvents[eventName] =
new
List<
int
>();
eventList.Add(listenID);
return
listenID;
}
void
Call(
string
eventName,
object
arg1,
object
arg2,
object
arg3) {
List<
int
> listenerList;
if
(mRegisteredEvents.TryGetValue(eventName,
out
listenerList)) {
for
(
int
i = listenerList.Count - 1; i >= 0; --i) {
Listen listener;
if
(mRegistered.TryGetValue(listenerList[i],
out
listener)) {
if
(!listener.mAction(arg1, arg2, arg3)) {
mRegistered.Remove(listenerList[i]);
mFreeListen.Push(listener);
listenerList.RemoveAt(i);
}
}
else
{
listenerList.RemoveAt(i);
}
}
if
(listenerList.Count == 0) {
mRegisteredEvents.Remove(eventName);
}
}
}
class
Listen {
public
int
mID;
public
Func<
object
,
object
,
object
,
bool
> mAction;
}
class
Dispatched {
public
Dispatched Set(
string
eventName,
object
arg1=
null
,
object
arg2=
null
,
object
arg3=
null
) {
mEventName = eventName;
mArg1 = arg1;
mArg2 = arg2;
mArg3 = arg3;
return
this
;
}
public
string
mEventName;
public
object
mArg1, mArg2, mArg3;
}
Dictionary<
string
,List<
int
>> mRegisteredEvents =
new
Dictionary<
string
, List<
int
>>();
Dictionary<
int
,Listen> mRegistered =
new
Dictionary<
int
, Listen>();
Stack<Listen> mFreeListen =
new
Stack<Listen>();
Stack<Dispatched> mFreeDispatch =
new
Stack<Dispatched>();
Queue<Dispatched> mDispatched =
new
Queue<Dispatched>();
int
mNextListenID = 4711;
}
}</i></i>
|
接下来我会在最后一讲也就是设计六中,给大家说一下如何去使用。
Unity3D架构系列之- FSM有限状态机设计六
由于最近一直赶项目进度,没时间写,昨晚终于项目终于完成了,空闲下来,做一个总结。在这一篇中主要是把前五章一起总结一下,以及通过举例演示如何使用?有限状态机在游戏中使用的地方非常多,比如我们界面之间的切换,角色的状态切换等等。所以非常值得大家去学习一下,接下来我们主要实现的功能,为了表达清楚,我通过图例给大家说明一下:
给大家解析一下,程序运行首先进入主菜单,里面有三个按钮,开始游戏,音量,退出游戏。先从第一个说起,如果是开始游戏,它会进入到下一个界面游戏界面,游戏界面有个返回主菜单功能。二者可以互相切换。接下来是音量按钮,整个按钮是调节音量的,调节好了后,点确认和取消都是返回主菜单。二者之间互相切换,最后一个是退出游戏,会进入是否退出界面,如果否,返回主界面,如果是真正的关闭游戏。我们就把这个简单的功能用我们的有限状态机实现一下:
首先我们声明两个对象:
public static EventSystem.Dispatcher Events = new EventSystem.Dispatcher();
public FiniteStateMachine FSM = new FiniteStateMachine();
events主要是创建一个全局的事件系统用于我们指定的UI。
FSM是作为一个状态机被驱动。
接下来我们注册几个状态用我们的状态机:
FSM.Register("MainMenu", new MainMenuUI());
FSM.Register("AudioMenu", new AudioMenuUI());
FSM.Register("MainGame", new MainGame(FSM));
FSM.Register("QuitGame", new QuitGameUI());
我们用EntryPoint告诉玩家我们第一个界面是主界面:
FSM.EntryPoint("MainMenu");
我们为主界面定义几个actions,OPEN_AUDIO,PLAY_GAME, QUIT_GAME.其中OPEN_AUDIO和QUIT_GAME用于取代顶部栈的状态。PLAY_GAME用于增加状态栈新的item。代码如下:
FSM.State("MainMenu").On("OPEN_AUDIO").Enter("AudioMenu")
.On("PLAY_GAME").Push("MainGame")
.On("QUIT_GAME").Enter("QuitGame");
退出菜单响应PROCESS_QUIT action。代码如下:
FSM.State("QuitGame").On("PROCESS_QUIT", delegate(bool sure) {
if (sure) {
gameObject.GetComponent<TestUIState>().enabled = false;
Camera.main.backgroundColor = Color.black;
} else { FSM.Enter("MainMenu"); }
});
上述代码主要实现的功能:如果确认游戏结束,否则返回主菜单。
游戏类是负责对于主菜单弹出栈顶元素的。
01
02
03
04
05
06
07
08
09
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
|
<font face=
"微软雅黑"
><font size=
"3"
><font color=
"#000000"
>
using
UnityEngine;
using
System.Collections;
class
MainGame : MenuUI, IState {
protected
FiniteStateMachine FSM;
protected
float
Score = 0;
public
MainGame(FiniteStateMachine parentMachine) {
FSM = parentMachine;
}
public
void
OnEnter(
string
prevState) {
Score = 0;
}
public
void
OnExit(
string
nextState) {
}
public
void
OnUpdate() {
}
public
override
void
DoGUI() {
if
(GUILayout.Button(
"Quit / Back To Menu"
, GUILayout.Width(Screen.width))) {
FSM.Pop();
}
GUILayout.Space(25);
GUILayout.Label(
"The waiting game!"
);
GUILayout.Space(25);
GUILayout.Label(
"CurrentScore: "
+ System.Convert.ToInt32(Score));
Score += Time.deltaTime;
}
}</font></font></font>
|
声音菜单保留它自己的状态,处理音量逻辑。代码如下:
SM.State("AudioMenu").On("BACK_TO_MENU").Enter("MainMenu");
最后将每一个事件系统挂到状态机的actions里面,代码如下:
Events.On("OpenMainGame", delegate() { FSM.CurrentState.Trigger("PLAY_GAME"); });
Events.On("OpenAudioMenu", delegate() { FSM.CurrentState.Trigger("OPEN_AUDIO"); });
Events.On("QuitGame", delegate() { FSM.CurrentState.Trigger("QUIT_GAME"); });
Events.On("ConfirmQuit", delegate() { FSM.CurrentState.Trigger("PROCESS_QUIT", true); });
Events.On("CancelQuit", delegate() { FSM.CurrentState.Trigger("PROCESS_QUIT", false); });
Events.On("BackToMenu", delegate() { FSM.CurrentState.Trigger("BACK_TO_MENU", false); });
大家可能会问,状态机是如何切换的,我们将在Update里面实现,代码很简单:
public void Update() {
FSM.Update();
}
这样就可以每一帧都可以进行检测push还是top状态机了。
为了响应主界面我们定义了一个OnGUI函数:
void OnGUI() {
if (FSM.CurrentState == null)
return;
MenuUI ui = (MenuUI)FSM.CurrentState.StateObject;
ui.DoGUI();
}
最后因为我们涉及到主界面各个操作,所以它们都有自己的类。
我将它们都拿出来给大家分享:
主菜单中声音菜单的逻辑代码如下:
using UnityEngine;
using System.Collections;
class AudioMenuUI : MenuUI, IState {
float volume = 0.5f;
float backupVolume = 0.0f;
public void OnEnter(string prevState) {
backupVolume = volume;
}
public void OnExit(string nextState) {
}
public void OnUpdate() {
}
public override void DoGUI() {
GUILayout.Space(25.0f);
volume = GUILayout.HorizontalSlider(volume, 0.0f, 1.0f, GUILayout.Width(Screen.width));
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.Label("Volume: " + System.Convert.ToInt32(volume * 100.0f) + " %");
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Cancel", GUILayout.Height(75.0f))) {
volume = backupVolume;
TestUIState.Events.Trigger("BackToMenu");
}
if (GUILayout.Button("Confirm", GUILayout.Height(75.0f))) {
TestUIState.Events.Trigger("BackToMenu");
}
GUILayout.EndHorizontal();
}
}
主菜单类逻辑代码如下:
using UnityEngine;
using System.Collections;
public class MainMenuUI : MenuUI, IState {
public void OnEnter(string prevState) {
}
public void OnExit(string nextState) {
}
public void OnUpdate() {
}
public override void DoGUI() {
if (GUILayout.Button("Play Game", GUILayout.Width(Screen.width), GUILayout.Height(Screen.height / 3))) {
TestUIState.Events.Trigger("OpenMainGame");
}
if (GUILayout.Button("Audio Menu", GUILayout.Width(Screen.width), GUILayout.Height(Screen.height / 3))) {
TestUIState.Events.Trigger("OpenAudioMenu");
}
if (GUILayout.Button("Quit Game", GUILayout.Width(Screen.width), GUILayout.Height(Screen.height / 3))) {
TestUIState.Events.Trigger("QuitGame");
}
}
}
我们定义了一个菜单操作的抽象类用于继承:
using UnityEngine;
using System.Collections;
public class MenuUI {
public virtual void DoGUI() {
}
}
游戏退出类代码如下:
using UnityEngine;
using System.Collections;
class QuitGameUI : MenuUI, IState {
public void OnEnter(string prevState) {
}
public void OnExit(string nextState) {
}
public void OnUpdate() {
}
public override void DoGUI() {
GUILayout.BeginHorizontal();
if (GUILayout.Button("Confirm", GUILayout.Width(Screen.width / 2), GUILayout.Height(Screen.height))) {
TestUIState.Events.Trigger("ConfirmQuit");
}
if (GUILayout.Button("Cancel", GUILayout.Width(Screen.width / 2), GUILayout.Height(Screen.height))) {
TestUIState.Events.Trigger("CancelQuit");
}
GUILayout.EndHorizontal();
}
}
最后只要将下面的脚本挂到对象上就可以了:
using UnityEngine;
using System.Collections;
public class TestUIState : MonoBehaviour {
public static EventSystem.Dispatcher Events = new EventSystem.Dispatcher();
public FiniteStateMachine FSM = new FiniteStateMachine();
public void Awake() {
FSM.Register("MainMenu", new MainMenuUI());
FSM.Register("AudioMenu", new AudioMenuUI());
FSM.Register("MainGame", new MainGame(FSM));
FSM.Register("QuitGame", new QuitGameUI());
FSM.EntryPoint("MainMenu");
FSM.State("MainMenu").On("OPEN_AUDIO").Enter("AudioMenu")
.On("PLAY_GAME").Push("MainGame")
.On("QUIT_GAME").Enter("QuitGame");
FSM.State("QuitGame").On("PROCESS_QUIT", delegate(bool sure) {
if (sure) {
gameObject.GetComponent<TestUIState>().enabled = false;
Camera.main.backgroundColor = Color.black;
} else { FSM.Enter("MainMenu"); }
});
FSM.State("AudioMenu").On("BACK_TO_MENU").Enter("MainMenu");
Events.On("OpenMainGame", delegate() { FSM.CurrentState.Trigger("PLAY_GAME"); });
Events.On("OpenAudioMenu", delegate() { FSM.CurrentState.Trigger("OPEN_AUDIO"); });
Events.On("QuitGame", delegate() { FSM.CurrentState.Trigger("QUIT_GAME"); });
Events.On("ConfirmQuit", delegate() { FSM.CurrentState.Trigger("PROCESS_QUIT", true); });
Events.On("CancelQuit", delegate() { FSM.CurrentState.Trigger("PROCESS_QUIT", false); });
Events.On("BackToMenu", delegate() { FSM.CurrentState.Trigger("BACK_TO_MENU", false); });
}
public void Update() {
FSM.Update();
}
void OnGUI() {
if (FSM.CurrentState == null)
return;
MenuUI ui = (MenuUI)FSM.CurrentState.StateObject;
ui.DoGUI();
}
}
测试用的代码一共有AudioMenUI.cs MainGame.cs MainMenuUI.cs MenuUI.cs QuitGameUI.cs TestUIState.cs.大家只要将TestUIStat.cs挂到对象上就可以运行了。在使用的过程中可以先调试一下,前五章是给大家封装的,可以直接将其运用到项目中。这里的类只是跟大家分享一下如何去运用。
有限状态机到这里就全部结束了。有什么问题欢迎一起讨论。接下来我会给大家分享其他技术点。。。。。