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