Unity——模拟AI视觉

news2024/11/24 7:42:57

人类的视觉系统有以下几个特点:

  1. 距离有限。近处看得清,远处看不清
  2. 容易被遮挡。不能穿过任何不透明的障碍物
  3. 视野范围大约为90度。实现正前方信息丰富,具有色彩和细节;实现外侧的部分只有轮廓和运动信息
  4. 注意力有限。当关注某个具体的方位或物体时,其他部分被忽略,如魔术中的障眼法总是能骗过观众

对AI视觉的模拟就是基于以上这些基本特点,在此基础上有各种各样的实现思路。如果从第二个特点出发,很容易联想到射线也具有不能穿过物体的特点,而且涉嫌也可以设定发射距离。最大的难点在于“视野范围大约为90°”这一特点,在3D自由视角的游戏中,视野范围是一个圆锥体,在俯视角游戏中视野范围是一个扇形区域。无论圆锥体还是扇形区域,都无法直接用射线、球形射线或盒子射线等简单形状模拟,必须找到一种变通的解决方案。

针对用射线模拟扇形区域的问题,这里给出一个易于理解的方法:用多条射线模拟区域。

脚本和效果图如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AIEnemy : MonoBehaviour
{
    public int viewRadius = 4;  //视野距离
    public int viewLines = 30; //射线数量
    void Start(){ }

   
    void Update()
    {
        FieldOfView();
         
    }
    void FieldOfView()
    {
        //获得最左边那条射线的向量,相对正前方,角度是-45°
        Vector3 forward_left = Quaternion.Euler(0, -45, 0) * transform.forward * viewRadius;
        //依次处理每条射线
        for(int i = 0; i <= viewLines; i++)
        {
            Vector3 v = Quaternion.Euler(0, (90.0f / viewLines) * i, 0) * forward_left;
            //角色位置+v,就是射线终点pos
            Vector3 pos = transform.position + v;
            //从玩家为之到pos画线段,只会在编辑器里看到
            Debug.DrawLine(transform.position, pos, Color.red);
        }
    }
}

 6580e9a0e296452c9a8e469c04c4a939.png

但这只是第一步,实际还需要将射线发射出去才可以。只有将射线发射出去才能判断是否碰到了障碍物,如果碰到障碍物,视线端点就落在碰撞点上。将FieldOfView的代码修改如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AIEnemy : MonoBehaviour
{
    public int viewRadius = 4;  //视野距离
    public int viewLines = 30; //射线数量
    void Start(){ }

   
    void Update()
    {
        FieldOfView();
    }
    void FieldOfView()
    {
        //获得最左边那条射线的向量,相对正前方,角度是-45°
        Vector3 forward_left = Quaternion.Euler(0, -45, 0) * transform.forward * viewRadius;
        //依次处理每条射线
        for(int i = 0; i <= viewLines; i++)
        {
            Vector3 v = Quaternion.Euler(0, (90.0f / viewLines) * i, 0) * forward_left;
            //角色位置+v,就是射线终点pos
            Vector3 pos = transform.position + v;

            //实际发射射线。注意RayCast的参数,重载很多容易搞错
            RaycastHit hitInfo;
            if(Physics.Raycast(transform.position,v,out hitInfo, viewRadius))
            {
                //碰到物体,终点改为碰到的点
                pos = hitInfo.point;
            }

            //从玩家位置到pos画线段,只会在编辑器里看到
            Debug.DrawLine(transform.position, pos, Color.red);
        }
    }
}

效果图:

81d26d83217049f3a3c636cc1f97dce6.png

修改之后 ,用任意物体阻碍射线,会发现射线确实出现了被阻挡的效果。

在实际游戏中,当射线集中玩家时,就表示敌人发现了玩家,这时就需要进行进一步处理。简单来说,就是把逻辑代码插入上述代码的if代码段里。

以上方法虽然实现了逻辑功能,但没有清晰表现出视野范围。在经典的潜入为游戏中,为了给玩家提示具体的敌人视野范围,会加入明显的画面表现。例如在《崩坏3》中的一些关卡里,就有机器人前面有一个非常夸张的红色扇形,用来显示敌人的视野区域。

下面利用程序建模的方法,将视野范围显示出来,准备工作如下:

  1. 给敌人新建一个空物体作为子物体,命名为view,位置归0
  2. 添加Mesh Renderer组件和Mesh Filter组件
  3. 新建一个材质,将其渲染模式改为Transparent(透明),颜色改为绿色,透明度改为150左右。为检验材质设置可以将它拖曳到立方体上进行测试,观察透明度是否合适
  4. 将材质球拖曳到新建的view物体上。由于view物体暂时没有模型,因此显示不出来

由于视野范围是动态变化的,因此需要用代码拼出一个扇形平面模型并赋予view物体,以表现视野范围。由于改动较多,将修改后的完整脚本展示如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AIEnemy : MonoBehaviour
{
    public int viewRadius = 4;  //视野距离
    public int viewLines = 30; //射线数量
    public MeshFilter viewMeshFilter;
    List<Vector3> viewVerts;  //定点列表
    List<int> viewIndices;  //定点序号列表
    void Start()
    {
        Transform view = transform.Find("view");
        viewMeshFilter = view.GetComponent<MeshFilter>();
        viewVerts=new List<Vector3>();
        viewIndices = new List<int>();
    }

   
    void Update()
    {
        FieldOfView();
    }
    void FieldOfView()
    {
        viewVerts.Clear();
        viewVerts.Add(Vector3.zero);  //加入起点坐标,局部坐标系


        //获得最左边那条射线的向量,相对正前方,角度是-45°
        Vector3 forward_left = Quaternion.Euler(0, -45, 0) * transform.forward * viewRadius;
        //依次处理每条射线
        for(int i = 0; i <= viewLines; i++)
        {
            Vector3 v = Quaternion.Euler(0, (90.0f / viewLines) * i, 0) * forward_left;
            //角色位置+v,就是射线终点pos
            Vector3 pos = transform.position + v;

            //实际发射射线。注意RayCast的参数,重载很多容易搞错
            RaycastHit hitInfo;
            if(Physics.Raycast(transform.position,v,out hitInfo, viewRadius))
            {
                //碰到物体,终点改为碰到的点
                pos = hitInfo.point;
            }
            //将每个点的位置加入列表,注意转为局部坐标系
            Vector3 p = transform.InverseTransformPoint(pos);
            viewVerts.Add(p);
         
        }
        //根据顶点绘制模型
        RefreshView();
    }
    void RefreshView()
    {
        viewIndices.Clear();
        //逐个加入三角面,每个三角面都以起点开始
        for(int i = 1; i < viewVerts.Count-1; i++)
        {
            viewIndices.Add(0);
            viewIndices.Add(i);
            viewIndices.Add(i+1);
        }
        //填写Mesh信息
       Mesh mesh = new Mesh();
        mesh.vertices= viewVerts.ToArray();
        mesh.triangles = viewIndices.ToArray();
        viewMeshFilter.mesh = mesh;
    }
}

简单来说,网格信息是由"顶点"和"顶点序号"组成的。顶点是空间位置,因此用Vector3表示,所有顶点放在一个大数组中,每个顶点对应一个数组的下标。

而顶点序号指的正是数组的下标。由于网格是由三角面组成的,因此3个序号为一组,3个又3个地填写顶点序号,就代表着1个又1个的三角面。

三角面的正反:

三角面是由3个顶点序号组成的,而3个点的顺序可能有两种,分别是a-b-c和a-c-b,即顺时针和逆时针。一般的材质默认都是单面渲染,每个面只能从一侧看到,而从另一侧看不到。

只要是顺时针具体点的先后顺序不同是没有影响的,如a-b-c=b-c-a=c-a-b,而c-b-a就是相反的。

如果在写代码时看不到三角面,那么就查看反面是否显示。交换3个点中的任意2个序号的顺序,即可将三角面反向。

准备好顶点列表和顶点序列表后,就可以创建Mesh对象了。Mesh对象的几个属性都是数组类型,因此需要用List.ToArray方法将列表转为数组。最后将Mesh对象赋予Mesh FIlter组件即可。

按照上述步骤操作并运行,就会得到一个半透明的绿色扇面模型。而且绿色范围被障碍物阻挡时仍能正确显示范围。

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

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

相关文章

深入浅出学Verilog--数据类型

1、数值类型 在Verilog可以用4种数值来描述其构建的电路的电平逻辑&#xff0c;除了event类型和real类型外&#xff0c;几乎所有的数据类型都可以用这4种数值来表示。 0&#xff1a;代表逻辑0&#xff0c;或者条件“假”1&#xff1a;代表逻辑1&#xff0c;或者条件“真”x或X…

ubuntu基本配置

记录一下每次重新安装系统之后都要进程的操作 更新源 更新源的教程 sudo bash -c "cat << EOF > /etc/apt/sources.list && apt update deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse deb-src http://mirrors.a…

《Envoy 代理:云原生时代的流量管理》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

持安科技孙维伯:零信任理念下的实战攻防:ISC2023数字小镇演讲

近日&#xff0c;在ISC 2023第十一届互联网安全大会上&#xff0c;持安科技联合创始人孙维伯作为零信任办公安全赛道代表&#xff0c;亮相数字小镇New50&#xff0c;并发表《全方位防御&#xff1a;零信任理念下的实战攻防》主题演讲。 以下是本次演讲实录&#xff1a; 这几年…

第9章_freeRTOS入门与工程实践之任务管理

本教程基于韦东山百问网出的 DShanMCU-F103开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id724601559592 配套资料获取&#xff1a;https://rtos.100ask.net/zh/freeRTOS/DShanMCU-F103 freeRTOS系列教程之freeRTOS入…

考虑负荷满意度的微电网运行多目标优化方法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

基本Dos命令

1.打开cmd的方式 &#xff08;1&#xff09;winR&#xff0c;输入cmd即可 &#xff08;2&#xff09;在任意文件夹下面&#xff0c;按住shift键后点击鼠标右键&#xff0c;即可在此文件夹目录下打开命令行窗口。 &#xff08;3&#xff09;资源管理器的地址栏前面加上 cmd…

云原生Kubernetes:K8S实用插件和工具

目录 一、理论 1.Kubectl插件 2.kubens 3.krew 二、实验 1.kubectl插件 2.kubens 3.krew 一、理论 1.kubectl插件 &#xff08;1&#xff09;概念 kubectl插件其实就是以kubectl-为前缀的任意可执行文件&#xff0c;比如执行: ln -s /bin/echo /usr/local/bin/ku…

Mybatis-plus 自动生成代码

Mybatis-plus 自动生成代码 这里主要是介绍 Mybatis-plus 自动生成代码操作流程&#xff0c;方便以后查阅&#xff01;&#xff01;&#xff01; 一、简介 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis …

一点整理

&#xff08;1&#xff09; 美国在2010年以后开始流行数字化转型的。 在2010年以前&#xff0c; 2006年社交网络FB “YOU”&#xff1a;在2004-2006 Web2.0热之前&#xff0c;企业是无法直接触达到每个消费者的2006年Amazon电子商务&#xff1a;这个是我瞎凑的&#xff0c;但因…

js中如何实现一个简单的防抖函数?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 防抖函数⭐ 使用示例⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏…

第一颗国产 单/双端口 MIPI CSI/DSI 至 HDMI 1.4 发射器 芯片LT9611

1. 描述 LT9611 MIPI DSI/CSI 至 HDMI1.4 桥接器具有双端口 MIPI D-PHY 接收器前端配置&#xff0c;每个端口有 4 个数据通道&#xff0c;每个数据通道以 2Gbps 的速度工作&#xff0c;最大输入带宽为 16Gbps。 该桥接器提供一个 HDMI 数据输出&#xff0c;具有可选的 …

(面试经典刷题)挑战一周刷完150道-Python版本-第1天(11个题)

一、合并数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff1a;最终&#xff0…

Multisim14.0仿真(十一)差分放大器

一、仿真原理图&#xff1a; 二、仿真效果图&#xff1a;

c++仿照string类,完成myString 类

#include <iostream> #include <cstring>using namespace std;class myString {private:char *str; //记录c风格的字符串int size; //记录字符串的实际长度public://无参构造myString():size(10){str new char[size1]; //构造出一个长…

Java“牵手”1688商品详情数据,1688商品详情接口,1688API接口申请指南

1688商品详情API接口的作用是获取1688平台上某个商品的详细信息&#xff0c;包括商品标题、价格、图片、规格、参数、店铺信息等。 开发者可以通过该接口获取到商品的原始数据&#xff0c;方便进行数据分析、价格比较、爬取等操作。通过该接口获取到的商品详情数据可以结合其他…

tkintter四大按钮:Button,Checkbutton, Radiobutton, Menubutton

文章目录 四大按钮Button连击MenubuttonCheckbuttonRadiobutton tkinter系列&#xff1a; GUI初步&#x1f48e;布局&#x1f48e;绑定变量&#x1f48e;绑定事件&#x1f48e;消息框&#x1f48e;文件对话框控件样式扫雷小游戏&#x1f48e;强行表白神器 四大按钮 tkinter中…

有哪些常用的压力测试工具,移动网站压力测试流程有哪些内容?

压力测试工具 在移动网络飞速发展的今天&#xff0c;移动网站成为企业对外宣传、开展业务的重要窗口&#xff0c; 对于访客量大的网站来说&#xff0c;一旦其无法承受过于巨大的流量&#xff0c;就会使网站崩溃&#xff0c;进而影响公司正常的业务。与之相对应的&#xff0c;…

故障排除指南:解决 Kibana Discover 加载中的 6 个常见问题

作者&#xff1a;Steffanie Nestor Discover 是 Elastic 的核心 Kibana UI&#xff0c;用于搜索、过滤和检查&#xff08;时间序列&#xff09;数据。 可视化用于数据聚合/摘要。 Discover UI 对于大数据 Elasticsearch 响应具有弹性&#xff0c;但有时会因&#xff08;未压缩的…

VRTK4⭐一.VRTK4和VRTK的区别 , 及VRTK4简介

文章目录 &#x1f7e5; VRTK4和VRTK的区别1️⃣ 版本区别2️⃣安装方式区别 &#x1f7e7; 安装VRTK41️⃣ AssetStore网址2️⃣安装不同功能的包 &#x1f7e9;Tilia的独立功能包介绍及配置方法&#x1f381;Tilia.CameraRigs.SpatialSimulator.Unity [重要]&#x1f381;Til…