Unity--射线检测--RayCast

Unity–射线检测–RayCast

1.射线检测的含义

射线检测,根据名称而言,使用一条射线来检测是击中了某个物体/多个物体

射线检测的包含两个部分: 射线检测

2.射线检测可以用在哪些地方

  1. 射击游戏
    • 玩家的瞄准和射击:检测玩家视线是否与敌人或其他目标相交。
    • 子弹轨迹和效果:模拟子弹的飞行路径和击中效果。
  2. 交互和UI
    • 鼠标点击检测:检测玩家的鼠标点击是否与游戏对象或UI元素相交。
    • 触摸屏交互:在移动设备上检测玩家的触摸是否与特定的游戏元素相交。
  3. 角色控制器和AI
    • 视野检测:NPC或敌人在一定范围内检测玩家或其他角色。
    • 碰撞避免:AI角色在移动时使用射线检测来避免碰撞。
  4. 虚拟现实(VR)和增强现实(AR)
    • 眼睛或手部追踪:在VR中检测玩家的视线或手部位置。
    • 对象交互:在AR中检测玩家是否与虚拟对象相交。

3.Unity中的射线Ray

在日常生活中的场景射线是很多地方都可以见到的, 比如手电筒,ppt激光翻页笔,庆余年中的镭射眼

射线由一个起点,一个方向和一个距离构成, 即: origin , directiondistance

在物理上射线的距离是无限远的, 因此物理上的射线只有一个起点和一个方向. 在游戏中,射线的最大距离也是被系统限制的, 一般我们是自定义距离,例如1000.0米. 以下是关于射线Ray的说明.

在Unity中,射线Ray是一个结构体,结构体积的基本成员包括origin, directionGetPoint. 也就是起点,方向和沿射线一定距离的点的位置. 以下是Unity中有关Ray的代码

using System;namespace UnityEngine
{public struct Ray : IFormattable{        public Ray(Vector3 origin, Vector3 direction);public Vector3 origin { get; set; } // 起点(向量)public Vector3 direction { get; set; }// 方向(向量)public Vector3 GetPoint(float distance);// 沿着射线一定距离的点(向量)public override string ToString();public string ToString(string format);public string ToString(string format, IFormatProvider formatProvider);}
}

3.1 构建射线

根据上面的代码,可以看出,可以直接Ray的构造函数来构建一条射线,例如:

Ray ray = new Ray(Vector3.zero, Vector3.forward); // 射线的起点 + 射线的方向

根据上面的代码,我们可以看出,射线的起点是Unity中世界坐标的原点(0,0,0), 射线的方向是世界坐标的向前方向.

3.2 如何显示射线

现实中我们的上激光笔,手电筒是可以看见的,而Unity中的射线是看不见,因此,如果要将射线显示出来,我们可以使用的方法有

  • 使用Debug.DrawRay()进行显示射线
  • 使用LineRenderer组件将射线绘制出来

4.Unity中的射线检测

只是将射线构建出来或者显示出来并没有什么意义, 这就相当于手上拿着一个工具,不用工具来干活一个道理.

如何使用射线来进行物体检测.

仔细思考一下: 日常中我们的激光笔或者手电筒发出激光后可以在墙上显示出来光点或者照亮某个地方.这说明墙面是可以被交互的的,换句话说就是射线碰撞到了物体/检测到了物体. 在Unity中使用以下的API来判断是否检测到了物体.

4.1Raycat 函数

// 基础版API
public static bool Raycast(Ray ray);public static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask);
public static bool Raycast(Ray ray, out RaycastHit hitInfo);
public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance);// 常用版API
public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);public static bool Raycast(Ray ray, float maxDistance);

要使用射线检测, 需要使用Unity中的Physics库, 里面包含都是和物理相关的静态函数

看到上面的这么的重载函数,我们往往不知道使用哪一个. 其实, 只需要基础基本的即可, 参数多的其实就是根据需求来使用不同的重载参数.

首先看基础版本的API public static bool Raycast(Ray ray); 根据返回值我们可以获取了解到的是射线是否击中了一个物体,击中了就返回true,没有击中就返回false.

4.2HitInfo 结构体

在另外一个API中射线检测public static bool Raycast(Ray ray, out RaycastHit hitInfo);中多了一个参数hitInfo

这个hitInfo参数就是射线击中的物体的信息结构体. 该结构体比较大,指的包含的信息比较多,这和Unreal Engine中的射线检测击中物体的结果(FHitResult)是类似的. 从宏观上来讲或者从现实上来讲, 射线击中的物体, 我们可以获取物体本身的信息和击中点的信息

  • 物体的信息, 通过transfor可以获得所有信息
  • 击中点的信息

获取物体的信息很好理解: 比如物体的名称,物体的transfrom组件,该物体的Tag…

获取击中点的信息即射线击中物体的一个点(命中点)的信息:比如该点的坐标,法线(normal),法平面,该点的顶点颜色

以下是RaycastHit结构体的信息, 其中常用的已添加注释. 没有添加的注释的也有很多是常用的,比如lightmapCoord是光照贴图坐标, 用于渲染的.

namespace UnityEngine
{public struct RaycastHit{public Collider collider { get; }						// 碰撞器public int colliderInstanceID { get; }public Vector3 point { get; set; }						// 击中的点(命中点)public Vector3 normal { get; set; }						// 命中点的法线public Vector3 barycentricCoordinate { get; set; }		 // 重心坐标public float distance { get; set; }						// 命中点距离射线起点的距离public int triangleIndex { get; }public Vector2 textureCoord { get; }public Vector2 textureCoord2 { get; }public Transform transform { get; }						// Transform组件public Rigidbody rigidbody { get; }						// 刚体组件public ArticulationBody articulationBody { get; }public Vector2 lightmapCoord { get; }public Vector2 textureCoord1 { get; }}
}

也就是说HitInfo保存了我们击中物体的信息, 我们可以通过该信息来做更多的事情

4.3 layerMask

在Unity中,layerMask 是一个用于控制物理碰撞、光线投射、射线检测等操作的对象层选择机制。通过设置 layerMask,你可以指定哪些层应该被包括或排除在这些操作中。

继续回到常用的射线检测API public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);中.

在上面的API中,maxDistance这个不用多说, 就是射线的距离,系统也有一个设定最大值为Mathf.Infinity. 该APi中还有一个参数 layerMask, 也就是层级遮罩.

在Unity中区分游戏对象, 我们可以通过添加标签的方式将游戏对象区分开来, 即添加Tag. 但是Tag比较麻烦, 需要我们手动输入标签名,手敲还容易敲错. 同时, 由于是字符串, 在Unity底层计算的时候速度要慢一点. 在在Unity中即有层的概念.

在Unity中的layerMask中包含32层, 其中部分层级是系统已经使用了的,比如Player层, UI层. 还有很多层我们没有被系统使用, 我们可以添加层, 然后给游戏对象添加童工层的方式来分类.

如何添加层? 需要在任何一个物体的Insepector面板上点击Layer,然后点击AddLayer即可, 然后将需要需要修改层级的物体,手动指定层即可.

Unity_RayCast_AddLayer

手动添加了Layer后要如何使用.

public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);中的layereMask我们了解到它使一个int类型的整数, 但是, 我们不能直接填写数字,填写规则使用移位操作,

如何填写layerMask:

  1. 获取Layer Mask值

    • 每个层都有一个对应的整数值,从0开始。例如,默认层(Default)的值为0,UI层通常为5。
    • 要为特定层创建layerMask,可以使用 1 << LayerMask.NameToLayer("LayerName")。这将返回一个整数值,表示该层的layerMask。
  2. 组合多个层

    • 如果你想要组合多个层,可以使用位或操作符 |。例如,layerMask = LayerMask.GetMask("Layer1", "Layer2") 会创建一个layerMask,包括 “Layer1” 和 “Layer2”。
  3. 排除层

    • 要排除一个层,可以先创建一个包含所有层的mask,然后使用位异或操作符 ^ 来排除特定层。例如,layerMask = ~LayerMask.GetMask("ExcludeLayer")
  4. 检查层

    • 要检查一个对象是否在指定的layerMask中,可以使用 layerMask.value & (1 << gameObject.layer)。如果结果不为0,则表示对象在layerMask中。
    // 示例代码
    // 创建一个包含Layer1和Layer2的layerMask
    int layerMask = LayerMask.GetMask("Layer1", "Layer2");// 排除Layer3
    layerMask = ~LayerMask.GetMask("Layer3");// 使用layerMask进行射线检测
    RaycastHit hit;
    if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
    {// 处理射线击中的对象
    }

5.射线检测代码

以下是使用不含有hitInfo和含有hitInfo参数的代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RayCast : MonoBehaviour
{/// 构建一条射线 : 射线产生的起始点 + 射线的方向.private Ray ray1 = new Ray(Vector3.zero, Vector3.forward);// 射线距离private float rayDistance = 100.0f;// 击中的判定结果bool hitResult = false;// 射线击中的物体private RaycastHit hitInfo;void Start(){// 不含有hitInfo的函数bool result1 = Physics.Raycast(Vector3.zero + new Vector3(0,0,10), Vector3.forward, 1000.0f, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);if (result1){Debug.Log("射线击中物体");}// 含有hitInfo的函数hitResult =  Physics.Raycast(ray1, out hitInfo, rayDistance, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);if (hitResult == true){print(hitInfo.collider.name);print(hitInfo.transform);print(hitInfo.point);}}       
}

6.射线警报器

使用射线检测实现一个旋转的激光笔, 遇到物体射线长度就减少, 需要绘制出射线

需要用到的技能: 射线检测 + 旋转 + LineRender

以下是代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RotateRay : MonoBehaviour
{// LinerRender组件private LineRenderer lineRenderer = null;// 构建一条射线 : 射线产生的起始点 + 射线的方向.private Ray ray = new Ray(Vector3.zero, Vector3.forward);// 射线距离private float rayDistance = 1000.0f;// 击中的判定结果bool hitResult = false;// 射线击中的物体private RaycastHit hitInfo;void Start(){// 添加线条绘制组件lineRenderer = this.gameObject.AddComponent<LineRenderer>();InitLineRenderer(lineRenderer);// 设置射线的起点和方向ray.origin = this.transform.position;ray.direction = this.transform.forward;}// Update is called once per framevoid Update(){// 重新设置射线的位置ray.origin = this.transform.position;ray.direction = this.transform.forward;// 旋转游戏对象 -- 每秒旋转60°Quaternion quaternion = Quaternion.AngleAxis(60f * Time.deltaTime, this.transform.up);this.transform.rotation *= quaternion;        // 判断击中的物体hitResult =  Physics.Raycast(ray, out hitInfo, rayDistance, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);if (hitResult == true){print(hitInfo.collider.name);}// 显示并更新射线UpdateLineRendererByRay(lineRenderer, ray, hitResult,hitInfo, rayDistance);}/// <summary>/// 初始化线条渲染组件/// </summary>void InitLineRenderer(LineRenderer lineRenderer){// lineRenderer = this.gameObject.AddComponent<LineRenderer>();lineRenderer.positionCount = 2;lineRenderer.startWidth = 0.2f;lineRenderer.endWidth = 0.2f;lineRenderer.startColor = Color.red;lineRenderer.endColor = Color.green;}/// <summary>/// 击中物体的时候修改射线的长度/// </summary>/// <param name="lineRenderer">lineRenderer组件</param>/// <param name="ray">射线</param>/// <param name="hitResult">是否命中物体</param>/// <param name="hitInfo">命中物体信息</param>/// <param name="rayDistance">射线距离</param>void UpdateLineRendererByRay(LineRenderer lineRenderer,Ray ray, bool hitResult, RaycastHit hitInfo, float rayDistance){if (lineRenderer == null || lineRenderer.positionCount < 2){Debug.Log("LineRender组件不可以使用");return;}// 修改起点位置lineRenderer.SetPosition(0, ray.origin);// 修改终点位置if (hitResult == true){lineRenderer.SetPosition(1, hitInfo.point);}else {lineRenderer.SetPosition(1, ray.GetPoint(rayDistance));}}
}

优化后的代码

using UnityEngine;public class RotateRay : MonoBehaviour
{private LineRenderer lineRenderer;private Ray ray;private float rayDistance = 1000.0f;private RaycastHit hitInfo;void Start(){lineRenderer = this.gameObject.AddComponent<LineRenderer>();InitLineRenderer(lineRenderer);ray = new Ray(Vector3.zero, Vector3.forward);}void Update(){UpdateRayPosition();RotateObject();PerformRaycast();UpdateLineRenderer();}void InitLineRenderer(LineRenderer lineRenderer){lineRenderer.positionCount = 2;lineRenderer.startWidth = 0.2f;lineRenderer.endWidth = 0.2f;lineRenderer.startColor = Color.red;lineRenderer.endColor = Color.green;}void UpdateRayPosition(){ray.origin = this.transform.position;ray.direction = this.transform.forward;}void RotateObject(){Quaternion rotation = Quaternion.AngleAxis(60f * Time.deltaTime, this.transform.up);this.transform.rotation *= rotation;}void PerformRaycast(){int layerMask = 1 << LayerMask.NameToLayer("Default");hitInfo = new RaycastHit(); // 初始化hitInfo,避免未击中时的错误Physics.Raycast(ray, out hitInfo, rayDistance, layerMask, QueryTriggerInteraction.UseGlobal);}void UpdateLineRenderer(){if (lineRenderer == null || lineRenderer.positionCount < 2){Debug.LogError("LineRenderer component is not available or not properly initialized.");return;}lineRenderer.SetPosition(0, ray.origin);lineRenderer.SetPosition(1, hitInfo.collider != null ? hitInfo.point : ray.GetPoint(rayDistance));}
}

效果图如下

Unity_RayCast.gif

7.鼠标点击生成一个特效

using UnityEngine;public class  CameraRay: MonoBehaviour
{// 射线距离private float rayDistance = 1000.0f;// 特效Prefab--外部可以自定义特效public GameObject effectPrefab;void Update(){if (Input.GetMouseButtonDown(0)){// 从摄像机发出一条射线Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hitInfo;// 如果射线击中了指定层级的物体if (Physics.Raycast(ray, out hitInfo, rayDistance, LayerMask.GetMask("Wall"))){// 生成特效 --格局法线来计算特效位置GameObject effectObject = Instantiate(effectPrefab, hitInfo.point, Quaternion.LookRotation(hitInfo.normal));// 销毁特效,参数为延迟时间Destroy(effectObject, EffectPrefab.GetComponent<ParticleSystem>().main.duration);}}}
}

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

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

相关文章

阶段三:项目开发---大数据开发运行环境搭建:任务5:安装配置Kafka

任务描述 知识点&#xff1a;安装配置Kafka 重 点&#xff1a; 安装配置Kafka 难 点&#xff1a;无 内 容&#xff1a; Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;…

用起来超爽的4个宝藏软件工具

记得带 “记得带”是一款专为繁忙的都市人设计的生活服务软件&#xff0c;旨在帮助用户轻松管理日常生活中的各种事务。该应用程序集成了多种实用功能&#xff0c;包括购物清单、待办事项、日程安排和健康追踪等。它还具有智能提醒功能&#xff0c;可以根据用户的日常习惯和偏好…

14-41 剑和诗人15 - RLAIF 大模型语言强化培训

​​​​​​ 介绍 大型语言模型 (LLM) 在自然语言理解和生成方面表现出了巨大的能力。然而&#xff0c;这些模型仍然存在严重的缺陷&#xff0c;例如输出不可靠、推理能力有限以及缺乏一致的个性或价值观一致性。 为了解决这些限制&#xff0c;研究人员采用了一种名为“人工…

easily-openJCL 让 Java 与显卡之间的计算变的更加容易!

easily-openJCL 让 Java 与显卡之间的计算变的更加容易&#xff01; 开源技术栏 本文介绍了关于在 Java 中 easily-openJCL 的基本使用&#xff01;&#xff01;&#xff01; 目录 文章目录 easily-openJCL 让 Java 与显卡之间的计算变的更加容易&#xff01;目录 easily-op…

算法学习笔记(8)-动态规划基础篇

目录 基础内容&#xff1a; 动态规划&#xff1a; 动态规划理解的问题引入&#xff1a; 解析&#xff1a;&#xff08;暴力回溯&#xff09; 代码示例&#xff1a; 暴力搜索&#xff1a; Dfs代码示例&#xff1a;&#xff08;搜索&#xff09; 暴力递归产生的递归树&…

matlab仿真 信道(上)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第四章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; 1.加性高斯白噪声信道&#xff08;AWGN &#xff09; clear all t0:0.001:10; xsin(2*pi*t);%原始信号 snr20;%设定加性白噪…

CSS技巧:清除浏览器默认样式,让你的页面全由你做主!

莫名其妙的的问题哪里来? 你有没有过写了半天样式&#xff0c;却发现总有些与你想要的效果不同的地方&#xff1a;input带个黑框框&#xff0c;list 的小圈圈&#xff0c;锚点的文字颜色&#xff0c;莫名其妙多出来的一两个像素的距离。。 回到20年前&#xff0c;我刚刚接触…

HBuilder X 小白日记03-用css制作简单的交互动画

:hover选择器&#xff0c;用于选择鼠标指针浮动在上面的元素。 :hover选择器可用于所有元素&#xff0c;不只是链接 :link选择器 设置指向未被访问页面的链接的样式 :visited选择器 用于设置指向已被访问的页面的链接 :active选择器 用于活动链接

DBA 数据库管理

数据库&#xff1a;存储数据的仓库 数据库服务软件&#xff1a; 关系型数据库&#xff1a; 存在硬盘 &#xff0c;制作表格的 数据库的参数 [rootmysql50 ~]# cat /etc/my.cnf.d/mysql-server.cnf 主配置文件 [mysqld] datadir/var/lib/mysql 存放数据库目录…

【小鸡案例】表单focus和blur事件用法

input中有2个属性&#xff0c;一个是focus获取焦点&#xff0c;一个是blur失去焦点。获取焦点就是我们点击输入框时输入框被选中&#xff1b;失去焦点即点击输入框以外的区域&#xff0c;今天就用这两种属性做一个点击输入框的动画效果。 先写个输入框&#xff0c;代码如下&am…

GitLab介绍,以及add an SSH key

GitLab GitLab 是一个用于仓库管理系统的开源项目&#xff0c;现今并在国内外大中型互联网公司广泛使用。 git,gitlab,github区别 git 是一种基于命令的版本控制系统&#xff0c;全命令操作&#xff0c;没有可视化界面&#xff1b; gitlab 是一个基于git实现的在线代码仓库…

Spring的AOP进阶。(AOP的通知类型、通知顺序、切入点表达式和连接点。)

3. AOP进阶 AOP的基础知识学习完之后&#xff0c;下面我们对AOP当中的各个细节进行详细的学习。主要分为4个部分&#xff1a; 通知类型通知顺序切入点表达式连接点 我们先来学习第一部分通知类型。 3.1 通知类型 在入门程序当中&#xff0c;我们已经使用了一种功能最为强大…

武汉免费 【FPGA实战训练】 Vivado入门与设计师资课程

一&#xff0e;背景介绍 当今高度数字化和智能化的工业领域&#xff0c;对高效、灵活且可靠的技术解决方案的需求日益迫切。随着工业 4.0 时代的到来&#xff0c;工业生产过程正经历着前所未有的变革&#xff0c;从传统的机械化、自动化逐步迈向智能化和信息化。在这一背景下&…

BP神经网络的实践经验

目录 一、BP神经网络基础知识 1.BP神经网络 2.隐含层选取 3.激活函数 4.正向传递 5.反向传播 6.不拟合与过拟合 二、BP神经网络设计流程 1.数据处理 2.网络搭建 3.网络运行过程 三、BP神经网络优缺点与改进方案 1.BP神经网络的优缺点 2.改进方案 一、BP神经网络基…

windows obdc配置

进入控制面板&#xff1a; 进入管理工具&#xff1a;

transformer网络学习

Transformer encoder-decoder模型之间共享的是Encoder最后一层输出的hidden-state。 GitHub - huggingface/transformers: &#x1f917; Transformers: State-of-the-art Machine Learning for Pytorch, TensorFlow, and JAX. Bert2Bert中&#xff0c;Encoder的hidden-state同…

昇思25天学习打卡营第10天|ShuffleNet图像分类

ShuffleNet网络结构 ShuffleNet是一种专为移动设备设计的、计算效率极高的卷积神经网络&#xff08;CNN&#xff09;架构。其网络结构的设计主要围绕减少计算复杂度和提高模型效率展开&#xff0c;通过引入逐点分组卷积&#xff08;Pointwise Group Convolution&#xff09;和…

AutoX.js从某音分享链接解析出视频ID

背景 从某音分享的链接中解析出数字的videoID&#xff0c;用来做评论Intent跳转 思路 基本所有的短链接都是302跳转或者js跳转&#xff0c;熟悉http协议都知道&#xff0c;当状态码为302&#xff0c;从headers中提取Location即刻获得视频的原链接 链接中就带有videoId 要注意…

【大模型LLM面试合集】大语言模型基础_Word2Vec

Word2Vec 文章来源&#xff1a;Word2Vec详解 - 知乎 (zhihu.com) 1.Word2Vec概述 Word2Vec是google在2013年推出的一个NLP工具&#xff0c;它的特点是能够将单词转化为向量来表示&#xff0c;这样词与词之间就可以定量的去度量他们之间的关系&#xff0c;挖掘词之间的联系。 …

Java之父James Gosling宣布正式退休 创造无数人的饭碗

编程语言Java的创始人&#xff0c;被誉为“Java之父”的James Gosling&#xff0c;近日在社交媒体上宣布了自己正式退休的消息。Gosling表示&#xff1a;“我终于退休了。做了这么多年的软件工程师&#xff0c;现在是时候享受人生了。”他透露&#xff0c;在亚马逊的过去7年是非…