Unity 一个比较适合学习的FSM状态机(汉化和功能简述)

        该轮子由网络资源而来,遵从作者开源意愿,仅作免费学习和分享,不作任何商业行为 ,本文不支持任何交易行为,侵权删!!!

        至于我为什么不将此文章设置为转载,是因为该代码所在的网站早在2020年前就已经关闭了,也就是该资源是祖传的,很有意思吧

 1.总览

请无视枚举中多出来的内容

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/**
基于 Eric Dybsand 在《Game Programming Gems 1》第3.1章中的有限状态机系统

作者:Roberto Cezar Bianchini,2010年7月

使用方法:
    1. 在相应的枚举中放置有限状态系统的转换和状态标签。

    2. 编写继承自 FSMState 的新类,并在每个类中填充(转换-状态)对。
       这些对表示当 FSMSystem 处于状态 S1 时,如果触发了转换 T,
       且状态 S1 有从 T 到 S2 的转换,FSMSystem 应该处于状态 S2。
       请记住,这是一个确定性 FSM。一个转换不能导致两个不同的状态。

       方法 Reason 用于确定应该触发哪个转换。
       你可以在其他地方编写触发转换的代码,并将此方法留空,
       如果你觉得这更适合你的项目。

       方法 Act 包含 NPC 在此状态下应该执行的动作代码。
       你可以在其他地方编写动作代码,并将此方法留空,
       如果你觉得这更适合你的项目。

    3. 创建 FSMSystem 类的实例,并添加状态。

    4. 从你的 Update 或 FixedUpdate 方法中调用 Reason 和 Act(或你用于触发转换和
       使 NPC 在游戏中表现的任何方法)。

    Unity 引擎的异步转换,如 OnTriggerEnter、SendMessage,也可以使用,
    只需在事件发生时从你的 FSMSystem 实例中调用 PerformTransition 方法,并传入正确的转换。

软件按“原样”提供,不提供任何形式的明示或暗示担保,
包括但不限于适销性、特定用途适用性和非侵权的担保。
在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,
无论是在合同诉讼、侵权行为或其他情况下,
由软件或软件使用或其他交易引起或与之相关。
*/



/// <summary>
/// 在这个枚举中放置转换的标签。
/// 不要更改第一个标签 NullTransition,因为 FSMSystem 类使用它。
/// </summary>
public enum Transition {
    NullTransition = 0, // 使用这个转换来表示系统中不存在的转换
    StartButtonClick,
    PauseButtonClick
}


/// <summary>
/// 在这个枚举中放置状态的标签。
/// 不要更改第一个标签 NullStateID,因为 FSMSystem 类使用它。
/// </summary>
public enum StateID {
    NullStateID = 0, // 使用这个 ID 来表示系统中不存在的状态
    Menu,
    Play,
    Pause,
    GameOver
}


/// <summary>
/// 这个类代表有限状态系统中的状态。
/// 每个状态都有一个包含(转换-状态)对的字典,
/// 显示当这个状态是当前状态时,如果触发了转换,
/// FSM 应该处于哪个状态。
/// 方法 Reason 用于确定应该触发哪个转换。
/// 方法 Act 包含 NPC 在这个状态下应该执行的动作代码。
/// </summary>
public abstract class FSMState : MonoBehaviour {
    protected Ctrl ctrl;
    public Ctrl CTRL {
        set {
            ctrl = value;
        }
    }
    protected FSMSystem fsm;
    public FSMSystem FSM {
        set {
            fsm = value;
        }
    }
    protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
    protected StateID stateID;
    public StateID ID {
        get {
            return stateID;
        }
    }

    public void AddTransition(Transition trans, StateID id) {
        // 检查参数是否无效
        if (trans == Transition.NullTransition) {
            Debug.LogError("FSMState 错误:NullTransition 不允许作为真实的转换");
            return;
        }

        if (id == StateID.NullStateID) {
            Debug.LogError("FSMState 错误:NullStateID 不允许作为真实的 ID");
            return;
        }

        // 由于这是一个确定性 FSM,
        // 检查当前转换是否已经在字典中
        if (map.ContainsKey(trans)) {
            Debug.LogError("FSMState 错误:状态 " + stateID.ToString() + " 已经有转换 " + trans.ToString() +
                           "无法分配给另一个状态");
            return;
        }

        map.Add(trans, id);
    }


    /// <summary>
    /// 这个方法从该状态的映射中删除一个转换-状态对。
    /// 如果转换不在该状态的映射中,将打印错误信息。
    /// </summary>
    public void DeleteTransition(Transition trans) {
        // 检查 NullTransition
        if (trans == Transition.NullTransition) {
            Debug.LogError("FSMState 错误:不允许使用 NullTransition");
            return;
        }

        // 在删除之前检查该对是否在映射中
        if (map.ContainsKey(trans)) {
            map.Remove(trans);
            return;
        }
        Debug.LogError("FSMState 错误:传递给 " + stateID.ToString() + " 的转换 " + trans.ToString() +
                       " 不在该状态的转换列表中");
    }


    /// <summary>
    /// 如果这个状态接收到一个转换,这个方法返回 FSM 应该处于的新状态
    /// </summary>
    public StateID GetOutputState(Transition trans) {
        // 检查映射中是否有这个转换
        if (map.ContainsKey(trans)) {
            return map[trans];
        }
        return StateID.NullStateID;
    }


    /// <summary>
    /// 这个方法用于在进入状态之前设置状态条件。
    /// 它在 FSMSystem 类将其分配为当前状态之前自动调用。
    /// </summary>
    public virtual void DoBeforeEntering() {
    }

    /// <summary>
    /// 这个方法用于在 FSMSystem 切换到另一个状态之前,
    /// 执行任何必要的操作,如重置变量。
    /// 它在 FSMSystem 切换到新状态之前自动调用。
    /// </summary>
    public virtual void DoBeforeLeaving() {
    }

    /// <summary>
    /// 这个方法决定状态是否应该转换到其列表中的另一个状态。
    /// NPC 是一个引用,指向由这个类控制的对象。
    /// </summary>
    public virtual void Reason() {
    }

    /// <summary>
    /// 这个方法控制 NPC 在游戏世界中的行为。
    /// NPC 执行的每个动作、移动或交流都应该放在这里。
    /// NPC 是一个引用,指向由这个类控制的对象。
    /// </summary>
    public virtual void Act() {
    }


} // class FSMState


/// <summary>
/// FSMSystem 类代表有限状态机类。
/// 它有一个包含 NPC 状态的列表和添加、删除状态以及更改当前状态的方法。
/// </summary>
public class FSMSystem {
    private List<FSMState> states;

    // 改变 FSM 状态的唯一方法是执行转换
    // 不要直接更改 CurrentState
    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 SetCurrentState(FSMState s) {
        currentState = s;
        currentStateID = s.ID;
        s.DoBeforeEntering();
    }

    /// <summary>
    /// 这个方法将新状态放入 FSM 中,
    /// 如果状态已经在列表中,则打印错误信息。
    /// 添加的第一个状态也是初始状态。
    /// </summary>
    public void AddState(FSMState s, Ctrl ctrl) {
        // 在删除之前检查空引用
        if (s == null) {
            Debug.LogError("FSM 错误:不允许空引用");
        }
        s.FSM = this;
        s.CTRL = ctrl;
        // 插入的第一个状态也是初始状态,
        // 模拟开始时机器所处的状态
        if (states.Count == 0) {
            states.Add(s);
            return;
        }

        // 如果状态不在列表中,则将其添加到列表中
        foreach (FSMState state in states) {
            if (state.ID == s.ID) {
                Debug.LogError("FSM 错误:无法添加状态 " + s.ID.ToString() +
                               " 因为状态已经被添加");
                return;
            }
        }
        states.Add(s);
    }


    /// <summary>
    /// 如果状态存在,这个方法从 FSM 列表中删除一个状态,
    /// 如果状态不在列表中,则打印错误信息。
    /// </summary>
    public void DeleteState(StateID id) {
        // 在删除之前检查 NullState
        if (id == StateID.NullStateID) {
            Debug.LogError("FSM 错误:不允许使用 NullStateID 作为真实状态");
            return;
        }

        // 搜索列表并删除其中的状态
        foreach (FSMState state in states) {
            if (state.ID == id) {
                states.Remove(state);
                return;
            }
        }
        Debug.LogError("FSM 错误:无法删除状态 " + id.ToString() +
                       "。它不在状态列表中");
    }

    /// <summary>
    /// 这个方法尝试根据当前状态和传递的转换来改变 FSM 所处的状态。
    /// 如果当前状态没有传递的转换的目标状态,则打印错误信息。
    /// </summary>
    public void PerformTransition(Transition trans) {
        // 在更改当前状态之前检查 NullTransition
        if (trans == Transition.NullTransition) {
            Debug.LogError("FSM 错误:不允许使用 NullTransition 作为真实转换");
            return;
        }

        // 检查当前状态是否有传递的转换
        StateID id = currentState.GetOutputState(trans);
        if (id == StateID.NullStateID) {
            Debug.LogError("FSM 错误:状态 " + currentStateID.ToString() + " 没有目标状态 " +
                           " 对于转换 " + trans.ToString());
            return;
        }

        // 更新 currentStateID 和 currentState
        currentStateID = id;
        foreach (FSMState state in states) {
            if (state.ID == currentStateID) {
                // 在设置新状态之前进行状态的后处理
                currentState.DoBeforeLeaving();

                currentState = state;

                // 在状态可以推理或行动之前重置其到所需条件
                currentState.DoBeforeEntering();
                break;
            }
        }

    } // PerformTransition()

} //class FSMSystem

2.功能分述 

        功能:定义了状态转换的标签。NullTransition 用于表示不存在的转换。

public enum Transition
{
    NullTransition = 0, // 使用这个转换来表示系统中不存在的转换
    StartButtonClick,
    PauseButtonClick
}

        功能:定义了状态的标签。NullStateID 用于表示不存在的状态。

public enum StateID
{
    NullStateID = 0, // 使用这个 ID 来表示系统中不存在的状态
    Menu,
    Play,
    Pause,
    GameOver
}

        功能

  • AddTransition:添加状态转换。
  • DeleteTransition:删除状态转换。
  • GetOutputState:获取转换后的状态。
  • DoBeforeEntering:进入状态前的设置。
  • DoBeforeLeaving:离开状态前的设置。
  • Reason:决定是否进行状态转换。
  • Act:控制 NPC 在当前状态下的行为。
  • public abstract class FSMState : MonoBehaviour
    {
        protected Ctrl ctrl;
        public Ctrl CTRL { set { ctrl = value; } }
        protected FSMSystem fsm;
        public FSMSystem FSM { set { fsm = value; } }
        protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
        protected StateID stateID;
        public StateID ID { get { return stateID; } }
    
        public void AddTransition(Transition trans, StateID id)
        {
            // 检查参数是否无效
            if (trans == Transition.NullTransition)
            {
                Debug.LogError("FSMState 错误:NullTransition 不允许作为真实的转换");
                return;
            }
    
            if (id == StateID.NullStateID)
            {
                Debug.LogError("FSMState 错误:NullStateID 不允许作为真实的 ID");
                return;
            }
    
            // 检查当前转换是否已经在字典中
            if (map.ContainsKey(trans))
            {
                Debug.LogError("FSMState 错误:状态 " + stateID.ToString() + " 已经有转换 " + trans.ToString() +
                               "无法分配给另一个状态");
                return;
            }
    
            map.Add(trans, id);
        }
    
        public void DeleteTransition(Transition trans)
        {
            // 检查 NullTransition
            if (trans == Transition.NullTransition)
            {
                Debug.LogError("FSMState 错误:不允许使用 NullTransition");
                return;
            }
    
            // 在删除之前检查该对是否在映射中
            if (map.ContainsKey(trans))
            {
                map.Remove(trans);
                return;
            }
            Debug.LogError("FSMState 错误:传递给 " + stateID.ToString() + " 的转换 " + trans.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 virtual void Reason() { }
    
        public virtual void Act() { }
    }
    

        功能

  • AddState:添加新状态到 FSM。
  • DeleteState:从 FSM 中删除状态。
  • SetCurrentState:设置当前状态。
  • PerformTransition:执行状态转换。
  • public class FSMSystem
    {
        private List<FSMState> states;
    
        // 改变 FSM 状态的唯一方法是执行转换
        // 不要直接更改 CurrentState
        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 SetCurrentState(FSMState s)
        {
            currentState = s;
            currentStateID = s.ID;
            s.DoBeforeEntering();
        }
    
        public void AddState(FSMState s, Ctrl ctrl)
        {
            // 在删除之前检查空引用
            if (s == null)
            {
                Debug.LogError("FSM 错误:不允许空引用");
            }
            s.FSM = this;
            s.CTRL = ctrl;
            // 插入的第一个状态也是初始状态,
            // 模拟开始时机器所处的状态
            if (states.Count == 0)
            {
                states.Add(s);
                return;
            }
    
            // 如果状态不在列表中,则将其添加到列表中
            foreach (FSMState state in states)
            {
                if (state.ID == s.ID)
                {
                    Debug.LogError("FSM 错误:无法添加状态 " + s.ID.ToString() +
                                   " 因为状态已经被添加");
                    return;
                }
            }
            states.Add(s);
        }
    
        public void DeleteState(StateID id)
        {
            // 在删除之前检查 NullState
            if (id == StateID.NullStateID)
            {
                Debug.LogError("FSM 错误:不允许使用 NullStateID 作为真实状态");
                return;
            }
    
            // 搜索列表并删除其中的状态
            foreach (FSMState state in states)
            {
                if (state.ID == id)
                {
                    states.Remove(state);
                    return;
                }
            }
            Debug.LogError("FSM 错误:无法删除状态 " + id.ToString() +
                           "。它不在状态列表中");
        }
    
        public void PerformTransition(Transition trans)
        {
            // 在更改当前状态之前检查 NullTransition
            if (trans == Transition.NullTransition)
            {
                Debug.LogError("FSM 错误:不允许使用 NullTransition 作为真实转换");
                return;
            }
    
            // 检查当前状态是否有传递的转换
            StateID id = currentState.GetOutputState(trans);
            if (id == StateID.NullStateID)
            {
                Debug.LogError("FSM 错误:状态 " + currentStateID.ToString() + " 没有目标状态 " +
                               " 对于转换 " + trans.ToString());
                return;
            }
    
            // 更新 currentStateID 和 currentState
            currentStateID = id;
            foreach (FSMState state in states)
            {
                if (state.ID == currentStateID)
                {
                    // 在设置新状态之前进行状态的后处理
                    currentState.DoBeforeLeaving();
    
                    currentState = state;
    
                    // 在状态可以推理或行动之前重置其到所需条件
                    currentState.DoBeforeEntering();
                    break;
                }
            }
    
        } // PerformTransition()
    
    } //class FSMSystem
    

 

 

 

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值