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

2024-07-13 Unity AI状态机2 —— 项目介绍

文章目录

  • 1 项目介绍
  • 2 模块介绍
    • 2.1 BaseState
    • 2.2 ...State
      • 2.2.1 PatrolState
      • 2.2.2 ChaseState / AttackState / BackState
    • 2.3 StateMachine
    • 2.4 Monster
  • 3 其他功能
  • 4 类图

项目借鉴 B 站唐老狮 2023年直播内容。 点击前往唐老狮 B 站主页。

1 项目介绍

​ 本项目使用 Unity 2022.3.32f1c1,实现基本的 AI 框架。其中,用 Cube(绿色)代替怪物模型,Cube(红色)代替玩家,即 AI 目标。

image-20240713164018099

​ 项目地址:https://github.com/zheliku/StateMachine_AI。

2 模块介绍

2.1 BaseState

​ 在框架介绍的基础上,添加了两个方法:

  1. DistanceOfXZ(Vector3, Vector3)

    用于计算 xz 平面上的距离(不考虑 y 轴方向)。

  2. DrawGizmos()

    用于辅助绘制范围,在 Scene 窗口中显示。

using UnityEngine;/// <summary>
/// 状态基类
/// </summary>
public abstract class BaseState
{public virtual EAIState AIState { get; } // 状态类型protected StateMachine _stateMachine; // 附属的状态机public BaseState(StateMachine stateMachine) {_stateMachine = stateMachine;}public abstract void OnStateEnter();public abstract void OnStateUpdate();public abstract void OnStateExit();/// <summary>/// 辅助绘制范围,不强制重写/// </summary>public virtual void DrawGizmos() { }/// <summary>/// XZ 平面上的距离/// </summary>protected float DistanceOfXZ(Vector3 pos1, Vector3 pos2) {pos1.y = pos2.y = 0;return Vector3.Distance(pos1, pos2);}
}

2.2 …State

​ 项目实现了 4 种 AI 状态,包括

  • PatrolState(巡逻状态)
  • ChaseState(追逐状态)
  • AttackState(攻击状态)
  • BackState(返回状态)

2.2.1 PatrolState

​ PatrolState 实现较为详细,其将巡逻方式分为 3 种:

public enum EPatrolType
{Stay,       // 原地播放某个动作(睡觉、放哨等)CircleMove, // 圆形范围内随机移动PathMove    // 按照路径移动
}

​ 巡逻数据可以直接在 PatrolState 类中声明。本项目选择封装在 PatrolStateData 中。所有 Data 均继承 ScripteObject 类,可以在 Project 窗口中右键直接创建并配置对应数据。

  • PatrolStateData:所有巡逻种类的共有数据。

  • PatrolStateStayData:原地巡逻的数据。

  • PatrolStateMoveData:移动巡逻的数据。

  • PatrolStateCircleMoveData:圆形范围移动巡逻的数据。

  • PatrolStatePathMoveData:路径移动巡逻的数据。

StateMachine_AI PatrolStateData
  1. Stay:

    原地播放某个动作,因此需要 AI 动作枚举。目前只添加 Sleep 动作。

    public enum EAction
    {Sleep,
    }
    
  2. Move:

    范围内随机移动,分为两种:Circle、Path。区别是获取下一次目标位置的方式不同,因此提取出如下逻辑:

    public class PatrolState : BaseState
    {private PatrolStateData _data; // 巡逻数据.../// <summary>/// 移动/// </summary>private void OnMoveUpdate(IAIObject aiObject, EPatrolType moveType) {var data = (PatrolStateMoveData)_data; // 转化数据..._data.targetPos = moveType switch {EPatrolType.CircleMove => CalCircleTargetPos((PatrolStateCircleMoveData)data),EPatrolType.PathMove   => CalPathTargetPos((PatrolStatePathMoveData)data),_                      => throw new ArgumentOutOfRangeException(nameof(moveType), moveType, null)};...}/// <summary>/// 更新圆形范围目标位置/// </summary>private Vector3 CalCircleTargetPos(PatrolStateCircleMoveData data) { ... }/// <summary>/// 更新路径范围目标位置/// </summary>private Vector3 CalPathTargetPos(PatrolStatePathMoveData data) { ... }...
    }
    

2.2.2 ChaseState / AttackState / BackState

​ 该 3 个状态都遵循以下大致框架:

using UnityEngine;public class ...State : BaseState
{private ...StateData _data;public override EAIState AIState { get => ...; }public AttackState(StateMachine stateMachine) : base(stateMachine) {// 加载数据var data = Resources.Load<...StateData>("StateData/.../...StateData");_data = Object.Instantiate(data);}public override void OnStateEnter() { ... }public override void OnStateUpdate() { ... }public override void OnStateExit() { ... }...
}

2.3 StateMachine

​ AddState()、ChangeState() 和 UpdateState() 逻辑如下:

/// <summary>
/// 添加 AI 状态
/// </summary>
public void AddState(EAIState state) {switch (state) {case EAIState.Patrol:_stateDic.Add(state, new PatrolState(this));break;case EAIState.Back:_stateDic.Add(state, new BackState(this));break;case EAIState.Chase:_stateDic.Add(state, new ChaseState(this));break;case EAIState.Attack:_stateDic.Add(state, new AttackState(this));break;default: throw new ArgumentOutOfRangeException(nameof(state), state, null);}
}/// <summary>
/// 切换状态
/// </summary>
/// <param name="state"></param>
public void ChangeState(EAIState state) {_nowState?.OnStateExit(); // 退出状态if (_stateDic.TryGetValue(state, out BaseState nowState)) { // 进入状态_nowState = nowState;_nowState.OnStateEnter();}
}/// <summary>
/// 更新当前状态
/// </summary>
public void UpdateState() {_nowState?.OnStateUpdate();_nowState?.DrawGizmos(); // 辅助绘图
}

2.4 Monster

​ 怪物类实现了 IAIObject 接口(详见 2024-07-12 Unity AI状态机1 —— 框架介绍),通过 Unity 导航系统中的 NavMeshAgent 实现基本移动。除了 IAIObject 接口,还包含一些自己的数据。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;public class Monster : MonoBehaviour, IAIObject
{public Transform  targetTransform; // 目标位置public GameObject bullet;          // 子弹public float      attackRange = 3; // 攻击范围private Quaternion _startRotation;  // 记录开始攻击时的角度private Quaternion _targetRotation; // 记录目标角度private float      _rotateTime;     // 旋转计时private NavMeshAgent _navMeshAgent; // 导航代理private StateMachine _aiMachine; // AI 状态机private void Start() {_navMeshAgent = GetComponent<NavMeshAgent>();BornPos       = transform.position;_aiMachine = new StateMachine();_aiMachine.Init(this);// 为 AI 添加巡逻状态_aiMachine.AddState(EAIState.Patrol);_aiMachine.AddState(EAIState.Chase);_aiMachine.AddState(EAIState.Attack);_aiMachine.AddState(EAIState.Back);// 更改初始状态_aiMachine.ChangeState(EAIState.Patrol);}private void Update() {_aiMachine.UpdateState();}#region IAIObject 接口实现...#endregion
}

3 其他功能

​ 为了辅助绘图,为 BaseState 添加了 DrawGizmos() 方法(virtual),子类可以实现该方法,在 Scene 窗口中绘制辅助线。同时,在 BaseState 中添加 DistanceOfXZ() 方法,以便所有状态都可使用。以下是 BaseState 的全部逻辑:

using UnityEngine;/// <summary>
/// 状态基类
/// </summary>
public abstract class BaseState
{public virtual EAIState AIState { get; } // 状态类型protected StateMachine _stateMachine; // 附属的状态机public BaseState(StateMachine stateMachine) {_stateMachine = stateMachine;}public abstract void OnStateEnter();public abstract void OnStateUpdate();public abstract void OnStateExit();/// <summary>/// 辅助绘制范围,不强制重写/// </summary>public virtual void DrawGizmos() { }/// <summary>/// XZ 平面上的距离/// </summary>protected float DistanceOfXZ(Vector3 pos1, Vector3 pos2) { ... }
}

​ 辅助绘图时,使用 Unity Asset Store 中的插件 DrawXXL 实现,例如 ChaseState 中的 DrawGizmos 如下:

public override void DrawGizmos() {var aiObject = _stateMachine.AIObject;// 绘制攻击范围DrawShapes.Circle(aiObject.Transform.position, aiObject.AttackRange, Color.red,Vector3.up, lineWidth: 0.05f);// 绘制脱离范围DrawShapes.Circle(aiObject.Transform.position, _data.chaseDistance, new Color(1, 0.5f, 0),Vector3.up, lineWidth: 0.05f, outlineStyle: DrawBasics.LineStyle.dotted);
}

4 类图

StateMachine_AI 部分类图

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Dify中的高质量索引模式实现过程
  • 华为USG6000V防火墙NAT智能选举
  • Python和C++行人轨迹预推算和空间机器人多传感融合双图算法模型
  • 字节码编程之bytebuddy结合javaagent支持多种监控方式
  • 【Spring全家桶系列之核心篇 | Spring Cloud】 - 第七章 掌握Gateway核心技术,实现高效路由与转发
  • 灵雀云AML:赋能金融AI,构建数智时代核心竞争力
  • Android SurfaceView 组件介绍,挖洞原理详解
  • Apache httpd-vhosts.conf 配置详解(附Demo)
  • 【学习笔记】无人机(UAV)在3GPP系统中的增强支持(十一)-无人机服务可用性用例需求
  • 不常用的第三方服务集成
  • [米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-22 TPG图像测试数据发生器设计
  • CSS实现从上往下过渡效果
  • 【算法基础】Dijkstra 算法
  • 乘积量化pq:将高维向量压缩 97%
  • SSM 整合(Spring + MyBatis;Spring + Spring MVC)
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • CentOS 7 修改主机名
  • jQuery(一)
  • MQ框架的比较
  • PAT A1120
  • 你真的知道 == 和 equals 的区别吗?
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 试着探索高并发下的系统架构面貌
  • 问题之ssh中Host key verification failed的解决
  • 正则与JS中的正则
  • nb
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • # 利刃出鞘_Tomcat 核心原理解析(七)
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • (1)无线电失控保护(二)
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (MATLAB)第五章-矩阵运算
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (zhuan) 一些RL的文献(及笔记)
  • (办公)springboot配置aop处理请求.
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (论文阅读40-45)图像描述1
  • (十二)Flink Table API
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (转) RFS+AutoItLibrary测试web对话框
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • .net core Redis 使用有序集合实现延迟队列
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法