引言:近日忙于毕业论文,今天看到涨了3个粉丝,不甚惊喜。遂今日更新FSM有限状态机学习,希望大家共同进步!
开发版本:Unity 2017.1.1f1、VS 2017
适合人群:初学Unity者
一.有限状态机定义
有限状态机(英文:Finite State Machine,简称FSM),是指在不同阶段呈现不同的运行状态的系统,这些状态是有限的,不重叠的。这样的系统在某一时刻一定会处于其中所有状态中的一个状态,此时它接收一部分的输入,产生相应的响应,并且迁移到可能的状态。
一般有限状态机有一下几种类型的动作:
1.进入动作:进入当前状态时执行
2.退出动作:退出当前状态时执行
3.输入动作:在当前状态时执行
4.转移动作:在进行特定切换状态时执行
有限状态机提供了描述和控制应用逻辑的强大方法,可以应用于控制NPC行为,图形界面管理等方面。FSM可以使得各个状态之间相互独立,互不影响,避免了状态与状态之间的耦合度,具有规则简单,可读性和可验证性等优点。
二.整理思路
简单的有限状态机就是switch语句,但如果状态过多的情况下,switch会使得代码显得臃肿,可扩展性很低。
假设敌人有四种状态,Idle、Patrol、Chase、Attack,switch实现方法如下:
public enum States
{
Idle,//空闲状态
Patrol,//巡逻状态
Chase,//追逐状态
Attack//攻击状态
}
public class SimpleFSM : MonoBehaviour
{
private States currentState = States.Idle;
private void Update()
{
switch (currentState)
{
case States.Idle:
//TODO 空闲状态动作
break;
case States.Patrol:
//TODO 巡逻状态动作
break;
case States.Chase:
//TODO 追逐状态动作
break;
case States.Attack:
//TODO 攻击状态动作
break;
}
}
}
接下来整理“高级的”有限状态机实现方法,FSM由状态类和管理类组成,先创建一个状态基类FSMState,用于实现状态类的基本方法,每添加一种状态,就作为它的子类。然后,用一个管理类FSMSystem把所有状态管理起来,方便使用状态机。FSM类图如下所示:
三.实现代码
1.定义枚举类型
public enum StateID
{
NullStateID,
}
public enum Transition
{
NullTransition
}
定义每一种状态的ID和状态之间的转换条件
2.FSMState基类
public abstract class FSMState
{
protected StateID stateID;
public StateID ID { get { return this.stateID; } }
protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
protected FSMSystem fsm;
public FSMState(FSMSystem fsm)
{
this.fsm = fsm;
}
/// <summary>
/// 添加转换条件
/// </summary>
/// <param name="trans">转换条件</param>
/// <param name="stateID">转换的目标状态</param>
public void AddTransition(Transition trans, StateID stateID)
{
if (trans == Transition.NullTransition)
{
Debug.LogError(trans + "为空,转换条件不允许为空");return;
}
if (stateID == StateID.NullStateID)
{
Debug.LogError(stateID + "为空,状态ID不允许为空"); return;
}
if (map.ContainsKey(trans))
{
Debug.LogError(trans + "已经存在,请查看该转换条件是否正确");
}
else
{
map.Add(trans, stateID);
}
}
/// <summary>
/// 删除转换条件
/// </summary>
/// <param name="trans">需删除的转换条件</param>
public void DeleteTransition(Transition trans)
{
if (trans == Transition.NullTransition)
{
Debug.LogError(trans + "为空,转换条件不允许为空"); return;
}
if (map.ContainsKey(trans) == false)
{
Debug.LogError(trans + "不存在,请查看该转换条件是否正确");
}
else
{
map.Remove(trans);
}
}
/// <summary>
/// 通过转换条件,得到目标状态
/// </summary>
/// <param name="trans">转换条件</param>
/// <returns>返回目标状态</returns>
public StateID GetTargetStateID(Transition trans)
{
if (map.ContainsKey(trans) == false)
{
Debug.LogError(trans + "不存在,请查看该转换条件是否正确");
return StateID.NullStateID;
}
else
{
return map[trans];
}
}
public virtual void DoBeforeEntering() { }//进入动作
public virtual void DoAfterLeaving() { }//离开动作
public abstract void Act();//输入动作
public abstract void Reason();//转移动作
}
状态类主要存储转换条件和转换状态的字典,可以添加删除转换条件,根据条件返回对应的状态,并定义状态类的四种动作。
3.FSMSystem管理类
public class FSMSystem
{
private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>();
private FSMState currentState;
/// <summary>
/// 更新当前状态行为
/// </summary>
public void UpdateFSM()
{
currentState.Act();
currentState.Reason();
}
/// <summary>
/// 添加状态
/// </summary>
/// <param name="state">需管理的状态</param>
public void AddState(FSMState state)
{
if (state == null)
{
Debug.LogError(state + "为空"); return;
}
if (currentState == null)
{
currentState = state;
}
if (states.ContainsValue(state))
{
Debug.LogError(state + "已经存在");
}
else
{
states.Add(state.ID, state);
}
}
/// <summary>
/// 删除状态
/// </summary>
/// <param name="id">需要删除状态的ID</param>
/// /// <returns>删除成功返回true,否则返回false</returns>
public bool DeleteState(StateID id)
{
if (id == StateID.NullStateID)
{
Debug.LogError(id + "为空");
return false;
}
if (states.ContainsKey(id) == false)
{
Debug.LogError(id + "不存在");
return false;
}
else
{
states.Remove(id);
return true;
}
}
/// <summary>
/// 执行转换
/// </summary>
/// <param name="trans">转换条件</param>
public void PerformTransition(Transition trans)
{
if (trans == Transition.NullTransition)
{
Debug.LogError(trans + "为空");return;
}
StateID targetID = currentState.GetTargetStateID(trans);
if (states.ContainsKey(targetID) == false)
{
Debug.LogError(targetID + "不存在");return;
}
FSMState targetState = states[targetID];
currentState.DoAfterLeaving();
targetState.DoBeforeEntering();
currentState = targetState;
}
}
FSMSystem用来管理各个状态,字典存储ID和对应的状态,可以添加删除状态,并执行状态转换。
四.案例讲解
假设敌人按照路线巡逻,当发现玩家的时候,开始追逐。但玩家跑出追击范围的时候,敌人继续回去巡逻。
项目的源文件在文末,有需要的童鞋可以自行下载。
敌人巡逻效果如下:
敌人发现玩家开始追逐,当玩家逃离后,返回继续巡逻,效果如下:
分析此时敌人状态有两种,巡逻和追逐,转换条件也对应有两种
public enum StateID
{
NullStateID,
PatrolState,
ChaseState
}
public enum Transition
{
NullTransition,
FindPlayer,
LosePlayer
}
创建两个子类PatrolState和ChaseState都继承自FSMState,分别用来实现巡逻和追逐的状态行为。
在Enemy类中,实例化FSMSystem对象,添加巡逻和追逐状态,还有之间的转换条件
public class Enemy : MonoBehaviour
{
private FSMSystem fsm;
private void Start()
{
fsm = new FSMSystem();
FSMState patrolState = new PatrolState(fsm);
FSMState chaseState = new ChaseState(fsm);
fsm.AddState(patrolState);
fsm.AddState(chaseState);
patrolState.AddTransition(Transition.FindPlayer, StateID.ChaseState);
chaseState.AddTransition(Transition.LosePlayer, StateID.PatrolState);
}
private void Update()
{
fsm.UpdateFSM();
}
}
FSM有限状态机除了实现敌人的行为外,还可以用来管理UI界面的切换,使用方式基本相似。如果大神发现我分享的学习记录中有错误,还请不吝赐教。
案例百度云链接:链接:https://pan.baidu.com/s/1MVpKQ5U5eJE7zJTMXSMj3A 提取码:coh5
觉得我分享的学习记录不错的,点赞关注我哦!