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

 

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

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

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

http://s3.51cto.com/wyfs02/M02/59/1E/wKiom1THrgnDFZcsAAOShIcUaH0197.jpg

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

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


using UnityEngine;
using System.Collections;

public interface IState {
void OnEnter(string prevState);
void OnExit(string nextState);
void OnUpdate();
}

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

我们把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函数。代码如下:


public void Push(string newState) {
string lastName = null;
if (mStateStack.Count > 1) {
lastName = mStateStack.Peek().StateName;
}
Push(newState, lastName);
}
protected void Push(string stateName, string lastStateName) {
mStateStack.Push(mStates[stateName]);
mStateStack.Peek().StateObject.OnEnter(lastStateName);
}
public void Pop() {
Pop(null);
}
protected string Pop(string newName) {
FSState lastState = mStateStack.Peek();
string newState = null;
if (newName == null && mStateStack.Count > 1) {
int index = 0;
foreach (FSState item in mStateStack) {
if (index++ == mStateStack.Count - 2) {
newState = item.StateName;
}
}
}
else {
newState = newName;
}
string lastStateName = null;
if (lastState != null) {
lastStateName = lastState.StateName;
lastState.StateObject.OnExit(newState);
}
mStateStack.Pop();
return lastStateName;
}

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


public void Trigger(string eventName) {
CurrentState.Trigger(eventName);
}
public void Trigger(string eventName, object param1) {
CurrentState.Trigger(eventName, param1);
}

public void Trigger(string eventName, object param1, object param2) {
CurrentState.Trigger(eventName, param1, param2);
}

public void Trigger(string eventName, object param1, object param2, object param3) {
CurrentState.Trigger(eventName, param1, param2, param3);
}

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


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class FiniteStateMachine {
public delegate void EnterState(string stateName);
public delegate void PushState(string stateName, string lastStateName);
public delegate void PopState();
protected Dictionary<string, FSState> mStates;
protected string      mEntryPoint;
protected Stack<FSState>    mStateStack;
public FiniteStateMachine() {
mStates = new Dictionary<string, FSState>();
mStateStack = new Stack<FSState>();
mEntryPoint = null;
}
public void Update() {
if (CurrentState == null) {
mStateStack.Push(mStates[mEntryPoint]);
CurrentState.StateObject.OnEnter(null);
}
CurrentState.StateObject.OnUpdate();
}
public void Register(string stateName, IState stateObject) {
if (mStates.Count == 0)
mEntryPoint = stateName;
mStates.Add(stateName, new FSState(stateObject, this, stateName, Enter, Push, Pop));
}
public FSState State(string stateName) {
return mStates[stateName];
}
public void EntryPoint(string startName) {
mEntryPoint = startName;
}
public FSState CurrentState {
get {
if (mStateStack.Count == 0)
return null;
return mStateStack.Peek();
}
}
public void Enter(string stateName) {
Push(stateName, Pop(stateName));
}
public void Push(string newState) {
string lastName = null;
if (mStateStack.Count > 1) {
lastName = mStateStack.Peek().StateName;
}
Push(newState, lastName);
}
protected void Push(string stateName, string lastStateName) {
mStateStack.Push(mStates[stateName]);
mStateStack.Peek().StateObject.OnEnter(lastStateName);
}
public void Pop() {
Pop(null);
}
protected string Pop(string newName) {
FSState lastState = mStateStack.Peek();
string newState = null;
if (newName == null && mStateStack.Count > 1) {
int index = 0;
foreach (FSState item in mStateStack) {
if (index++ == mStateStack.Count - 2) {
newState = item.StateName;
}
}
}
else {
newState = newName;
}
string lastStateName = null;
if (lastState != null) {
lastStateName = lastState.StateName;
lastState.StateObject.OnExit(newState);
}
mStateStack.Pop();
return lastStateName;
}
public void Trigger(string eventName) {
CurrentState.Trigger(eventName);
}
public void Trigger(string eventName, object param1) {
CurrentState.Trigger(eventName, param1);
}

public void Trigger(string eventName, object param1, object param2) {
CurrentState.Trigger(eventName, param1, param2);
}

public void Trigger(string eventName, object param1, object param2, object param3) {
CurrentState.Trigger(eventName, param1, param2, param3);
}
}

上面整个FSMStateMachine类就封装完了,由于时间关系本篇文章到此结束,下一篇我们继续

作者:大洋    博客:http://jxwgame.blog.51cto.com