2024-07-12 Unity AI状态机1 —— 框架介绍

文章目录

  • 1 有限状态机
  • 2 状态机实现框架
    • 2.1 StateMachine
    • 2.2 BaseState
    • 2.3 ...State
    • 2.4 IAIObject
  • 3 框架类图

本文章参考 B 站唐老狮 2023 年直播内容。点击前往唐老狮 B 站主页。

1 有限状态机

​ 有限状态机(Finite - State Machine,FSM),又称有限状态自动机,简称状态机。是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

  • 有限:有限度,不是无限的。

  • 状态:指所拥有的所有状态。

举例说明:

​ 人会做很多个动作,即有很多种状态,包括站立、走路、跑步、攻击、防守、睡觉等等。我们每天都会在这些状态中切换,而且这些状态虽然多但是是有限的。当达到某种条件时,就会在这些状态中进行切换,且这种切换时随时可能发生。

​ 游戏中的怪物 AI 表现一般为巡逻、朝玩家移动、攻击、逃跑、格挡等等状态。即使怪物 AI 表现看起来比较复杂,但是深入观察和分析,它们的状态一般是有限的,只是在不同条件下切换不同状态,由于状态的多样性和美术表现的多样性,让我们觉得它是比较智能的。

​ 因此,“有限”和“行为状态”这两个特点,使得游戏 AI 适用于使用有限状态机实现。

2 状态机实现框架

  1. StateMachine(状态机类,用于统一管理各个状态)。

  2. BaseState(状态基类,抽象出状态的共有内容)。

  3. …State(各状态类,有多少个状态就有几个状态类,均继承状态基类)。

  4. IAIObject(怪物 AI 接口,用于规范所有 AI 的共同行为)。

2.1 StateMachine

​ 有限状态机类,用于管理各个状态之间的切换。同时需要记录所有状态,因此需要如下成员:

  1. StateDictionay:记录所有状态。
  2. NowState:当前处于的状态。
  3. AIObject:附属在哪个 AI 对象上。
  4. AddState():向外部提供添加状态的方法(执行一帧)。
  5. ChangeState():向外部提供切换状态的方法(执行一帧)。
  6. UpdateState():向外部提供更新状态的方法(每帧调用)。

​ 基本结构如下:

using System;
using System.Collections.Generic;/// <summary>
/// 有限状态机类,用于管理各个状态之间的切换
/// </summary>
public class StateMachine
{/// <summary>/// 管理所有状态的字典容器/// </summary>private Dictionary<EAIState, BaseState> _stateDic = new Dictionary<EAIState, BaseState>();private BaseState _nowState; // 当前状态public IAIObject AIObject; // 管理的 AI 对象/// <summary>/// 初始化方法/// </summary>/// <param name="aiObject">待管理的 AI 对象</param>public void Init(IAIObject aiObject) {AIObject = aiObject;}/// <summary>/// 添加 AI 状态/// </summary>public void AddState(EAIState state) { ... }/// <summary>/// 切换状态/// </summary>/// <param name="state"></param>public void ChangeState(EAIState state) { ... }/// <summary>/// 更新当前状态/// </summary>public void UpdateState() { ... }
}

2.2 BaseState

​ 状态基类负责提取所有状态共有的特性,并声明为抽象类(Abstract),规范每个状态的共同行为。其共有特性基本包括如下:

  1. AIState:状态类型(所有状态需要告诉外面自己是哪种类型)。
  2. StateMachine:附属的状态机。
  3. OnStateEnter():进入状态时执行的逻辑(执行一帧)。
  4. OnStateUpdate():处于该状态时不断更新的逻辑(每帧更新)。
  5. OnStateExist():离开状态时执行的逻辑(执行一帧)。

​ 基本结构如下:

using UnityEngine;/// <summary>
/// 状态基类
/// </summary>
public abstract class BaseState
{public virtual EAIState AIState { get; } // 状态类型protected StateMachine _stateMachine; // 附属的状态机public BaseState(StateMachine stateMachine) {_stateMachine = stateMachine;}/// <summary>/// 进入状态/// </summary>public abstract void OnStateEnter();/// <summary>/// 保持状态/// </summary>public abstract void OnStateUpdate();/// <summary>/// 离开状态/// </summary>public abstract void OnStateExit();
}

2.3 …State

​ 在各个具体状态中,可以有独立的数据或逻辑,但每个状态都需要实现由基类规范的共同逻辑。

​ 以 PatrolState(巡逻状态)为例,其基本结构如下:

using System;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;/// <summary>
/// 巡逻状态类,处理巡逻逻辑
/// </summary>
public class PatrolState : BaseState
{// 巡逻数据...public override EAIState AIState { get => EAIState.Patrol; } // 表示自己是巡逻状态public PatrolState(StateMachine stateMachine) : base(stateMachine) {// 初始化...}public override void OnStateEnter() { ... }  // 进入巡逻状态的逻辑实现public override void OnStateUpdate() { ... } // 处于巡逻状态的逻辑实现public override void OnStateExit() { ... }   // 退出巡逻状态的逻辑实现// 其他逻辑、方法封装等...
}

2.4 IAIObject

​ AI 对象接口,规定 AI 对象必须继承该接口,用于规范 AI 对象的行为。

​ 在 StateMachine 中,不会存储具体的 AI 对象,而是存储 IAIObject,指向对应的 AI 对象。不同的 AI 对象均需要实现 IAIObject 中规范的逻辑,从而使 StateMachine 能够适用于具有不同逻辑的不同 AI 对象。

​ 基本实现如下:

using UnityEngine;/// <summary>
/// AI 对象接口,用于规范 AI 对象的行为
/// </summary>
public interface IAIObject
{public Transform Transform { get; } // AI 对象的 Transformpublic Transform TargetTransform { get; } // 目标监测对象的 Transformpublic float AttackRange { get; } // 攻击范围public Vector3 BornPos { get; } // 出生位置public void Move(Vector3 targetPos); // 移动public void StopMove(); // 停止移动public void Attack(); // 攻击public void ChangeAction(EAction action); // 切换指定动作// 等等...
}

3 框架类图

StateMachine_AI 框架介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/377409.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker安装RabbitMQ(带web管理端)

1.拉取带web管理的镜像 可以拉取rabbitmq对应版本的web管理端&#xff0c;比如&#xff1a;rabbitmq:3.9.11-management&#xff0c;也可以直接拉取带web管理端的最新版本 rabbitmq:management. docker pull rabbitmq:3.9.11-management 注意&#xff1a;如果docker pull ra…

websockt初始化,创建一个webSocket示例

写文思路&#xff1a; 以下主要从几个方面着手写websocket相关&#xff0c;包括以下&#xff1a;什么是webSocket&#xff0c;webSocket的优点和劣势&#xff0c;webSocket工作原理&#xff0c;webSocket握手示例&#xff0c;如何使用webSocket(使用webSocket的一个示例)&#…

Chapter 1:数据结构前言

在数字化的世界里&#xff0c;我们每天都在与数据打交道。然而&#xff0c;你是否曾想过&#xff0c;这些数据是如何被组织、存储和处理的&#xff1f;这就是数据结构的魅力所在。 数据结构&#xff0c;简单来说&#xff0c;就是数据的组织方式。它决定了我们如何高效地访问和操…

object-C 解答算法:合并两个有序数组(leetCode-88)

合并两个有序数组(leetCode-88) 题目如下图:(也可以到leetCode上看完整题目,题号88) 首先搞懂,什么叫“非递减顺序” 非递减顺序,是指一个序列中的元素从前往后&#xff08;或从左到右&#xff09;保持不减少或相等。 这意味着序列中的元素可以保持相同的值&#xff0c;但不会…

外包干了1个月,技术明显退步。。。

有一种打工人的羡慕&#xff0c;叫做“大厂”。 真是年少不知大厂香&#xff0c;错把青春插稻秧。 但是&#xff0c;在深圳有一群比大厂员工更庞大的群体&#xff0c;他们顶着大厂的“名”&#xff0c;做着大厂的工作&#xff0c;还可以享受大厂的伙食&#xff0c;却没有大厂…

Rust 使用 panic! 还是不用 panic!

使用 panic! 还是不用 panic! 那么&#xff0c;该如何决定何时应该 panic! 以及何时应该返回 Result 呢&#xff1f;如果代码 panic&#xff0c;就没有恢复的可能。你可以选择对任何错误场景都调用 panic!&#xff0c;不管是否有可能恢复&#xff0c;不过这样就是你代替调用者…

智能合约和分布式应用管理系统:技术革新与未来展望

引言 随着区块链技术的不断发展&#xff0c;智能合约和分布式应用&#xff08;DApps&#xff09;逐渐成为数字经济中的重要组成部分。智能合约是一种自执行的协议&#xff0c;能够在预设条件满足时自动执行代码&#xff0c;而无需人工干预或中介机构。这种自动化和信任机制极大…

DP V2.1a标准学习(二)

三、线材&连接器要求 1.线材分类 线材按照支持速率的不同,划分为不同规格,具体如下: DP和USB线材混用矩阵,也就是不同规格的线材对应的支持传输速率和传输类型,如下: 2.线材和连接器组件分类 线材+连接器组件分类与DP V1.4类似,不过型号更多了,如下: 也支持2种…

Vue3 使用 Vue Router 时,prams 传参失效和报错问题

Discarded invalid param(s) “id“, “name“, “age“ when navigating 我尝试使用 prams 传递数据 <script setup> import { useRouter } from vue-routerconst router useRouter() const params { id: 1, name: ly, phone: 13246566476, age: 23 } const toDetail…

基于conda包的环境创建、激活、管理与删除

Anaconda是一个免费、易于安装的包管理器、环境管理器和 Python 发行版&#xff0c;支持平台包括Windows、macOS 和 Linux。下载安装地址&#xff1a;Download Anaconda Distribution | Anaconda 很多不同的项目可能需要使用不同的环境。例如某个项目需要使用pytorch1.6&#x…

从 Icelake 到 Iceberg Rust

本文作者丁皓是Databend 研发工程师&#xff0c;也是 ASF Member&#xff0c; Apache OpenDAL PMC Chair &#xff0c;主要研究领域包括存储、自动化与开源。 太长不看 Icelake 已经停止更新&#xff0c;请改用 iceberg-rust。 Iceberg-rust 是一个由社区驱动的项目&#xff0…

kotlin数据类型

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 Kotlin基本数值类型 基本数据类型包括 Byte、Short、Int、Long、Float、Double 整数类型 类型位宽最小值最大…

内存函数(C语言)

内存函数 以下函数的头文件&#xff1a;string.h 针对内存块进行处理的函数 memcpy 函数原型&#xff1a; void* memcpy(void* destination, const void* source, size_t num);目标空间地址 源空间地址num&#xff0c;被拷贝的字节个数 返回目标空间的起始地…

在 PostgreSQL 里如何实现数据的实时监控和性能瓶颈的快速定位?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 在 PostgreSQL 里如何实现数据的实时监控和性能瓶颈的快速定位一、数据实时监控的重要性二、PostgreSQ…

【C语言初阶】探索编程基础:深入理解分支与循环语句的奥秘

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C语言 “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C语言入门 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀分支与循环语句 &#x1f4d2;1.…

聚类分析方法(三)

目录 五、聚类的质量评价&#xff08;一&#xff09;簇的数目估计&#xff08;二&#xff09;外部质量评价&#xff08;三&#xff09;内部质量评价 六、离群点挖掘&#xff08;一&#xff09;相关问题概述&#xff08;二&#xff09;基于距离的方法&#xff08;三&#xff09;…

element-plus 按需导入问题 404等问题

场景 新开一个项目&#xff0c;需要用element-plus这个ui库&#xff0c;使用按需引入。 这是我项目的一些版本号 "element-plus": "^2.7.6","vue": "^3.2.13","vue-router": "^4.0.3",过程&#xff08;看解决方法…

Unity发布webgl之后修改StreamingAssets 内的配置文件读取到的还是之前的配置文件的解决方案

问题描述 unity发布webgl之后&#xff0c;修改在StreamingAssets 中的配置信息&#xff0c;修改之后读取的还是之前的配置信息 读取配置文件的代码IEnumerator IE_WebGL_LoadWebSocketServerCopnfig(){var uri new System.Uri(Path.Combine(Application.streamingAssetsPath…

种田游戏扎堆,但玩家还有钱建设家园吗?

2024年暑期档&#xff0c;模拟经营游戏成为细分领域变化最为显著的一环。 6月26日泡泡玛特的《梦想家园》、7月10日《波西亚时光》手游版上线、还有心动跳跃整整研发了10年的《心动小镇》也将于7月17日上线。 事实上&#xff0c;从数量上看&#xff0c;模拟经营游戏并非小众品…

springboot 程序运行一段时间后收不到redis订阅的消息

springboot 程序运行一段时间后收不到redis订阅的消息 问题描述 程序启动后redis.user.two主题正常是可以收到消息的&#xff0c;发一条收一条&#xff0c;但是隔一段时间后&#xff1b;就收不到消息了&#xff1b; 此时如果你手动调用发送另外一个消息订阅redis.user.two2&…