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

news2024/9/21 20:45:29

文章目录

  • 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 部分类图

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++基础(一)

目录 1.不同版本的hello word&#xff01; 2.namespace和&#xff1a;&#xff1a;域作用限定符以及using 2.1 namespace 2.2&#xff1a;&#xff1a; 2.3using用于展开域 3.C输入和输出 4.缺省参数 5.重载 6.引用 6.1引用介绍 6.2 引用的特性 注意&#xff1a; 6.4 c…

基于CSS两种菜单创建方式

作者&#xff1a;私语茶馆 1.前言 HTML多种场景下需要菜单的实现&#xff0c;这里提供两种方式&#xff1a; &#xff08;1&#xff09;基于CSS的POPUP式菜单 特征&#xff1a;按需显示POPUP菜单&#xff0c;但布局是浮动的。 &#xff08;2&#xff09;覆盖式弹出菜单。 …

独家揭秘!五大内网穿透神器,访问你的私有服务

本文精心筛选了五款炙手可热的内网穿透工具&#xff0c;它们各怀绝技&#xff0c;无论您是企业用户、独立开发者&#xff0c;还是技术探索者&#xff0c;这篇文章都物有所值&#xff0c;废话不多说&#xff0c;主角们即将上场。 目录 1. 巴比达 - 安全至上的企业护航者 2. 花…

二次改写内容,ai智能写作软件最便捷

对于自媒体创作者&#xff0c;二次改写是获取内容的一个不错的方法&#xff0c;通过二次改写不仅能提高自媒体内容创作的效率问题&#xff0c;而且还解决了创作中因灵感缺失产不出内容的问题&#xff0c;但是二次改写内容也是需要讲究方法的&#xff0c;如果没有好的方法&#…

68种语言说爱你网页源码

浪漫至死不渝&#xff0c;68种语言说爱你网页源码&#xff0c;本地和上传服务器都可以用&#xff0c;无加密。 网页源码&#xff1a;68种语言说爱你单页.zip 下面是HTML代码CSS代码超过七百多行打包成文件了。 <!DOCTYPE html> <html lang"en"><h…

计算机体系结构和指令系统2

1.存储系统的层次结构 有的教材把安装在电脑内部的磁盘成为“辅存”&#xff0c;把U盘、光盘等成为外存&#xff0c;也有的教材把磁盘、U盘、光盘等统称为“辅存”或外存。 主存——辅存&#xff1a;实现虚拟存储系统&#xff0c;解决了主存容量不够&#xff0c;辅存速度不够的…

【密码学】实现消息认证或数字签名的几种方式

消息认证的目的是验证消息的完整性和确认消息的来源。数字签名的目的是不仅验证消息的完整性和来源&#xff0c;还提供了不可否认性。此外&#xff0c;数字签名还可以验证消息的创建时间&#xff0c;防止重放攻击。那么具体有哪些实现的方式呢&#xff1f; 一、仅提供消息认证…

2024年7月13日全国青少年信息素养大赛Python复赛小学高年级组真题

第一题 题目描述 握情况。他决定让每个人输入一个正整数 N (0≤N≤1000)&#xff0c;然后计算并输出(5*N)的值。请用 在一个神秘的王国里&#xff0c;国王希望通过一个简单的测试来评估他的子民对基 础数学运算的掌 Python 编写程序&#xff0c;程序执行后要求用户输入一个正…

【山东大学】web数据管理——复习笔记

写在前面 若有图片加载失败&#xff0c;请科学上网 。本文为对软件学院连老师的PPT课件总结所得的复习笔记&#xff0c;仅供参考。不保证对考点的全覆盖&#xff0c;以PPT为主。对往年考过的题相关知识点前面都标注了“考过”&#xff0c;并高亮&#xff0c;供参考。写的比较匆…

代码随想录第51天|单调栈

42. 接雨水 参考 思路1: 暴力解法 找每个柱子的左右高度超时 O(N^2) 思路2: 双指针优化 class Solution { public:int trap(vector<int>& height) {vector<int> lheight(height.size(), 0);vector<int> rheight(height.size(), 0);lheight[0] hei…

【超音速 专利 CN117576413A】基于全连接网络分类模型的AI涂布抓边处理方法及系统

申请号CN202311568976.4公开号&#xff08;公开&#xff09;CN117576413A申请日2023.11.22申请人&#xff08;公开&#xff09;超音速人工智能科技股份有限公司发明人&#xff08;公开&#xff09;张俊峰&#xff08;总&#xff09;; 杨培文&#xff08;总&#xff09;; 沈俊羽…

Python酷库之旅-第三方库Pandas(021)

目录 一、用法精讲 52、pandas.from_dummies函数 52-1、语法 52-2、参数 52-3、功能 52-4、返回值 52-5、说明 52-6、用法 52-6-1、数据准备 52-6-2、代码示例 52-6-3、结果输出 53、pandas.factorize函数 53-1、语法 53-2、参数 53-3、功能 53-4、返回值 53-…

网络割接方案通用模板

第一章 项目概述 1.1 编写目的 为规范“十三五”以来&#xff0c;随着移动互联新技术的发展以及我国政府职能的不断转变&#xff0c; 我国的政法网络进入新的发展阶段&#xff0c;跨地域、跨部门、跨系统的信息共享、业务 协同以及智慧政务等成为了各地电子政务的重点建设内容。…

【Django+Vue3 线上教育平台项目实战】构建高效线上教育平台之首页模块

文章目录 前言一、导航功能实现a.效果图&#xff1a;b.后端代码c.前端代码 二、轮播图功能实现a.效果图b.后端代码c.前端代码 三、标签栏功能实现a.效果图b.后端代码c.前端代码 四、侧边栏功能实现1.整体效果图2.侧边栏功能实现a.效果图b.后端代码c.前端代码 3.侧边栏展示分类及…

自动化故障排查与运维团队建设策略

在当前的运维领域&#xff0c;自动化故障排查已成为提高运维效率、保障系统稳定性的关键手段。为了进一步提升故障排查能力&#xff0c;并建立高效的运维团队&#xff0c;以下策略值得深入考虑和实施。 一、自动化故障排查流程与工具 标准化故障排查流程 建立一套标准化的故…

121. 小红的区间翻转(卡码网周赛第二十五期(23年B站笔试真题))

题目链接 121. 小红的区间翻转&#xff08;卡码网周赛第二十五期&#xff08;23年B站笔试真题&#xff09;&#xff09; 题目描述 小红拿到了两个长度为 n 的数组 a 和 b&#xff0c;她仅可以执行一次以下翻转操作&#xff1a;选择a数组中的一个区间[i, j]&#xff0c;&#x…

Nginx入门到精通五(动静分离)

下面内容整理自bilibili-尚硅谷-Nginx青铜到王者视频教程 Nginx相关文章 Nginx入门到精通一&#xff08;基本概念介绍&#xff09;-CSDN博客 Nginx入门到精通二&#xff08;安装配置&#xff09;-CSDN博客 Nginx入门到精通三&#xff08;Nginx实例1&#xff1a;反向代理&a…

全球生成式AI 产品分析报告

全球生成式AI 产品研究报告 时代背景 全球进入AI驱动的生产革命&#xff0c;生成式技术是时代际遇。中美在生成式AI产业展开科技竞争&#xff0c;全栈组合拳拉锯发展。‍ 技术变革 Transformer架构优化模型泛化的训推能力与理解生成的内容能力。文本模态达高应用成熟度&…

OpenCV图像处理——获取穿过圆的直线与圆相交的两个点

在OpenCV中&#xff0c;没有直接的函数来计算直线与圆的交点&#xff0c;但可以通过数学方法来实现这一功能。以下是计算直线与圆交点的步骤&#xff0c;以及相应的C代码示例&#xff1a; 确定直线方程&#xff1a;使用直线上的两个点 P 1 ( x 1 , y 1 ) P1(x1, y1) P1(x1,y1)和…

旷野之间17 - 适合所有人的 Transformer

最初出现在著名论文《Attention is all your need》中&#xff0c;基于Transformer的架构已成为大多数成功的人工智能模型中必不可少的。 然而&#xff0c;许多用户甚至基于人工智能的产品的创造者可能不了解Transformer是什么或它是如何工作的。 嗯&#xff0c;阅读研究论文…