有限状态机

23 篇文章 0 订阅
6 篇文章 0 订阅
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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值