当前位置: 首页 > news >正文

Unity有限状态机的简易实现

本人嘴笨,不会说。
该代码实现一个功能较为齐全的有限状态机,可用于大部分的应用场景。
大致实现几个功能

  • 状态更新
  • 状态转换
  • 状态消息处理
  • 全局状态转换和反转状态(转换为前一个状态)

代码分为

  • 状态类
  • 状态管理类
  • 枚举(状态枚举和消息枚举)

简易通用框架代码

状态接口

using System;namespace FSM
{public interface IStateBase<T, TState, TMessage>where TState : Enumwhere TMessage : Enum{//确定该状态枚举public TState GetState();//进入状态void OnEnter(T owner);//更新状态void OnUpdate(T owner);//退出状态void OnExit(T owner);// 转换状态 (-1 为返回上一个状态,0 为保持不变,1 为转换为下一个状态)int OnTransfer(T owner, out TState newState);//消息接收void OnMessage(T owner, TMessage e);}
}

全局状态接口

using System;namespace FSM
{public interface IGlobalState<T, TState, TMessage> : IStateBase<T, TState, TMessage>where TState : Enumwhere TMessage : Enum{//全局转换状态bool OnGlobalTransfer(T owner);}
}

状态管理类

using System;
using System.Collections.Generic;
using UnityEngine;namespace FSM
{public class FsmMachine<T, TState, TMessage>where TState : Enumwhere TMessage : Enum{//状态类存储private Dictionary<TState, IStateBase<T, TState, TMessage>> _states =new Dictionary<TState, IStateBase<T, TState, TMessage>>();//全局状态类存储private Dictionary<TState, IGlobalState<T, TState, TMessage>> _globalStates =new Dictionary<TState, IGlobalState<T, TState, TMessage>>();//Fsm的使用对象private T _owner;//状态枚举private TState _stateID;//上一个状态private IStateBase<T, TState, TMessage> _previousState;//状态类private IStateBase<T, TState, TMessage> _state;//初始化public void Init(T owner, TState stateID){if (owner == null)throw new NullReferenceException();if (!_states.ContainsKey(stateID))throw new AggregateException(stateID + "不存在");_owner = owner;_stateID = stateID;_state = _states[stateID];_state.OnEnter(_owner);}//注册状态类public void RegisterState(IStateBase<T, TState, TMessage> state){if (_states == null)_states = new Dictionary<TState, IStateBase<T, TState, TMessage>>();if (_states.ContainsKey(state.GetState()))Debug.Log(state.GetState() + "已存在");_states.Add(state.GetState(), state);}//删除状态类public void RemoveState(TState stateID){if (_states == null || !_states.ContainsKey(stateID))return;_states.Remove(stateID);}//注册全局状态类public void RegisterGlobalState(IGlobalState<T, TState, TMessage> globalState){if (_globalStates == null)_globalStates = new Dictionary<TState, IGlobalState<T, TState, TMessage>>();if (_globalStates.ContainsKey(globalState.GetState()))Debug.Log(globalState.GetState() + "已存在");_globalStates.Add(globalState.GetState(), globalState);RegisterState(globalState);}//删除全局状态类public void RemoveGlobalState(TState stateID){if (_globalStates == null || !_globalStates.ContainsKey(stateID))return;_globalStates.Remove(_stateID);RemoveState(stateID);}//更新状态public void Update(){if (_state == null)return;//状态更新_state.OnUpdate(_owner);//全局状态转换foreach (var globalState in _globalStates.Values){if (globalState.OnGlobalTransfer(_owner)){ChangeState(globalState.GetState());return;}}//状态转换switch (_state.OnTransfer(_owner, out TState stateID)){case -1:RevertToPreviousState();break;case 0://不做任何事break;case 1:ChangeState(stateID);break;}}//状态反转(返回上一个状态)private void RevertToPreviousState(){ChangeState(_previousState.GetState());}//转换状态public void ChangeState(TState stateID){if (_states == null || !_states.ContainsKey(stateID)) return;if (_stateID.Equals(stateID)) return;_state.OnExit(_owner);_stateID = stateID;_previousState = _state;_state = _states[stateID];_state.OnEnter(_owner);}}
}

实现代码

敌人枚举部分

public enum EnemyState
{None,//闲置Idle,//追逐Chase,
}public enum EnemyMessage
{None,//消息Log,
}

敌人状态基类

using FSM;namespace Enemy
{public abstract class EnemyStateBase : IGlobalState<EnemyEntity, EnemyState, EnemyMessage>{public abstract EnemyState GetState();public abstract void OnEnter(EnemyEntity owner);public abstract void OnUpdate(EnemyEntity owner);public abstract void OnExit(EnemyEntity owner);public abstract int OnTransfer(EnemyEntity owner, out EnemyState newState);public virtual bool OnGlobalTransfer(EnemyEntity owner) => false;public void OnMessage(EnemyEntity owner, EnemyMessage e){//可以通过虚方法的方式,让子类继承实现对应消息的方法if (e == EnemyMessage.Log)OnLog();}protected virtual void OnLog() { }}
}

实现了两个状态 Idle 和 Chase

using UnityEngine;namespace Enemy.State
{public class EnemyIdleState : EnemyStateBase{public override EnemyState GetState() => EnemyState.Idle;public override void OnEnter(EnemyEntity owner){owner.SetVelocity(Vector3.zero);}public override void OnUpdate(EnemyEntity owner) { }public override void OnExit(EnemyEntity owner) { }public override int OnTransfer(EnemyEntity owner, out EnemyState newState){if (Vector3.SqrMagnitude(owner.target.transform.position - owner.transform.position) <= owner.chaseRange * owner.chaseRange){newState = EnemyState.Chase;return 1;}newState = GetState();return 0;}}
}
using UnityEngine;namespace Enemy.State
{public class EnemyChaseState : EnemyStateBase{public override EnemyState GetState() => EnemyState.Chase;public override void OnEnter(EnemyEntity owner) { }public override void OnUpdate(EnemyEntity owner){Vector3 v = owner.target.transform.position - owner.transform.position;//靠太近就挺下来,但状态还是追逐if (Vector3.SqrMagnitude(v) < 1)owner.SetVelocity(Vector3.zero);elseowner.SetVelocity(v.normalized * owner.speed);}public override void OnExit(EnemyEntity owner) { }public override int OnTransfer(EnemyEntity owner, out EnemyState newState){if (Vector3.SqrMagnitude(owner.target.transform.position - owner.transform.position) > owner.escapeRange * owner.escapeRange){newState = EnemyState.Idle;return 1;}newState = GetState();return 0;}}
}

敌人部分

using Enemy;
using Enemy.State;
using FSM;
using UnityEngine;public class EnemyEntity : MonoBehaviour
{//移动速度public float speed = 2f;//追逐范围public float chaseRange = 5f;//逃脱范围public float escapeRange = 8f;//追逐目标public GameObject target;private Rigidbody _rigidbody;private FsmMachine<EnemyEntity, EnemyState, EnemyMessage> _machine;public void SetVelocity(Vector3 v3) => _rigidbody.velocity = v3;private void Awake(){ _rigidbody = GetComponent<Rigidbody>();//创建Fsm并添加状态_machine = new FsmMachine<EnemyEntity, EnemyState, EnemyMessage>();_machine.RegisterState(new EnemyIdleState());_machine.RegisterState(new EnemyChaseState());_machine.Init(this, EnemyState.Idle);}private void Update(){_machine.Update();}
}

玩家部分

using System;
using UnityEngine;public class PlayerEntity : MonoBehaviour
{public float speed = 4f;private void Update(){float hor = Input.GetAxis("Horizontal");float ver = Input.GetAxis("Vertical");Vector3 v = new Vector3(hor, 0, ver).normalized;transform.position += (speed * Time.deltaTime) * v;}
}

敌人主要实现追逐和闲置两个状态,玩家靠近就追逐,远离就闲置。
画面就不展示了,可以复制代码自己尝试

相关文章:

Unity有限状态机的简易实现

本人嘴笨&#xff0c;不会说。 该代码实现一个功能较为齐全的有限状态机&#xff0c;可用于大部分的应用场景。 大致实现几个功能 状态更新状态转换状态消息处理全局状态转换和反转状态&#xff08;转换为前一个状态&#xff09; 代码分为 状态类状态管理类枚举&#xff08;…...

什么是NetApp的DQP和如何安装DQP?

首先看看什么是DQP&#xff0c;DQPDisk Qualification Package&#xff0c;文字翻译就是磁盘验证包。按照NetApp的最佳实践&#xff0c;要定期升级DQP包&#xff0c;保证对最新磁盘和磁盘扩展柜的兼容。 本文主要介绍7-mode下如何升级DQP&#xff0c;至于cluster mode另外文章…...

Vue之Vue的介绍安装开发实例生命周期钩子

博主心得&#xff1a; keyup必须与change一起使用v-on.click可以直接写成clickclick“setVal”里的setVal换成数字之后有惊喜VS Code是真的狗&#xff0c;一些报错根本不会直接显示总结&#xff1a;VS code太狗了 1.vue介绍 1.1 什么是vue vue是一个构建用户界面UI的渐进式jav…...

【计网】计算机网络概述

目录 一、计算机网络的概念 二、计算机网络的组成 1、从组成部分上看 2、从工作方式上看 3、从功能组成上看 三、计算机网络的功能 1、数据通信 2、资源共享 3、分布式处理 4、提高可用性 5、负载均衡 四、计算机网络的分类 1、按分布范围 1.广域网 2.城域网 3.…...

初识Java 14-1 测试

目录 测试 单元测试 JUnit 测试覆盖率 前置条件 断言 Java提供的断言语法 Guava提供的更方便的断言 契约式设计中的断言 DbC 单元测试 Guava中的前置条件 本笔记参考自&#xff1a; 《On Java 中文版》 测试 ||| 如果没有经过测试&#xff0c;代码就不可能正常工作…...

react常用的hooks有哪些?

React常用的Hooks包括以下几种&#xff1a; 1.useState&#xff1a;用于在函数组件中创建和管理状态。它返回一个数组&#xff0c;第一个值是当前状态的值&#xff0c;第二个值是更新状态的函数。 使用时&#xff0c;首先通过解构赋值获取状态值和更新函数&#xff0c;并设置初…...

Nacos config 配置中心详解

目录 一、Nacos启动 1.预备环境准备 2.下载编译后压缩包方式 3. 启动服务器 二、 配置管理 多配置格式编辑器 编辑DIFF 示例代码 监听者查询 配置的版本及一键回滚 命名空间管理 登录管理 修改默认用户名/密码方法 关闭登录功能 会话时间 三、启动配置管理 添加依赖&#xff1a…...

C++ UDP通信

#pragma once #ifndef XUDP_H #define XUDP_H#ifdef WIN32 #include <windows.h> #define socklen_t int #else #include <arpa/inet.h> #define closesocket close //替换close函数 #include <unistd.h>#include<iostream> #endifclass XUdp { pub…...

自由程序员想接私活?那你还不得知道这几个接单平台!最后一个就是宝藏!!

相信喜欢搞钱的程序员都知道&#xff0c;平常在平台上接点私活&#xff0c;利用闲暇时间接单是搞钱的常用套路&#xff0c;可是你确定你选对平台了吗&#xff1f;不管你是刚准备接单的小白&#xff0c;还是已经干了一段时间的老油条&#xff0c;都建议你看完本期文章&#xff0…...

二叉树与递归的相爱相杀

数据结构之二叉树 一、基于二叉树的基础操作1.二叉树的构建2.二叉树的遍历①前序遍历&#xff08;深度遍历&#xff09;②中序遍历③后序遍历④层序遍历判断一棵二叉树是否是完全二叉树&#xff08;基于层序遍历的思想&#xff09; 3.二叉树的数量问题①求二叉树结点个数②求二…...

Docker 安装 reids

docker run -itd --name myredis -p 6379:6379 redis --requirepass “123456” --restartalways --appendonly yes...

opensl学习——base16编码解码、base64编码解码、ASCII码表、扩展ASCII码

文章目录 ASCII表概述base家族简单说明 Hex(十六进制)编码、Base32编码、Base64编码、base256编码base16编码与解码base64编码概述转换过程不足 3 字节处理方法例子一,不足3字节&#xff0c;只有一个字节例子二,不足3字节&#xff0c;只有两个字节 base64示例代码1代码分析 acl…...

gazebo各种插件

类别 libgazebo_ros_api_plugin.so&#xff1a;提供与Gazebo仿真环境进行通信的API接口。 libgazebo_ros_block_laser.so&#xff1a;模拟激光传感器的插件。 libgazebo_ros_bumper.so&#xff1a;模拟碰撞传感器的插件。 libgazebo_ros_camera.so&#xff1a;模拟相机传感器的…...

C语言Free空指针会怎样?

在C语言中&#xff0c;使用free函数释放一个空指针是安全的&#xff0c;不会引发任何错误或异常。具体来说&#xff0c;当使用free函数释放一个空指针时&#xff0c;free函数会忽略这个空指针&#xff0c;并且不会执行任何操作。这是因为free函数只对有效的指针进行内存释放操作…...

软件测试全套教程,软件测试自学线路图

软件测试&#xff1a; 软件测试是为了发现程序中的错误而执行程序的过程。 通俗的说&#xff0c;软件测试需要在发布软件之前&#xff0c;尽可能的找软件的错误&#xff0c;尽量避免在发布之后给用户带来不好的体验&#xff0c;并要满足用户使用的需求。 现在市面上这么多软…...

禁止浏览器缩放

禁止浏览器缩放 1. 页面中添加如下代码&#xff1a;2. css单位统一使用rem&#xff0c;如下&#xff1a; 两个条件即可实现&#xff1a; 1. 动态修改html属性fontsize的值&#xff1b; 2. css单位统一使用rem。 1. 页面中添加如下代码&#xff1a; // 定义基准字体 new functi…...

前端食堂技术周刊第 100 期:TS 5.3 Beta、ViteConf2023、Rspress 1.0、Fresh 1.5、Chrome 118

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;乌龙金桂 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看下…...

汇川IT7000系列HMI使用脚本实现画面跳转时自动切换手自动模式

汇川IT7070E工业HMI使用实例(1) 用脚本切换模式 我们在使用工业HMI做画面时,可能会有这样的需求,希望切换画面时,可以根据不同的画面,自动切换相应的模式,比如有些画面是进行手动操作的,有些画面是进行自动操作的,当我们需要手动时,希望进入画面自动切换为“手动模…...

FDTD Solutions笔记

FDTD Solutions笔记 目录使用流程实例 目录 使用流程 实例 材料条件 步骤 基底 2. 添加规则膜层 3. 添加仿真区 解释&#xff1a; 仿真区为&#xff08;0,0&#xff09;&#xff0c;x方向为0.4&#xff0c;y方向是1 解释&#xff1a; 一般先用低精度进行计算 解释&#xff1a…...

SQL SELECT DISTINCT(选择不同) 语法

SQL SELECT DISTINCT 语法 SELECT DISTINCT语法用于仅返回不同的&#xff08;different&#xff09;值。 在一张表内&#xff0c;一列通常包含许多重复的值; 有时你只想列出不同的&#xff08;different&#xff09;值。 SELECT DISTINCT语句用于仅返回不同的&#xff08;diffe…...

OpenClaw定时任务:千问3.5-9B每日早报自动推送

OpenClaw定时任务&#xff1a;千问3.5-9B每日早报自动推送 1. 为什么需要自动化早报推送 每天早上打开电脑第一件事&#xff0c;就是手动收集行业新闻、技术动态和日程提醒&#xff0c;再整理成早报发到团队群。这种重复劳动持续三个月后&#xff0c;我开始思考&#xff1a;能…...

姜翰奇补题

3.23-3.29一、PTA天梯赛5:第5&#xff0c;7&#xff0c;8&#xff0c;10&#xff0c;11&#xff0c;12二、牛客&#xff1a;136周赛三、马蹄集&#xff1a;DFS和BFS搜索题目四、牛客&#xff1a;蓝桥杯模拟赛3.30-4.5一、PTA天梯赛6:第8、9、10二、牛客&#xff1a;137周赛三、…...

代理商客户归管+赊欠账明细查询,易特进销存商贸版一键解决

做商贸生意的朋友&#xff0c;大概率会遇到这样的难题&#xff1a;发展了代理商拓展市场&#xff0c;代理商的客户却需要公司统一管理&#xff0c;既要明确客户归属&#xff0c;又要精准统计赊欠账目。比如代理商张三&#xff0c;总共欠公司1万元&#xff0c;查账时想清晰看到他…...

深入解析Dify中的RAG内容检索:Rerank模型与权重计算的实战对比

1. RAG内容检索的核心挑战与Rerank的价值 当你用Dify搭建一个智能问答系统时&#xff0c;最头疼的问题往往是&#xff1a;明明数据库里有正确答案&#xff0c;但系统总是返回一堆不相关的文档。这就像在图书馆用关键词搜索书籍&#xff0c;结果管理员给你搬来了整个书架——这时…...

CSS 嵌套:编写更优雅的样式代码

CSS 嵌套&#xff1a;编写更优雅的样式代码让 CSS 结构更清晰&#xff0c;层次更分明&#xff0c;代码更易维护。一、CSS 嵌套的优势 作为一名把代码当散文写的 UI 匠人&#xff0c;我对代码的可读性和结构有着近乎偏执的要求。CSS 嵌套让我们能够按照 HTML 的层次结构来组织样…...

基于MATLAB的多种概率分布拟合与KS检验:从GEV到Exponential分布选择与实践

11种概率分布的拟合与ks检验&#xff0c;可用于概率分析&#xff0c;可靠度计算等领域 案例中提供11种概率分布&#xff0c;具体包括&#xff1a;gev、logistic、gaussian、tLocationScale、Rayleigh、Loglogistic、Lognormal、GeneralizedPareto、Weibull、Gamma、Exponential…...

3个技巧让N_m3u8DL-RE流媒体下载更高效

3个技巧让N_m3u8DL-RE流媒体下载更高效 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-RE 还在为喜欢的在线视频无…...

别再乱配了!给COMSOL选工作站,CPU、内存、主板到底怎么搭才不浪费钱?

COMSOL工作站黄金配置法则&#xff1a;精准匹配需求&#xff0c;避开性能陷阱 当你面对琳琅满目的CPU型号、内存规格和主板参数时&#xff0c;是否感到无从下手&#xff1f;COMSOL Multiphysics作为一款强大的多物理场仿真工具&#xff0c;其性能表现与硬件配置息息相关。但盲目…...

样本收集的致命误区:为什么你的AI模型“一上产线就拉胯”?

很多工程师在收集TVA训练集时&#xff0c;喜欢从良品堆里挑50个&#xff0c;再从废品筐里挑50个&#xff0c;觉得凑够100个样本就能让AI“快速学习”了。结果一上线&#xff0c;只要背景稍微有点灰尘&#xff0c;或者螺母有点偏转角度&#xff0c;系统就疯狂误报。这就是典型的…...

水下珍品目标检测数据集海胆(sea urchin),海参(sea cucumber),扇贝(scallop)总计796张图像,图像大小是1920×1080数据集是YOLO格式和VOC格式可直接

水下珍品目标检测数据集 海胆(sea urchin)&#xff0c;海参(sea cucumber)&#xff0c;扇贝(scallop) 总计796张图像&#xff0c;图像大小是19201080 数据集是YOLO格式和VOC格式 可直接进行YOLO检测&#xff0c;目前yolov5检测map高达0.91 图像是原始图像&#xff0c;未做清晰化…...