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. 需求分析阶段 数据库设计:需求分析是数据库设计的初始阶段,主要任务是收集和分析用户的需求,包括…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
