有限狀態機

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

/**
基於Eric Dybsand的遊戲編程Gems 1第3.1章的有限狀態機系統Roberto Cezar Bianchini著,2010年7月
 
How to use:
	1. 爲有限狀態系統的轉換和狀態放置標籤在相應的枚舉中。
 
	2. 編寫從FSMState繼承的新類,並用對(轉換狀態)填充每個類。這些對錶示當處於狀態S1, a時,FSMSystem應該處於的狀態S2轉換T被觸發,狀態S1從它轉換到S2。記住這是一個確定性的FSM。你不可能有一個轉變導致兩個不同的狀態。
 
	   方法原因用於確定應該觸發哪個轉換。您可以在另一個地方編寫觸發轉換的代碼,如果需要,則保留此方法爲空覺得它更適合你的項目。
 
	   Method Act有代碼來執行NPC在這個狀態下應該執行的操作。您可以在另一個地方爲操作編寫代碼,如果需要,可以將此方法保留爲空覺得它更適合你的項目。
 
	3. 創建一個FSMSystem類的實例,並向其添加狀態。
 
	4. 調用Reason和Act(或者你擁有的用於觸發轉換和製作npc的任何方法)行爲在你的遊戲)從你的更新或FixedUpdate方法。
 
	從Unity引擎的異步轉換,如OnTriggerEnter, SendMessage,也可以使用,只需從FSMSystem實例調用PerformTransition方法,並使用正確的轉換事件發生時。
 
 
 
本軟件是“按原樣”提供的,沒有任何明示或暗示的保證,包括但不限於適銷性、適合某一特定用途的保證和不侵權。在任何情況下,作者或版權持有人均不對任何索賠承擔責任,損害賠償或其他責任,無論是在合同、侵權或其他訴訟中,
與本軟件無關或與本軟件有關的,或與本軟件的使用或其他交易有關的。
*/


/// <summary>
/// 在這個枚舉中放置轉換的標籤。
/// 不要更改第一個標籤,因爲FSMSystem類使用了它。
/// </summary>
public enum Transition
{
    NullTransition = 0, // 使用此轉換表示系統中不存在的轉換
    StartButtonClick,
    PauseButtonClick
}

/// <summary>
/// 在這個枚舉中放置狀態的標籤。
/// 不要更改第一個標籤,因爲FSMSystem類使用了它。
/// </summary>
public enum StateID
{
    NullStateID = 0, // 使用此ID表示系統中不存在的狀態	
    Menu,
    Play,
    Pause,
    GameOver
}

/// <summary>
/// 該類表示有限狀態系統中的狀態。
/// 每個狀態都有一個顯示對(轉換狀態)的字典
/// 如果在此狀態下觸發轉換,FSM應該處於哪種狀態
/// 是當前狀態。
/// 方法原因用於確定應該觸發哪個轉換。
/// Method Act有代碼來執行NPC在這個狀態下應該執行的操作。
/// </summary>
public abstract class FSMState : MonoBehaviour
{
    protected Ctrl ctrl;        //Control腳本
    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)
    {
        // 檢查是否有arg是無效的
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
            return;
        }

        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
            return;
        }

        // 因爲這是一個確定性的FSM,
        //   檢查當前轉換是否已經在映射中
        if (map.ContainsKey(trans))
        {
            Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
                           "Impossible to assign to another state");
            return;
        }

        map.Add(trans, id);
    }

    /// <summary>
    /// 此方法從該狀態映射中刪除一對轉換狀態。
    /// 如果轉換不在狀態映射中,則會打印一條錯誤消息。
    /// </summary>
    public void DeleteTransition(Transition trans)
    {
        // Check for NullTransition
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSMState ERROR: NullTransition is not allowed");
            return;
        }

        // 在刪除之前,檢查對是否在映射中
        if (map.ContainsKey(trans))
        {
            map.Remove(trans);
            return;
        }
        Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
                       " was not on the state's transition list");
    }

    /// <summary>
    /// 該方法返回FSM應該是if的新狀態
    ///   這個狀態接收一個轉換和
    /// </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類表示有限狀態機類。
///  It has a List with the States the NPC has and methods to add,,
///  刪除一個狀態,並更改機器當前處於的狀態。
/// </summary>
public class FSMSystem
{
    private List<FSMState> states;

    // 更改FSM狀態的惟一方法是執行轉換
    // 不要直接改變當前狀態
    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)
    {
        // Check for Null reference before deleting
        if (s == null)
        {
            Debug.LogError("FSM ERROR: Null reference is not allowed");
        }
        s.FSM = this;
        s.CTRL = ctrl;
        // 插入的第一個狀態也是初始狀態,
        //   模擬開始時機器所處的狀態
        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: Impossible to add state " + s.ID.ToString() +
                               " because state has already been added");
                return;
            }
        }
        states.Add(s);
    }

    /// <summary>
    /// 這個方法從FSM列表中刪除一個狀態,如果它存在,
    ///  或者,如果狀態不在列表中,則打印錯誤消息。
    /// </summary>
    public void DeleteState(StateID id)
    {
        // 刪除之前檢查NullState
        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
            return;
        }

        // 搜索列表並刪除列表中的狀態
        foreach (FSMState state in states)
        {
            if (state.ID == id)
            {
                states.Remove(state);
                return;
            }
        }
        Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
                       ". It was not on the list of states");
    }

    /// <summary>
    /// 該方法試圖改變FSM所處的狀態
    /// 當前狀態和轉換已通過。如果當前狀態
    ///  轉換沒有目標狀態,
    /// 打印錯誤消息。
    /// </summary>
    public void PerformTransition(Transition trans)
    {
        //在更改當前狀態之前檢查NullTransition
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
            return;
        }

        // 檢查當前狀態是否將轉換作爲參數傳遞
        StateID id = currentState.GetOutputState(trans);
        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +
                           " for transition " + 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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章