Unity 一个比较适合学习的FSM状态机(汉化和功能简述)
该轮子由网络资源而来,遵从作者开源意愿,仅作免费学习和分享,不作任何商业行为 ,本文不支持任何交易行为,侵权删!!!
至于我为什么不将此文章设置为转载,是因为该代码所在的网站早在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) {// 检查 NullTransitionif (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 状态的唯一方法是执行转换// 不要直接更改 CurrentStateprivate 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) {// 在删除之前检查 NullStateif (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) {// 在更改当前状态之前检查 NullTransitionif (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 和 currentStatecurrentStateID = 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){// 检查 NullTransitionif (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 状态的唯一方法是执行转换// 不要直接更改 CurrentStateprivate 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){// 在删除之前检查 NullStateif (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){// 在更改当前状态之前检查 NullTransitionif (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 和 currentStatecurrentStateID = id;foreach (FSMState state in states){if (state.ID == currentStateID){// 在设置新状态之前进行状态的后处理currentState.DoBeforeLeaving();currentState = state;// 在状态可以推理或行动之前重置其到所需条件currentState.DoBeforeEntering();break;}}} // PerformTransition()} //class FSMSystem
相关文章:

Unity 一个比较适合学习的FSM状态机(汉化和功能简述)
该轮子由网络资源而来,遵从作者开源意愿,仅作免费学习和分享,不作任何商业行为 ,本文不支持任何交易行为,侵权删!!! 至于我为什么不将此文章设置为转载,是因为该代码所在…...

25、Wpf之App资源应用
开发平台:Win10 64位 开发环境:VS2022(64位) Preview .NET Framework:.NET 6 文章目录 一 Resources1.1 Application中定义资源1.2 样式(Styles)1.3 模板(Templates)1.4 数据转换器(…...

【深度好文】反模式:10种滥用设计模式案例分析
Hello,大家好,我是V哥。很多文章都在介绍设计模式怎么用,讲解设计模式的原理等等,设计模式的思想是编程中的精髓,用好了可以让代码结构利于维护和扩展,同时代码风格也更加优雅,V 哥也写过这样一…...

OkHttp Interceptor日志上报
最近为了做一些网络上的优化,所以就得提前埋点,为后续网络优化提供数据支持。 主要是对发起请求埋点,请求错误埋点,客户端请求耗时埋点。 事件上报到阿里云,接入的是阿里的应用实时监控服务。 网络请求使用的是OhHttp…...

高性能反向代理--HAProxy
文章目录 Web架构负载均衡介绍为什么使用负载均衡负载均衡类型 HAProxy简介应用场景HAProxy是什么HAProxy功能 脚本安装HAProxy基础配置global多进程和线程HAProxy日志配置项 Proxies配置-listen-frontend-backendserver配置 frontendbackend配置实例子配置文件 HAProxy调度算法…...

数据结构应用实例(四)——最小生成树
Content: 一、问题描述二、算法思想三、代码实现四、两种算法的比较五、小结 一、问题描述 利用 prim 算法和 kruskal 算法实现最小生成树问题; 二、算法思想 首先判断图是否连通,只有在连通的情况下才进行最小树的生成; 三、代…...

为OneAPI配置MySQL数据库及设置开机启动
OneAPI启动时,如果发现没有数据库他会在项目根目录自动创建SqlLit,为提高OneAPI的性能及管理,这里给出一个使用MySQL数据库的案例,同时本文介绍如何在源码部署的情况下,设置OneAPI的开机自动启动。 OneAPI的源代码安装…...

完整的k8s搭建服务器流程
一、准备 1、禁用selinux #临时禁用 setenforce 0 #永久禁用 sed -i s/enforcing/disabled/ /etc/selinux/config #检查selinux是否已禁用 sestatus 2、禁用交换分区 #命令行临时禁用 swapoff -a #永久禁用 vim /etc/fstab 注释掉有swap字样的那行,重启 3、允许…...

【Petri网导论学习笔记】Petri网导论入门学习(一)
Petri 网导论 如需学习转载请注明原作者并附本帖链接!!! 如需学习转载请注明原作者并附本帖链接!!! 如需学习转载请注明原作者并附本帖链接!!! 发现网上关于Petri网的学习…...

Zabbix监控自动化
监控在运维工作中所占的比例为 30%左右,监控做得好,会省很多事,让工作能有序地进行。理想的监控应该是自动化的,只需要配置规则,即可自动完成所有的事情,比如主机的自动添加和注册、模板的自动添加、分组的…...

pytorch pyro 贝叶斯神经网络 bnn beyesean neure network svi 定制SVI目标和培训循环,变更推理
定制SVI目标和培训循环 Pyro支持各种基于优化的贝叶斯推理方法,包括Trace_ELBO作为SVI(随机变分推理)的基本实现。参见文件(documents的简写)有关各种SVI实现和SVI教程的更多信息I, 二,以及罗马数字3了解SVI的背景。 在本教程中…...

Openeuler22 部署 RackTables0.22.0
目录 0、前言 一、部署lamp环境,lamp环境测试 1、部署Apache,apache环境测试 2、部署php、mysql,php环境测试 二、放文件 三、配置mysql 四、安装racktables 第一步、点击proceed继续 第二步、点击proceed 第三步、根据提示进行操作…...

从传统到智能:高标准农田灌区信息化助力农业现代化
从传统农业的粗放式管理,到如今智能化、精准化的现代农业转型,高标准农田灌区信息化建设无疑是推动这一历史进程的关键力量。它不仅标志着农业生产方式的根本性变革,还深刻影响着农业资源的高效利用与可持续发展策略,为实现农业现…...

堆排序-建堆,增删替换
我们 之前写过根据 堆排序的优先级队列,但是如果我们想要建立一个堆怎么办呢? 如何实现上浮 下潜 具体看这篇文章 堆排序-优先级队列-CSDN博客 建堆 我们有两种方法建立一个堆 1.我们基于add方法建立一个堆,一次次的add,然后对…...

使用AI写WebSocket知识是一种怎么样的体验?
一、WebSocket基础知识 1. WebSocket概念 1.1 为什么会出现WebSocket 一般的Http请求我们只有主动去请求接口,才能获取到服务器的数据。例如前后端分离的开发场景,自嘲为切图仔的前端大佬找你要一个配置信息的接口,我们后端开发三下两下开…...

若依系统(Security)增加微信小程序登录(自定义登录)
若依系统(分离版后端)自带的账号验证是基于 UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(username, password); 验证,然后在系统中controller或service类中 SecurityUtils 工具类中直接可获取用户或用户…...

道可云人工智能元宇宙每日资讯|2024互联网岳麓峰会在长沙召开
道可云元宇宙每日简报(2024年9月10日)讯,今日元宇宙新鲜事有: 2024互联网岳麓峰会在长沙召开 9月9日,2024互联网岳麓峰会在长沙召开,湖南省副省长曹志强在峰会表示,今年上半年湖南省人工智能产…...

MySQL JDBC URL各参数详解
jdbc:mysql://localhost:3306/test?userroot&password123456&useUnicodetrue&characterEncodinggbk &autoReconnecttrue&failOverReadOnlyfalse&serverTimezoneUTC&drivercom.mysql.cj.jdbc.Driver 参数名称参数说明缺省值user指定用于连接数据库…...

celery control.shutdown
Celery 提供了 control 模块,允许你发送控制命令给正在运行的 worker。其中 shutdown 命令可以用来关闭一个或多个 worker。下面是如何使用 control.shutdown 来关闭 worker 的详细说明。 使用 control.shutdown 1. 导入必要的模块 首先,你需要导入 C…...

数据库设计与软件工程阶段的对应关系
数据库设计的很多阶段确实可以与软件工程的各阶段对应起来,这体现了数据库设计作为软件工程中一个核心组成部分的紧密关联性。 1. 需求分析阶段 数据库设计:需求分析是数据库设计的初始阶段,主要任务是收集和分析用户的需求,包括…...

基于ASP+ACCESS的教师信息管理系统
摘要 随着我国社会主义市场经济的发展和改革开放的不断深入,计算机的应用已遍及国民经济的各个领域,计算机来到我们的工作和生活中,改变着我们和周围的一切。在以前,学校用手工处理教师档案以及工资发放等繁多的工作和数据时&…...

【智能体】浅谈大模型之AI Agent
随着ChatGPT推出插件和函数调用功能,构建以LLM(大语言模型)为核心控制器的AI Agent愈发成为一个拥有无限可能的概念。 AI Agent是一种超越简单文本生成的人工智能系统。它使用大型语言模型(LLM)作为其核心计算引擎&am…...

大疆 嵌入式 笔记 面试题目汇总大全[嵌入式找工作必看] 比较有难度适合进阶收藏学习
24届大疆车载嵌入式系统安全面试经验 投递岗位:(大疆车载)嵌入式系统安全 投递时间:大疆大概在7月-8月月初开秋招岗位。8月月中开始笔试,8月月末开始面试。(理论上9月,10月开奖。)…...

线程池以及详解使用@Async注解异步处理方法
目录 一.什么是线程池: 二.使用线程池的好处: 三.线程池的使用场景: 四.使用线程池来提高Springboot项目的并发处理能力: 1.在application.yml配置文件中配置: 2.定义配置类来接受配置文件内的属性值:…...

css鼠标移动过去变成手的图标
在css中定义 cursor:pointer;直接在html中指定 <div class"mt-2 mt-md-2 mt-lg-1 fs-md-1 fs-lg-2 " style"cursor:pointer;"></div>...

uniapp 懒加载、预加载、缓存机制深度解析
uniapp 懒加载、预加载、缓存机制深度解析 文章目录 uniapp 懒加载、预加载、缓存机制深度解析一、为什么要使用uniapp的懒加载、预加载和缓存机制二、如何使用uniapp的懒加载、预加载和缓存机制1. 懒加载2. 预加载3. 缓存机制 四、扩展与高级技巧1. 结合懒加载和预加载优化页面…...

《OpenCV计算机视觉》—— 图像形态学(腐蚀、膨胀等)
文章目录 一、图像形态学基本概念二、基本运算1.简单介绍2.代码实现 三、高级运算1.简单介绍2.代码实现 一、图像形态学基本概念 图像形态学是图像处理科学的一个独立分支,它基于集合论和数学形态学的理论,专门用于分析和处理图像中的形状和结构。图像形…...

【Rust光年纪】海洋学研究的利器:Rust语言海洋学计算库详解
探索Rust语言下的海洋学计算库:功能对比与选择指南 前言 随着科学技术的不断发展,海洋学领域对于计算和数据处理的需求也日益增长。在Rust语言中,出现了一系列专注于海洋学计算和数据处理的库,它们为海洋学工作者提供了强大的工…...

Word文档的读入【2】
现在,乔老师已经了解了Word文档的基本结构。 下面,我们通过观察一份答题卡来思考一下每条信息的具体位置。这样,在后面几天的学习和操作中,我们就能更快、更准确地读取到答题卡中的信息。 这份答题卡是由一个表格和一些段落组成。…...

报名开启 | 游戏开发缺队友?首期繁星招聘会来袭!
**点击蓝链领取游戏开发教程 ** EE GAMES 创作者社区是专注于链接每一位游戏创作者,提供社区交流、团队匹配、经验共享、成果展示、知识整合、最新活动资讯等全方位服务的游戏领域垂类社区。 这里不仅仅是一个游戏创作的互助平台,更是每一位游戏创作者…...