该轮子由网络资源而来,遵从作者开源意愿,仅作免费学习和分享,不作任何商业行为 ,本文不支持任何交易行为,侵权删!!!
至于我为什么不将此文章设置为转载,是因为该代码所在的网站早在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