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. 需求分析阶段 数据库设计:需求分析是数据库设计的初始阶段,主要任务是收集和分析用户的需求,包括…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
