【Unity】Finite State Machine 有限状态机

农雅畅
2023-12-01

原文链接:http://wiki.unity3d.com/index.php/Finite_State_Machine


翻译了下Unity官方Wiki有限状态机的代码注释部分。

出于学习目的,修改了部分注释和调试输出等内容,版权归原文所有,仅供大家学习使用。


FSMSystem.cs

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

/// <summary>
/// 脚本功能:状态机系统,用于控制战斗系统中角色和敌人的状态转换
/// 添加对象:无
/// 创建时间:2015年6月12日16:39:02 duzixi.com
/// 知识要点:
/// 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;
            }
        }
    } 
} 


 类似资料: