Unity中FSM有限状态机
什么是FSM
FSM 即有限状态机,它是一个状态管理系统,表示一个对象的几种状态在指定条件下转移行为,即随着条件的不断改变内部状态不断地切换。
FSM用处或者使用背景
通常使用FSM去实现一些简单的AI逻辑,对于游戏中的每个对象都可以在其生命周期中分出一些状态,比如一个小兵,他可能在休息,或者巡逻,当敌人出现时,他的状态可能切换为追逐敌人或者攻击敌人,当某些条件成立时,状态机从当前状态转移到下一状态,在不同状态下有不同的任务,所以要使用有限状态机去实现。
FSM使用的必要性
当需要实现角色的状态时以及状态间的切换时,相信第一时间想到的应该是if-else,但是如果状态的切换条件表达式过于复杂,if-else就显得臃肿麻烦了。再加上所有条件的判断全在一起,状态一多也容易出现Bug,扩展也不好,使用if-else就会相当臃肿,所以在这种条件下,有限状态机的使用很有必要,当然如果相对简单的状态切换,使用有限状态机就没必要了,需要根据个人需求。
FSM使用注意点
FSM有两个重要的概念:状态和转移,转移是切换条件,必须有一个初始状态,并保存当前状态,以及注意每种状态的转移的必要条件。
FSM优点
使整个状态切换逻辑比较清晰,增强代码的可扩展性,也便于后期维护。
具体实现代码
下面就实现一下FSM,使用怪物AI的例子
1.创建状态基类FSMstate ,此类负责处理一个状态的周期,状态的进入前,状态中,离开状态等。以及状态切换条件的增删。具体如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 状态ID /// </summary> public enum StateID { NoneStateID, Parol,//巡逻状态 Chase,//追逐状态 } /// <summary> /// 状态切换条件 /// </summary> public enum Transition { NoneTransition, SeePlayer,//看到玩家 LosePlayer,//看不到玩家 } /// <summary> /// FSM中状态基类(实现状态的基本方法) /// </summary> public abstract class FSMstate { protected StateID stateID;//状态对应的ID public StateID ID { get { return stateID; } } protected Dictionary<Transition, StateID> Transition_StateIDDic = new Dictionary<Transition, StateID>();//存储转换条件和状态的ID protected FSMsystem fsmSystem;//管理状态对象(因为要状态更新需要通过FSMsystem去管理实现的,所以需要一个管理对象) public FSMstate(FSMsystem fsm) { this.fsmSystem = fsm; } /// <summary> ///增加转条件 /// </summary> /// <param name="trans"></param> /// <param name="id"></param> public void AddTransition(Transition trans,StateID id) { if (trans==Transition.NoneTransition) { Debug.Log("添加的转换条件不能为null"); return; } if (id==StateID.NoneStateID) { Debug.Log("添加的状态ID不能为null"); return; } if (Transition_StateIDDic.ContainsKey(trans)) { Debug.Log("添加转换条件的时候," + trans + "已经存在于Transition_StateIDDic中"); return; } Transition_StateIDDic.Add(trans,id); } /// <summary> /// 删除转换条件 /// </summary> /// <param name="trans"></param> public void DeleteTransition(Transition trans) { if (trans == Transition.NoneTransition) { Debug.Log("删除的转换条件不能为null"); return; } if (!Transition_StateIDDic.ContainsKey(trans)) { Debug.Log("删除转换条件的时候," + trans + "不存在于Transition_StateIDDic中"); return; } Transition_StateIDDic.Remove(trans); } /// <summary> /// 根据转换条件获得状态ID /// </summary> /// <param name="trans"></param> /// <returns></returns> public StateID GetStateID(Transition trans) { if (Transition_StateIDDic.ContainsKey(trans)) { return Transition_StateIDDic[trans]; } return StateID.NoneStateID; } /// <summary> ///转换到此状态前要执行的逻辑 /// </summary> public virtual void DoBeforeEnterAcion() { } /// <summary> /// 离开此状态前要执行的逻辑 /// </summary> public virtual void DoAfterLevAction(){ } /// <summary> /// 处在本状态时要执行的逻辑 /// </summary> /// <param name="TargetObj"></param> public abstract void CurrStateAction(GameObject TargetObj); /// <summary> /// 切换到下一状态需要执行的逻辑 /// </summary> /// <param name="TargetObj"></param> public abstract void NextStateAction(GameObject TargetObj); }
2. 创建状态管理类FSMSystem的创建。用来管理所有的状态(状态的添加,删除,切换,更新等)。
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 状态处理类(添加,删除,切换,更新等管理所有状态) /// </summary> public class FSMsystem { private Dictionary<StateID, FSMstate> StateDic = new Dictionary<StateID, FSMstate>();//保存状态ID以及ID对应的状态 private StateID _CurrentStateID;//当前处于的状态ID private FSMstate _CurrentState;//当前处于的状态 /// <summary> /// 添加状态 /// </summary> /// <param name="state">需管理的状态</param> public void AddState(FSMstate state) { if (state==null) { Debug.Log("添加的状态"+state+"不能为null"); return; } if (_CurrentState==null) { _CurrentState = state; _CurrentStateID = state.ID; } if (StateDic.ContainsKey(state.ID)) { Debug.Log("状态机 "+state.ID+"已经存在,无法添加"); return; } StateDic.Add(state.ID,state); } /// <summary> /// 删除状态 /// </summary> /// <param name="stateID">删除要管理状态的ID</param> public void DeleteState(StateID stateID) { if (stateID==StateID.NoneStateID) { Debug.Log("无法删除Null的状态"); return; } if (!StateDic.ContainsKey(stateID) ) { Debug.Log("无法删除不存在的状态:" + stateID); return; } StateDic.Remove(stateID); } /// <summary> /// 状态转换(状态的切换是根据转换条件的变化) /// </summary> /// <param name="trans">转换条件</param> public void PerformTranstion(Transition trans) { if (trans == Transition.NoneTransition) { Debug.Log("无法执行NULL的转换条件"); return; } StateID stateId = _CurrentState.GetStateID(trans); if (stateId==StateID.NoneStateID) { Debug.Log("要转换的状态ID为null"); return; } if (!StateDic.ContainsKey(stateId)) { Debug.Log("状态机中没找到状态ID "+stateId+" 无法转换状态"); return; } FSMstate state = StateDic[stateId];//根据状态ID获取要转换的状态 _CurrentState.DoAfterLevAction();//执行离开上一状态逻辑 _CurrentState = state;//更新当前状态 _CurrentStateID = stateId;//更新当前状态ID _CurrentState.DoBeforeEnterAcion();//执行进入当前状态前要执行的逻辑 } /// <summary> /// 更新当前状态行为 /// </summary> /// <param name="TargetObj"></param> public void UpdateState(GameObject TargetObj) { _CurrentState.CurrStateAction(TargetObj); _CurrentState.NextStateAction(TargetObj); } }
3.那我们用怪物巡逻,追逐玩家的例子来实现状态机。
3.1. 增加一个PartalState类,用怪物的巡逻。
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 怪物巡逻状态类 /// </summary> public class PatrolState : FSMstate { private List<Transform> path = new List<Transform>();//巡逻点 private int index = 0; private Transform PlayerTrasform; /// <summary> /// 初始化巡逻状态数据 /// </summary> /// <param name="fsm"></param> public PatrolState(FSMsystem fsm):base(fsm) { stateID = StateID.Parol; //路点 Transform pathTransform = GameObject.Find("Path").transform; Transform[] children = pathTransform.GetComponentsInChildren<Transform>(); foreach (Transform child in children) { if (child != pathTransform) { path.Add(child); } } PlayerTrasform = GameObject.Find("Player").transform; } /// <summary> /// 当前状态(巡逻状态)执行的逻辑 /// </summary> /// <param name="TargetObj"></param> public override void CurrStateAction(GameObject TargetObj) { TargetObj.transform.LookAt(path[index].position); TargetObj.transform.Translate(Vector3.forward * Time.deltaTime * 3); if (Vector3.Distance(TargetObj.transform.position, path[index].position) < 1) { index++; index %= path.Count; } } /// <summary> /// 切换到追逐状态(下一状态)执行的的逻辑 /// </summary> /// <param name="TargetObj"></param> public override void NextStateAction(GameObject TargetObj) { if (Vector3.Distance(PlayerTrasform.position, TargetObj.transform.position) < 3) { fsmSystem.PerformTranstion(Transition.SeePlayer); } } }
3.2.增加一个ChaseState类,用怪物的追逐。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 追逐状态类 /// </summary> public class ChaseState : FSMstate { private Transform PlayerTransForm;//玩家位置信息 public ChaseState(FSMsystem fsm):base(fsm) { stateID = StateID.Chase; PlayerTransForm = GameObject.Find("Player").transform; } /// <summary> /// 追逐状态下执行的逻辑 /// </summary> /// <param name="targrtObj"></param> public override void CurrStateAction(GameObject targrtObj) { targrtObj.transform.LookAt(PlayerTransForm.transform.position); targrtObj.transform.Translate(Vector3.forward*2*Time.deltaTime); } /// <summary> /// 切换到下一状态(巡逻)前要执行的逻辑 /// </summary> /// <param name="targrtObj"></param> public override void NextStateAction(GameObject targrtObj) { if (Vector3.Distance(PlayerTransForm.position,targrtObj.transform.position)>6) { fsmSystem.PerformTranstion(Transition.LosePlayer); } } }
3.3 创建一个控制怪物的脚本Enemy
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 怪物类 /// </summary> public class Enemy : MonoBehaviour { private FSMsystem fsmsystem; //在Enemy类中,实例化FSMSystem对象,添加巡逻和追逐状态,还有之间的转换条件 void Start () { InitFsm();//创建状态机 } void Update () { fsmsystem.UpdateState(this.gameObject);//检查更新状态 } /// <summary> /// 创建状态机 /// 怪物有两种状态分别是巡逻和追逐玩家 /// 如果怪物初始状态(设置为Parol状态)一旦SeePlayer 切换状态被激活后,就切换到Chase状态 /// 如果他在Chase状态一旦LosePlayer状态被激活了,它就转变到Parol状态 /// </summary> void InitFsm() { fsmsystem = new FSMsystem(); FSMstate PatrolState = new PatrolState(fsmsystem); PatrolState.AddTransition(Transition.SeePlayer, StateID.Chase); FSMstate ChaseState = new ChaseState(fsmsystem); ChaseState.AddTransition(Transition.LosePlayer, StateID.Parol); fsmsystem.AddState(PatrolState);//初始状态 fsmsystem.AddState(ChaseState); } }
以上就用FSM有限状态机实现了怪物AI,具体代码中都有注释,有理解不到位的地方,望请指正。
- unity游戏开发中的有限状态机FSM经验
- 在Unity中实现FSM有限状态机
- Unity 有限状态机FSM原理剖析
- Unity中使用有限状态机(FSM)来管理UI面板的切换(UGUI)
- Unity FSM 有限状态机
- cocos2d-x 游戏开发之有限状态机(FSM) (三)
- 有限状态机(FSM)的设计与实现(二)
- Unity3D架构系列之- FSM有限状态机设计(四)
- 实现简易而强大的游戏AI——FSM,有限状态机
- Unity有限状态机详解
- Unity_设计模式_有限状态机_010
- Unity3D架构系列之- FSM有限状态机设计一
- Unity3D有限状态机(FSM)学习笔记【7】使用实例
- FSM有限状态机(一)
- Unity3D架构系列之- FSM有限状态机设计五
- [python]有限状态机(FSM)简单实现
- 有限状态机FSM的写法
- U3D架构系列之- FSM有限状态机设计四
- U3D架构系列之- FSM有限状态机设计五