开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

教程分享

关注:648

当前位置:游戏蛮牛 技术专区 教程分享

查看: 882|回复: 8

[基础知识] Unity3D架构系列之- FSM有限状态机设计一至四

[复制链接]  [移动端链接]
7日久生情
4285/5000
排名
2175
昨日变化
4

1646

主题

1657

帖子

4285

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
132510
好友
17
蛮牛币
57291
威望
0
注册时间
2015-12-24
在线时间
352 小时
最后登录
2017-6-20
发表于 2016-7-6 11:47:02 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
  我们在游戏开发中经常面临架构设计问题,在蛮牛问答里面也有好多朋友问关于架构方面的问题,在这里我就将一些经常使用的游戏开发方面的知识跟大家介绍 一下, 一是给大家提供一个设计思路,在此基础上可以举一反三,二是大家可以通过这个平台共同学习。闲话不多说了,切入正题,FSM有限状态机,在游戏开发中如何使用?那些模块的设计需要它?接下来我们就分析一下游戏:

  比如我们的玩家自身的状态切换,Idle,walk,attack等等,这些都属于状态的切换,可以用有限状态机;还有比如玩家去完任务:领取任务,杀怪,交易,交接任务;还有界面逻辑比如登录,进入场景,退出等等,在游戏中状态切换还是很多的,以上我说的都可以使用有限状态机。既然这么多地方可以使用,那么我们如何去设计有限状态机?

  下面我们先设计我们的FSM有限状态机类图,如下所示:

  在这里我的优先状态机,共五个类文件组成。下面就给大家一一说明:

  第一个是IState类,这个类是抽象的,主要是实现有限状态机的接口,书写如下:

  

[p=26,null,

  left]usingUnityEngine;

  usingSystem.Collections;

  publicinterface

  IState {

  voidOnEnter(stringprevState);

  voidOnExit(stringnextState);

  voidOnUpdate();

  }

[p=26,null,

  left]

  这个抽象类一共只有三个接口,一个是进入状态,停止状态,更新状态。

  第二个类我会在系列二中给大家介绍。

  时间有点晚了,就先到这里吧。

  架构系列之- FSM有限状态机设计二

  在设计一中,我们把IState类设计了一下,接下来,我们写一下FiniteStateMachine这个类,这个类主要的作用就是对于外界调用,这个类是独立封装的,不继承Mono。这个类的功能是提供一个栈,用于存放FSState,还有通过Update进行状态的切换,以及对栈的管理,Pop和Push操作以及状态的注册。最重要的一点是声明了三个委托函数代码如下:

  public delegate void EnterState(string stateName);

  public delegate void PushState(string stateName, string lastStateName);

  public delegate void PopState();

  主要是用于状态之间的切换。定义了Dictionary和Stack,前者用于State注册,后者是状态切换。代码如下:

  protected Dictionary<string, FSState> mStates;

  protected Stack<FSState>    mStateStack;

  对应的相关处理函数如下:

  public void Register(string stateName, IState stateObject) {

  if (mStates.Count == 0)

  mEntryPoint = stateName;

  mStates.Add(stateName, new FSState(stateObject, this, stateName, Enter, Push, Pop));

  }

  用于状态机的注册,对Stack的操作是如下两个函数,一个是Pop,一个是Push函数。代码如下:

  

[p=26,null,

  left]publicvoid

  Push(stringnewState)

  {

  stringlastName

  = null;

  if(mStateStack.Count

  > 1) {

  lastName

  = mStateStack.Peek().StateName;

  }

  Push(newState,

  lastName);

  }

  protectedvoid

  Push(stringstateName,

  stringlastStateName)

  {

  mStateStack.Push(mStates[stateName]);

  mStateStack.Peek().StateObject.OnEnter(lastStateName);

  }

  publicvoid

  Pop() {

  Pop(null);

  }

  protectedstring

  Pop(stringnewName)

  {

  FSState

  lastState = mStateStack.Peek();

  stringnewState

  = null;

  if(newName

  == null&&

  mStateStack.Count > 1) {

  intindex

  = 0;

  foreach(FSState

  item inmStateStack)

  {

  if(index++

  == mStateStack.Count - 2) {

  newState

  = item.StateName;

  }

  }

  }

  else{

  newState

  = newName;

  }

  stringlastStateName

  = null;

  if(lastState

  != null)

  {

  lastStateName

  = lastState.StateName;

  lastState.StateObject.OnExit(newState);

  }

  mStateStack.Pop();

  returnlastStateName;

  }

  下面增加了状态触发消息事件,代码如下:

  

[p=26,null,

  left]publicvoid

  Trigger(stringeventName)

  {

  CurrentState.Trigger(eventName);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1)

  {

  CurrentState.Trigger(eventName,

  param1);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1,

  objectparam2)

  {

  CurrentState.Trigger(eventName,

  param1, param2);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1,

  objectparam2,

  objectparam3)

  {

  CurrentState.Trigger(eventName,

  param1, param2, param3);

  }

[p=26,null,

  left]

  FiniteStateMachine核心的功能给大家介绍完了,下面奉上整个类的代码:

  <font

  face="微软雅黑"><font

  size="3"><font

  color="black">

[p=26,null,

  left]usingUnityEngine;

  usingSystem.Collections;

  usingSystem.Collections.Generic;

  publicclass

  FiniteStateMachine {

  publicdelegate

  void

  EnterState(stringstateName);

  publicdelegate

  void

  PushState(stringstateName,

  stringlastStateName);

  publicdelegate

  void

  PopState();

  protectedDictionary<string,

  FSState>        mStates;

  protectedstring

  mEntryPoint;

  protectedStack<FSState>

  mStateStack;

  publicFiniteStateMachine()

  {

  mStates

  = newDictionary<string,

  FSState>();

  mStateStack

  = newStack<FSState>();

  mEntryPoint

  = null;

  }

  publicvoid

  Update() {

  if(CurrentState

  == null)

  {

  mStateStack.Push(mStates[mEntryPoint]);

  CurrentState.StateObject.OnEnter(null);

  }

  CurrentState.StateObject.OnUpdate();

  }

  publicvoid

  Register(stringstateName,

  IState stateObject) {

  if(mStates.Count

  == 0)

  mEntryPoint

  = stateName;

  mStates.Add(stateName,newFSState(stateObject,

  this,

  stateName, Enter, Push, Pop));

  }

  publicFSState

  State(stringstateName)

  {

  returnmStates[stateName];

  }

  publicvoid

  EntryPoint(stringstartName)

  {

  mEntryPoint

  = startName;

  }

  publicFSState

  CurrentState {

  get{

  if(mStateStack.Count

  == 0)

  returnnull;

  returnmStateStack.Peek();

  }

  }

  publicvoid

  Enter(stringstateName)

  {

  Push(stateName,

  Pop(stateName));

  }

  publicvoid

  Push(stringnewState)

  {

  stringlastName

  = null;

  if(mStateStack.Count

  > 1) {

  lastName

  = mStateStack.Peek().StateName;

  }

  Push(newState,

  lastName);

  }

  protectedvoid

  Push(stringstateName,

  stringlastStateName)

  {

  mStateStack.Push(mStates[stateName]);

  mStateStack.Peek().StateObject.OnEnter(lastStateName);

  }

  publicvoid

  Pop() {

  Pop(null);

  }

  protectedstring

  Pop(stringnewName)

  {

  FSState

  lastState = mStateStack.Peek();

  stringnewState

  = null;

  if(newName

  == null&&

  mStateStack.Count > 1) {

  intindex

  = 0;

  foreach(FSState

  item inmStateStack)

  {

  if(index++

  == mStateStack.Count - 2) {

  newState

  = item.StateName;

  }

  }

  }

  else{

  newState

  = newName;

  }

  stringlastStateName

  = null;

  if(lastState

  != null)

  {

  lastStateName

  = lastState.StateName;

  lastState.StateObject.OnExit(newState);

  }

  mStateStack.Pop();

  returnlastStateName;

  }

  publicvoid

  Trigger(stringeventName)

  {

  CurrentState.Trigger(eventName);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1)

  {

  CurrentState.Trigger(eventName,

  param1);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1,

  objectparam2)

  {

  CurrentState.Trigger(eventName,

  param1, param2);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1,

  objectparam2,

  objectparam3)

  {

  CurrentState.Trigger(eventName,

  param1, param2, param3);

  }

  }

  </font></font></font>

  上面整个FSMStateMachine类就封装完了,要知后事如何?且看系列三的讲解。

  Unity3D架构系列之- FSM有限状态机设计三

  在设计二中,我们实现了有限状态机管理类,接下来,我们实现FSState这个类,这里类主要是状态的基本操作以及事件触发。在这里我们定义了在FiniteStateMachine类里声明的三个委托。在FSState里面使用的代码如下:

  protected FiniteStateMachine.EnterState mEnterDelegate;

  protected FiniteStateMachine.PushState mPushDelegate;

  protected FiniteStateMachine.PopState mPopDelegate;

  这个FSState是独立的一个类,不继承Mono,我们定义了一个构造函数,将我们的委托进行了初始化:

  public FSState(IState obj, FiniteStateMachine owner, string name, FiniteStateMachine.EnterState e, FiniteStateMachine.PushState pu, FiniteStateMachine.PopState po) {

  mStateObject = obj;

  mStateName = name;

  mOwner = owner;

  mEnterDelegate = e;

  mPushDelegate = pu;

  mPopDelegate = po;

  mTranslationEvents = new Dictionary<string, FSEvent>();

  }

  我们声明了FSEvent事件处理函数用于将事件的名字和事件加入的Dictionary里面

  public FSEvent On(string eventName) {

  FSEvent newEvent = new FSEvent(eventName, null, this, mOwner, mEnterDelegate, mPushDelegate, mPopDelegate);

  mTranslationEvents.Add(eventName, newEvent);

  return newEvent;

  }

  加入到列表后,我们需要从表里面取出去执行,这就需要Trigger触发函数:

  public void Trigger(string name) {

  mTranslationEvents[name].Execute(null, null, null);

  }

  public void Trigger(string eventName, object param1) {

  mTranslationEvents[eventName].Execute(param1, null, null);

  }

  public void Trigger(string eventName, object param1, object param2) {

  mTranslationEvents[eventName].Execute(param1, param2, null);

  }

  public void Trigger(string eventName, object param1, object param2, object param3) {

  mTranslationEvents[eventName].Execute(param1, param2, param3);

  }

  以上也是FSState类的核心代码,闲话少说,FSState类的整个代码,如下:

  


  

  


  usingSystem;

  usingSystem.Collections;

  usingSystem.Collections.Generic;

  publicclass

  FSState {

  protectedFiniteStateMachine.EnterState

  mEnterDelegate;

  protectedFiniteStateMachine.PushState

  mPushDelegate;

  protectedFiniteStateMachine.PopState

  mPopDelegate;

  protectedIState

  mStateObject;

  protectedstring

  mStateName;

  protectedFiniteStateMachine

  mOwner;

  protectedDictionary<string,

  FSEvent> mTranslationEvents;

  publicFSState(IState

  obj, FiniteStateMachine owner, stringname,

  FiniteStateMachine.EnterState e, FiniteStateMachine.PushState pu, FiniteStateMachine.PopState po) {

  mStateObject

  = obj;

  mStateName

  = name;

  mOwner

  = owner;

  mEnterDelegate

  = e;

  mPushDelegate

  = pu;

  mPopDelegate

  = po;

  mTranslationEvents

  = newDictionary<string,

  FSEvent>();

  }

  publicIState

  StateObject {

  get{

  returnmStateObject;

  }

  }

  publicstring

  StateName {

  get{

  returnmStateName;

  }

  }

  publicFSEvent

  On(stringeventName)

  {

  FSEvent

  newEvent = newFSEvent(eventName,

  null,this,

  mOwner, mEnterDelegate, mPushDelegate, mPopDelegate);

  mTranslationEvents.Add(eventName,

  newEvent);

  returnnewEvent;

  }

  publicvoid

  Trigger(stringname)

  {

  mTranslationEvents[name].Execute(null,null,null);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1)

  {

  mTranslationEvents[eventName].Execute(param1,null,null);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1,

  objectparam2)

  {

  mTranslationEvents[eventName].Execute(param1,

  param2, null);

  }

  publicvoid

  Trigger(stringeventName,

  objectparam1,

  objectparam2,

  objectparam3)

  {

  mTranslationEvents[eventName].Execute(param1,

  param2, param3);

  }

  publicFSState

  On<T>(stringeventName,

  Func<T, bool>

  action) {

  FSEvent

  newEvent = newFSEvent(eventName,

  null,this,

  mOwner, mEnterDelegate, mPushDelegate, mPopDelegate);

  newEvent.mAction

  = delegate(objecto1,

  objecto2,

  objecto3)

  {

  T

  param1;

  try{

  param1 = (T)o1; } catch{

  param1 = default(T);

  }

  action(param1);

  returntrue;

  };

  mTranslationEvents.Add(eventName,

  newEvent);

  returnthis;

  }

  publicFSState

  On<T>(stringeventName,

  Action<T> action) {

  FSEvent

  newEvent = newFSEvent(eventName,

  null,this,

  mOwner, mEnterDelegate, mPushDelegate, mPopDelegate);

  newEvent.mAction

  = delegate(objecto1,

  objecto2,

  objecto3)

  {

  T

  param1;

  try{

  param1 = (T)o1; } catch{

  param1 = default(T);

  }

  action(param1);

  returntrue;

  };

  mTranslationEvents.Add(eventName,

  newEvent);

  returnthis;

  }

  publicFSState

  On<T1, T2>(stringeventName,

  Func<T1, T2, bool>

  action) {

  FSEvent

  newEvent = newFSEvent(eventName,

  null,this,

  mOwner, mEnterDelegate, mPushDelegate, mPopDelegate);

  newEvent.mAction

  = delegate(objecto1,

  objecto2,

  objecto3)

  {

  T1

  param1;

  T2

  param2;

  try{

  param1 = (T1)o1; } catch{

  param1 = default(T1);

  }

  try{

  param2 = (T2)o2; } catch{

  param2 = default(T2);

  }

  action(param1,

  param2);

  returntrue;

  };

  mTranslationEvents.Add(eventName,

  newEvent);

  returnthis;

  }

  publicFSState

  On<T1, T2>(stringeventName,

  Action<T1, T2> action) {

  FSEvent

  newEvent = newFSEvent(eventName,

  null,this,

  mOwner, mEnterDelegate, mPushDelegate, mPopDelegate);

  newEvent.mAction

  = delegate(objecto1,

  objecto2,

  objecto3)

  {

  T1

  param1;

  T2

  param2;

  try{

  param1 = (T1)o1; } catch{

  param1 = default(T1);

  }

  try{

  param2 = (T2)o2; } catch{

  param2 = default(T2);

  }

  action(param1,

  param2);

  returntrue;

  };

  mTranslationEvents.Add(eventName,

  newEvent);

  returnthis;

  }

  publicFSState

  On<T1, T2, T3>(stringeventName,

  Func<T1, T2, T3, bool>

  action) {

  FSEvent

  newEvent = newFSEvent(eventName,

  null,this,

  mOwner, mEnterDelegate, mPushDelegate, mPopDelegate);

  newEvent.mAction

  = delegate(objecto1,

  objecto2,

  objecto3)

  {

  T1

  param1;

  T2

  param2;

  T3

  param3;

  try{

  param1 = (T1)o1; } catch{

  param1 = default(T1);

  }

  try{

  param2 = (T2)o2; } catch{

  param2 = default(T2);

  }

  try{

  param3 = (T3)o3; } catch{

  param3 = default(T3);

  }

  action(param1,

  param2, param3);

  returntrue;

  };

  mTranslationEvents.Add(eventName,

  newEvent);

  returnthis;

  }

  publicFSState

  On<T1, T2, T3>(stringeventName,

  Action<T1, T2, T3> action) {

  FSEvent

  newEvent = newFSEvent(eventName,

  null,this,

  mOwner, mEnterDelegate, mPushDelegate, mPopDelegate);

  newEvent.mAction

  = delegate(objecto1,

  objecto2,

  objecto3)

  {

  T1

  param1;

  T2

  param2;

  T3

  param3;

  try{

  param1 = (T1)o1; } catch{

  param1 = default(T1);

  }

  try{

  param2 = (T2)o2; } catch{

  param2 = default(T2);

  }

  try{

  param3 = (T3)o3; } catch{

  param3 = default(T3);

  }

  action(param1,

  param2, param3);

  returntrue;

  };

  mTranslationEvents.Add(eventName,

  newEvent);

  returnthis;

  }

  }

  接下来,我会在设计四中给大家介绍FSEvent类的实现,敬请期待。


回复

使用道具 举报

6蛮牛粉丝
1197/1500
排名
2370
昨日变化
2

20

主题

393

帖子

1197

积分

Rank: 6Rank: 6Rank: 6

UID
99528
好友
1
蛮牛币
18170
威望
0
注册时间
2015-5-11
在线时间
434 小时
最后登录
2017-6-14
QQ
发表于 2016-7-11 19:27:59 | 显示全部楼层
好乱~~~~~~~~~~~~~~~~~

回复

使用道具 举报

3偶尔光临
216/300
排名
10602
昨日变化
12

1

主题

41

帖子

216

积分

Rank: 3Rank: 3Rank: 3

UID
97957
好友
0
蛮牛币
145
威望
0
注册时间
2015-5-6
在线时间
112 小时
最后登录
2017-7-24
发表于 2016-9-1 19:47:16 | 显示全部楼层
这么乱怎么看

回复

使用道具 举报

排名
11546
昨日变化
6

0

主题

12

帖子

91

积分

Rank: 2Rank: 2

UID
111548
好友
0
蛮牛币
267
威望
0
注册时间
2015-7-1
在线时间
27 小时
最后登录
2017-3-2
发表于 2016-9-28 16:43:49 | 显示全部楼层
没有耐心看下去了,没有排版没有注释

回复 支持 反对

使用道具 举报

4四处流浪
360/500
排名
7601
昨日变化
3

0

主题

141

帖子

360

积分

Rank: 4

UID
129533
好友
0
蛮牛币
446
威望
0
注册时间
2015-11-21
在线时间
109 小时
最后登录
2017-5-16
发表于 2016-12-9 15:42:34 | 显示全部楼层
楼主良心,感谢楼主
[发帖际遇]: 一个袋子砸在了 somnacin 头上,somnacin 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

0

主题

1

帖子

2

积分

Rank: 1

UID
205142
好友
0
蛮牛币
14
威望
0
注册时间
2017-2-6
在线时间
1 小时
最后登录
2017-2-6
发表于 2017-2-6 00:48:37 | 显示全部楼层
源码不提供一下吗??很好啊!

回复 支持 反对

使用道具 举报

5熟悉之中
659/1000
排名
5249
昨日变化
34

10

主题

375

帖子

659

积分

Rank: 5Rank: 5

UID
206456
好友
0
蛮牛币
862
威望
0
注册时间
2017-2-14
在线时间
134 小时
最后登录
2017-7-24
发表于 2017-3-2 08:47:09 | 显示全部楼层
乱乱的,怎么搞?

回复

使用道具 举报

0

主题

14

帖子

17

积分

Rank: 1

UID
209237
好友
0
蛮牛币
35
威望
0
注册时间
2017-3-1
在线时间
4 小时
最后登录
2017-4-19
发表于 2017-3-25 09:40:11 | 显示全部楼层
感谢楼主分享,麻烦整理整理整理代码呗

回复 支持 反对

使用道具 举报

3偶尔光临
192/300
排名
8542
昨日变化
6

0

主题

53

帖子

192

积分

Rank: 3Rank: 3Rank: 3

UID
195174
好友
0
蛮牛币
368
威望
0
注册时间
2016-12-22
在线时间
47 小时
最后登录
2017-7-21
发表于 2017-4-7 20:34:02 | 显示全部楼层
FSM的编程思想很精髓

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表