【Unity】Finite State Machine 有限状态机代码注释解析
发表于2018-01-06
本篇文章是翻译了Unity官方Wiki有限状态机的代码注释部分。出于学习目的,修改了有限状态机中部分注释和调试输出等内容,希望可以有助于大家去理解有限状态机。
(版权归原文所有,仅供大家学习使用。)
FSMSystem.cs
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 脚本功能:状态机系统,用于控制战斗系统中角色和敌人的状态转换 /// 添加对象:无 /// 创建时间:2015年6月12日16:39:02 /// 知识要点: /// 1. 状态机框架 /// 2. 虚方法 /// 3. 抽象类、抽象方法 /// 4. 枚举 /// 补充说明: /// 在回合制战斗系统中,状态的转换比较规整,状态机体现的优势不大。 /// 对于多人不同时的半回合制战斗系统,状态机将发挥一点作用。 /// 对于行为复杂的实时战斗系统,状态机将发挥重要作用。 /// </summary> // 状态转换 public enum Transition { NullTransition = 0, // 系统中不存在转换 // 需要根据实际应用继续扩展 } // 状态ID public enum StateID { NullStateID = 0, // 系统中不存在状态 PlayerMove = 2, // 玩家战斗移动状态 PlayerAttack = 4, // 玩家战斗攻击状态 EnemyMove = 6, // 敌人战斗移动状态 EnemyAttack = 8, // 敌人战斗攻击状态 } // 转换信息类: public class TransInfo { public string comment; } /// <summary> /// 状态机中的状态类:以字典的形式保存状态的转换 /// 1. Reason() 决定触发哪个转换 /// 2. Act() 决定NPC在当前状态下的行为 /// </summary> public abstract class FSMState { protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>(); // 状态转换映射 protected StateID stateID; // 状态ID public StateID ID { get { return stateID; } } // 获取当前状态ID /// 添加转换 public void AddTransition(Transition trans, StateID id) { // 空值检验 if (trans == Transition.NullTransition) { Debug.LogError("FSMState ERROR: 不能添加空转换"); return; } if (id == StateID.NullStateID) { Debug.LogError("FSMState ERROR: 不能添加空状态"); return; } // 检查是否已经有该转换 if (map.ContainsKey(trans)) { Debug.LogError("FSMState ERROR: 状态 " + stateID.ToString() + " 已经包含转换 " + trans.ToString() + "不可添加另一个状态"); return; } map.Add(trans, id); } /// 删除状态转换 public void DeleteTransition(Transition trans) { // 空值检验 if (trans == Transition.NullTransition) { Debug.LogError("FSMState ERROR: 不能删除空转换"); return; } // 检验是否有配对的转换 if (map.ContainsKey(trans)) { map.Remove(trans); return; } Debug.LogError("FSMState ERROR: 转换 " + trans.ToString() + " - 状态 " + stateID.ToString() + " 不存在"); } /// 获取下一个状态 public StateID GetOutputState(Transition trans) { // 如果存在转换,返回对应状态 if (map.ContainsKey(trans)) { return map[trans]; } return StateID.NullStateID; } /// 进入状态之前执行 public virtual void DoBeforeEntering() { } /// 离开状态之前执行 public virtual void DoBeforeLeaving() { } /// 状态转换条件 public abstract void Reason(GameObject player, GameObject npc); /// 控制行为 public abstract void Act(GameObject player, GameObject npc); } /// <summary> /// 状态机类:包含状态列表 /// 1. 删除状态 /// 2. 改变当前状态 /// </summary> public class FSMSystem { private List<FSMState> states; // 状态列表 // 在状态机中改变当前状态的唯一途径是通过转换,当前状态不可直接改变 private StateID currentStateID; public StateID CurrentStateID { get { return currentStateID; } } private FSMState currentState; public FSMState CurrentState { get { return currentState; } } public FSMSystem() { states = new List<FSMState>(); } /// 添加状态 public void AddState(FSMState s) { // 空值检验 if (s == null) { Debug.LogError("FSM ERROR: 不可添加空状态"); } // 当所添加状态为初始状态 if (states.Count == 0) { states.Add(s); currentState = s; currentStateID = s.ID; return; } // 遍历状态列表,若不存在该状态,则添加 foreach (FSMState state in states) { if (state.ID == s.ID) { Debug.LogError("FSM ERROR: 无法添加状态 " + s.ID.ToString() + " 因为该状态已存在"); return; } } states.Add(s); } /// 删除状态 public void DeleteState(StateID id) { // 空值检验 if (id == StateID.NullStateID) { Debug.LogError("FSM ERROR: 状态ID 不可为空ID"); return; } // 遍历并删除状态 foreach (FSMState state in states) { if (state.ID == id) { states.Remove(state); return; } } Debug.LogError("FSM ERROR: 无法删除状态 " + id.ToString() + ". 状态列表中不存在"); } /// 执行转换 public void PerformTransition(Transition trans) { // 空值检验 if (trans == Transition.NullTransition) { Debug.LogError("FSM ERROR: 转换不可为空"); return; } // 获取当前状态ID StateID id = currentState.GetOutputState(trans); if (id == StateID.NullStateID) { Debug.LogError("FSM ERROR: 状态 " + currentStateID.ToString() + " 不存在目标状态 " + " - 转换: " + trans.ToString()); return; } // 更新当前状态ID 与 当前状态 currentStateID = id; foreach (FSMState state in states) { if (state.ID == currentStateID) { // 执行当前状态后处理 currentState.DoBeforeLeaving(); currentState = state; // 执行当前状态前处理 currentState.DoBeforeEntering(); break; } } } }