Unity3D架构系列之- FSM有限状态机设计四至六

  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执行状态的切换。下面把整个代码给大家展现一下:
[C#]  纯文本查看  复制代码
?
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消息的作用。提供接口对外使用的。在这里面为了功能扩展方便用了好多模板函数,方便大家调用。

         分发消息的时候,我们采用的是队列的方式,就是先进先出原则。

        首先要把消息事件注册一下,就是将其放到我们预先定义的表里面。函数代码如下所示:

[C#] 纯文本查看 复制代码
?
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;
          }

     接下来就是最核心的功能了,状态机的切换功能代码:

[C#] 纯文本查看 复制代码
?
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事件系统的代码如下所示:

[C#] 纯文本查看 复制代码
?
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"); }
   });

上述代码主要实现的功能:如果确认游戏结束,否则返回主菜单。

游戏类是负责对于主菜单弹出栈顶元素的。

[C#] 纯文本查看 复制代码
?
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挂到对象上就可以运行了。在使用的过程中可以先调试一下,前五章是给大家封装的,可以直接将其运用到项目中。这里的类只是跟大家分享一下如何去运用。

     有限状态机到这里就全部结束了。有什么问题欢迎一起讨论。接下来我会给大家分享其他技术点。。。。。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值