【Unity入门】详解Unity中的射线与射线检测

目录

  • 前言
  • 一、射线的创建方法
  • 二、射线检测
    • 1、Raycast()
      • Raycast()不使用射线Ray
      • Raycast()使用射线Ray
    • 2、RaycastAll()
      • 使用射线Ray
      • RaycastAll() 不使用射线Ray
    • 3、射线的碰撞信息
  • 三、示例
  • 四、具体使用场景
  • 射线的调试方法
    • 1、Debug.DrawLine()
    • 2、Debug.DrawRay
    • 利用Gizmos

前言

碰撞检测可以帮助我们实现诸如抵达某个地点自动触发剧情、判断子弹是否击中玩家等功能,但我如果想要实现如当鼠标悬浮某个人物上,自动弹出该人物信息,要如何判断呢?这时使用碰撞检测,从摄像机生成一个透明碰撞体朝着人物移动,等碰撞到了人物再弹出该人物信息?会不会太繁琐了。或许你又会想,若我直接生成一个足够长的透明碰撞体呢,是不是在创建的那一刻就可以触发该人物的弹出信息逻辑?没错这样的确可以,而这就是射线!不过是把无限长的透明碰撞体变为了无限长的一条线,仅此而已。

一、射线的创建方法

常用的直线射线类型用类型Ray表示,Ray包含了 起点origin 跟 方向direction的定义,起点和方向都用Vector3类型表示,前者是一个坐标,后者是一个表示方向的向量。

Vector3 origin = transform.position;
Vector3 direction = Vector3.down;
Ray ray = new Ray(origin, direction);

二、射线检测

在游戏中发射一条射线,最常用的是Physics.Raycast()和Physics.RaycastAll(),前者只能检测一个碰撞体而被返回,后者可以一起检测多个碰撞体。如果使用 Physics.RaycastAll() 进行多次射线投射,将返回一个 RaycastHit[] 数组,其中包含所有射线与场景中对象的交互信息。这在需要获取所有碰撞点信息的情况下很有用。

1、Raycast()

从某个初始点开始,沿着特定的方向发射一条不可见且无限长的射线,通过此射线检测是否有任何模型添加了Collider碰撞器组件。一旦检测到碰撞,停止射线继续发射。

Raycast()不使用射线Ray

public static bool Raycast (Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction);

参数:

  1. origin:射线在世界坐标系中的起点。
  2. direction:射线的方向。
  3. hitInfo:如果返回 true,则 hitInfo 将包含有关最近的碰撞体的命中位置的更多信息。(另请参阅:RaycastHit)。
  4. maxDistance:射线应检查碰撞的最大距离。(可选,不写默认无限长)
  5. layerMask:层遮罩,用于在投射射线时有选择地忽略碰撞体。(可选,不写默认检测所有层)
  6. queryTriggerInteraction:指定该查询是否应该命中触发器。可以通过指定 queryTriggerInteraction 来控制是让触发碰撞体生成命中效果,还是使用全局 Physics.queriesHitTriggers 设置。

返回
bool 当光线与任何碰撞体相交时,返回 true,否则返回 false

Raycast()使用射线Ray

public static bool Raycast (Ray ray, out RaycastHit hitInfo, float maxDistance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction= QueryTriggerInteraction.UseGlobal);

参数:

  1. ray 光线的起点和方向。
  2. hitInfo:如果返回 true,则 hitInfo 将包含有关最近的碰撞体的命中位置的更多信息。(另请参阅:RaycastHit)。
  3. maxDistance 射线应检查碰撞的最大距离。(可选,不写默认无限长)
  4. layerMask 层遮罩,用于在投射射线时有选择地忽略碰撞体。(可选,不写默认检测所有层)
  5. queryTriggerInteraction 指定该查询是否应该命中触发器。

返回
bool 当光线与任何碰撞体相交时,返回 true,否则返回 false

Physics.Raycast 更多详情

2、RaycastAll()

使用射线Ray

public static RaycastHit[] RaycastAll (Ray ray, float maxDistance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction= QueryTriggerInteraction.UseGlobal);

参数:

  1. ray 光线的起点和方向。
  2. maxDistance 从射线起点开始,允许射线命中的最大距离。(可选,不写默认无限长)
  3. layerMask 层遮罩,用于在投射射线时有选择地忽略碰撞体。(可选,不写默认检测所有层)
  4. queryTriggerInteraction 指定该查询是否应该命中触发器。

返回
RaycastHit[] RaycastHit 对象的数组。注意,这些结果的顺序未定义。

描述
向场景中投射射线并返回所有命中对象。注意,这些结果的顺序未定义。

RaycastAll() 不使用射线Ray

public static RaycastHit[] RaycastAll (Vector3 origin, Vector3 direction, float maxDistance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction= QueryTriggerInteraction.UseGlobal);

参数

  1. origin 射线在世界坐标系中的起点。
  2. direction 射线的方向。
  3. maxDistance 从射线起点开始,允许射线命中的最大距离。(可选,不写默认无限长)
  4. layermask 层遮罩,用于在投射射线时有选择地忽略碰撞体。(可选,不写默认检测所有层)
  5. queryTriggerInteraction 指定该查询是否应该命中触发器。

Physics.RaycastAll 更多详情

3、射线的碰撞信息

我们再重点讲讲第三个参数RaycastHit,若没有这个参数,我们的返回值仅仅只是“是否碰到了物体”,而无法确定碰撞点在哪,也不知道碰撞体是哪一个。射线检测其实有着非常丰富的碰撞信息,如可以获得其碰撞坐标,信息,以及法线。这些丰富的信息都被保存在RaycastHit结构体中。

综合用法演示如下:

//声明变量,用于保存信息
RaycastHit hitInfo;
//发射射线,起点是当前物体位置,方向是世界前方
if(Physics.Raycast(transform.position,Vector3.forward,out hitInfo))
{//如果碰到物体,运行此处,返回的是bOOl值//获取碰撞点坐标Vector3 point = hitInfo.point;//获取对方碰撞体组件Collider coll = hitInfo.collider;//获取对方的Transgorm组件Transform trans = hitInfo.transform;//获取对方的物体名称string name = hitInfo.collider.name;//获取碰撞点的法线向量Vector3 normal = hitInfo.normal;
}

注意,2D游戏跟3D游戏获取碰撞体的方式有些不一样,具体对比如下

//声明变量,将碰撞信息直接赋值给变量
RaycastHit2D hitInfo=Physics2D.Raycast(transform.position,Vector3.forward);
//发射射线,起点是当前物体位置,方向是世界前方
if(hitInfo.collider != null)
{//如果检测的碰撞体不为空,运行此处。//获取碰撞点坐标Vector3 point = hitInfo.point;//获取对方碰撞体组件Collider coll = hitInfo.collider;//获取对方的Transgorm组件Transform trans = hitInfo.transform;//获取对方的物体名称string name = hitInfo.collider.name;//获取碰撞点的法线向量Vector3 normal = hitInfo.normal;
}

三、示例

//第一个简单小栗子void Update(){Ray ray = new Ray(transform.position, transform.forward);//声明一个Ray结构体,用于存储该射线的发射点,方向RaycastHit hitInfo;//声明一个RaycastHit结构体,存储碰撞信息if (Physics.Raycast(ray, out hitInfo)){Debug.Log(hitInfo.collider.gameObject.name);//这里使用了RaycastHit结构体中的collider属性//因为hitInfo是一个结构体类型,其collider属性用于存储射线检测到的碰撞器。//通过collider.gameObject.name,来获取该碰撞器的游戏对象的名字。}}
//第二个简单小栗子void Update(){RaycastHit hitInfo;if (Physics.Raycast(transform.position, transform.forward, out hitInfo)){Debug.Log(hitInfo.collider.gameObject.name);}}

四、具体使用场景

当使用 RaycastHit 类进行射线投射时,有许多不同的使用场景,包括检测碰撞、拾取物体、瞄准检测、物体高亮等。在以下每个场景示例中,我将向您展示如何使用 RaycastHit,并加入一些粒子效果(“lizi”,即粒子效果在中文中的写法)来增强可视化效果。

  1. 检测碰撞并发射粒子:
    在这个场景中,我们将使用射线投射来检测是否与一个可交互的对象发生碰撞,然后在碰撞点发射一些粒子效果。
using UnityEngine;public class RaycastCollisionExample : MonoBehaviour
{public ParticleSystem collisionParticles; // 粒子效果void Update(){Ray ray = new Ray(transform.position, transform.forward);RaycastHit hitInfo;if (Physics.Raycast(ray, out hitInfo)){// 碰撞点发射粒子效果if (collisionParticles != null){collisionParticles.transform.position = hitInfo.point;collisionParticles.Play();}Debug.Log("碰撞对象:" + hitInfo.collider.gameObject.name);}}
}

在这个示例中,我们在碰撞点发射了一个粒子效果,从而在用户与可交互对象发生碰撞时产生视觉效果。

  1. 物体拾取与交互:
    在这个场景中,我们将使用射线投射来检测是否与一个可拾取的物体发生碰撞,然后可以将物体拾取并交互。
using UnityEngine;public class ObjectPickupExample : MonoBehaviour
{private Transform pickedObject = null; // 当前拾取的物体void Update(){Ray ray = new Ray(transform.position, transform.forward);RaycastHit hitInfo;if (Physics.Raycast(ray, out hitInfo)){// 拾取物体if (Input.GetKeyDown(KeyCode.E)){if (hitInfo.collider.CompareTag("Pickable")){pickedObject = hitInfo.collider.transform;Debug.Log("拾取了:" + pickedObject.name);}}// 交互if (Input.GetKeyDown(KeyCode.F)){if (pickedObject != null){Debug.Log("与 " + pickedObject.name + " 进行了交互。");}}}}
}

在这个示例中,我们使用 Input.GetKeyDown(KeyCode.E) 来拾取与射线相交的可拾取物体,并使用 Input.GetKeyDown(KeyCode.F) 来与拾取的物体进行交互。

  1. 物体高亮效果:
    在这个场景中,我们将使用射线投射来检测是否与一个物体发生碰撞,然后在碰撞对象上显示一个高亮的粒子效果。
using UnityEngine;public class ObjectHighlightExample : MonoBehaviour
{public ParticleSystem highlightParticles; // 高亮粒子效果private Transform lastHighlightedObject = null; // 上一个高亮的物体void Update(){Ray ray = new Ray(transform.position, transform.forward);RaycastHit hitInfo;if (Physics.Raycast(ray, out hitInfo)){// 高亮物体if (hitInfo.collider.CompareTag("Highlightable")){if (lastHighlightedObject != hitInfo.collider.transform){if (lastHighlightedObject != null){highlightParticles.Stop();}lastHighlightedObject = hitInfo.collider.transform;highlightParticles.transform.position = lastHighlightedObject.position;highlightParticles.Play();}}else if (lastHighlightedObject != null){highlightParticles.Stop();lastHighlightedObject = null;}}}
}

在这个示例中,我们使用高亮粒子效果来显示玩家当前所指的物体。粒子效果在物体上播放和停止,从而实现高亮效果。

  1. 通过鼠标点击的位置获取与射线碰撞的物体
    private void Update(){if (Input.GetMouseButtonDown(0)){/*判断鼠标是否点击在UI上*/if (EventSystem.current.IsPointerOverGameObject() == false){/*通过鼠标位置获取射线*/Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);/*用于存储射线投射操作的结果*/RaycastHit hit;/* Physics.Raycast参数:* 射线在世界坐标系中的起点、* 射线的方向和存储射线投射操作的结果、* 射线应检查碰撞的最大距离、* 层遮罩,用于在投射射线时有选择地忽略碰撞体*/bool isCpllider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCobe"));if (isCpllider){/*得到点击的MapCude*/GameObject mapCude = hit.collider.gameObject;Debug.Log("成功");}}}}

注意:Collider组件中Is Trigger选项的开关并不影响射线检测! 对了还有一个参数,写在Raycast末尾,QueryTriggerInteraction(指定该射线是否应该命中触发器),上面我说过Is Trigger选项的开关不影响射线检测,但是前提是QueryTriggerInteraction该参数设置为检测触发器了,你也可以将该参数设置为仅对碰撞器进行检测,这个参数可以全局设置。对于射线投射起点位于碰撞体内的情况,Raycast 不会检测到碰撞体。在所有这些示例中,都使用了 FixedUpdate 而不是 Update。请参阅事件函数的执行顺序,以了解 Update 与 FixedUpdate 的区别,以及它们与物理查询的关系。

射线的调试方法

利用Debug调试

在游戏开发中,射线的调试方法非常重要,可以将看不见的射线以可视化的形式表现出来,方便我们查看数据是否正确。这个方法是使用Debug.DrawLine()函数和Debug.DrawRay()

1、Debug.DrawLine()

public static void DrawLine (Vector3 start, Vector3 end, Color color= Color.white, float duration= 0.0f, bool depthTest= true);

参数:

  1. start 应作为该直线起始点的世界空间中的点。
  2. end 应作为该直线结束点的世界空间中的点。
  3. color 该直线的颜色。
  4. duration 该直线的可见长度应为多长。
  5. depthTest 该直线是否应被靠近此摄像机的对象遮挡?

描述
在指定的起始点与结束点之间绘制一条直线。
当游戏正在运行并且启用辅助图标绘图时,将在 Editor 的游戏视图中绘制直线。当直线在游戏视图中可见时,还会在场景中绘制该直线。让游戏运行并显示直线。切换到场景视图,该直线将可见。
duration 是在第一次显示该直线后该直线可见的时间长短(单位为秒)。如果持续时间为零,则该直线仅显示一帧。

注意:这仅用于调试播放模式。应改用 Gizmos.Drawline 或 Handles.DrawLine 来绘制 Editor 辅助图标。
Debug.DrawLine 更多详情

2、Debug.DrawRay

public static void DrawRay (Vector3 start, Vector3 dir, Color color= Color.white, float duration= 0.0f, bool depthTest= true);

参数:

  1. start 应作为该射线起始点的世界空间中的点。
  2. dir 该射线的方向和长度。
  3. color 绘制的直线的颜色。
  4. duration 该直线的可见时长(单位为秒)。
  5. depthTest 该直线是否应被靠近此摄像机的其他对象遮挡?

描述
在世界坐标中绘制一条从 start 到 start + dir 的直线。
duration 参数确定在绘制该直线所在的帧之后该直线可见时长。如果持续时间为 0(默认值),则该直线被渲染 1 帧。
如果将 depthTest 设置为 true,则该直线将被此场景中更靠近摄像机的其他对象遮挡。将在该 Editor 的场景视图中绘制该直线。如果在游戏视图中启用了辅助图标绘图,则在该视图中也将绘制该直线。

Debug.DrawRay 更多详情

效果图可以看上面,都有涉及使用到。需要说明的是在默认的情况下,该辅助线仅在编辑器的场景窗口可见,如果要在game窗口看到,则需要单机Game窗口右上角的Gizmos开关。

利用Gizmos

调用OnDrawGizmos()函数或OnDrawGizmosSelected,它们同样都可以用来绘制辅助图标。区别在于

函数OnDrawGizmos()在程序一运行就执行,之后每帧都在执行,

函数OnDrawGizmosSelected()在鼠标点击到脚本挂载的物体的身上的时候运行,不管有多少父类对象,它都会执行。

这里推荐使用OnDrawGizmosSelected()

范例如下

  private void OnDrawGizmosSelected(){Gizmos.DrawLine(Vector3 from, Vector3 to);Gizmos.DrawRay((Vector3 from, Vector3 direction);}

可在scence窗口查看辅助线,同样想在game窗口可以看到,则需要单机Game窗口右上角的Gizmos开关。

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

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

相关文章

利用生成式人工智能进行功能管理测试

就 DevOps 而言,生成式 AI与功能管理测试的新兴集成标志着一次重大演变。我们将认真研究这项技术如何彻底改变我们创建测试环境的方式。 使用人工智能生成测试使我们能够模拟大量的用户场景和环境,这意味着我们可以开发和部署不仅好而且很棒的功能&…

React 的 diff 算法

React 的 diff 算法的演进。 在 React 16 之前,React 使用的是称为 Reconciliation 的 diff 算法。Reconciliation 算法通过递归地比较新旧虚拟 DOM 树的每个节点,找出节点的差异,并将这些差异应用到实际的 DOM 上。整个过程是递归的&#x…

蓝桥杯day2刷题日记

由浅入深 P8717 [蓝桥杯 2020 省 AB2] 成绩分析 #include <iostream> using namespace std; int num; double sum; int maxs,mins; int n;int main() {mins1e9;maxs-1e9;sum0;cin>>n;for(int i0;i<n;i){cin>>num;sumnum;maxsmax(maxs,num);minsmin(mins…

Ubuntu系统下C语言开发环境搭建与使用教程

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

graylog API 弱密码

graylog web 页面密码设置 输入密码&#xff1a;获取sha256加密后密码 echo -n "Enter Password: " && head -1 </dev/stdin | tr -d \n | sha256sum | cut -d" " -f1vi /etc/graylog/server/server.conf #修改以下配置 root_usernameroot ro…

C#,人工智能,机器学习,聚类算法,训练数据集生成算法、软件与源代码

摘要:本文简述了人工智能的重要分支——机器学习的核心算法之一——聚类算法,并用C#实现了一套完全交互式的、可由用户自由发挥的,适用于聚类算法的训练数据集生成软件——Clustering。用户使用鼠标左键(拖动)即可生成任意形状,任意维度,任意簇数及各种数据范围的训练数…

【Python】Flask上下文管理

current_app 类型&#xff1a;用用上下文的代理对象主要用途&#xff1a;提供对当前激活的Flask应用实例的访问。通常访问应用配置&#xff0c;注册的蓝图&#xff0c;应用级别的数据等等使用场景&#xff1a;在视图函数&#xff0c;错误处理器或者其他任何需要访问应用配置和属…

十步打造JAVA应用服务器

十步打造JAVA应用服务器 目录 十步打造JAVA应用服务器 1、要查看当前系统版本的Linux&#xff0c;您可以使用以下命令之一&#xff1a; 2、安装docker 3、安装mysql 4、安装 nginx 5、安装jdk 6、安装rzsz命令 7、安装git 源代码管理 8、打包jar包 9、启动项目 10、…

JSON 的了解和使用

目录 1. JSON 2. JSONcpp 的安装 3. JSONcpp 相关API的使用 3.1. 将 Json::Value 对象转化为 std::string 3.1.1. Json::Value 类 3.1.2. Json::Value::toStyledString 接口 3.1.3. Json::StyledWriter 类 3.1.4. Json::StyledWriter::write 接口 3.1.5. Json::Fas…

新一代云原生数据库OLAP

2023 OLAP峰会&#xff08;公开&#xff09;PPT汇总&#xff08;25份&#xff09;.zip 新一代云原生数据库的OLAP&#xff08;联机分析处理&#xff09;能力是其重要的特性之一&#xff0c;这种能力使得数据库能够支持复杂的数据分析查询&#xff0c;从而满足企业对大数据的深…

【Qt问题】使用QSlider创建滑块小部件无法显示

问题描述&#xff1a; 使用QSlider创建滑块小部件用于音量按钮的时候&#xff0c;无法显示&#xff0c;很奇怪&#xff0c;怎么都不显示 一直是这个效果&#xff0c;运行都没问题&#xff0c;但是就是不出现。 一直解决不了&#xff0c;最后我在无意中&#xff0c;在主程序中…

arp动态表缓存清除

一、arp表里清除表状态&#xff1a; 1&#xff0c;Delay&#xff1a;请求arp 2&#xff0c;Reachab&#xff1a;响应arp 3&#xff0c;Stale此状态下&#xff0c;待gc_stale_time超时后&#xff0c;准备gc_interval定期清理 二、限制条件 base_reachable_time&#xff1a;后变…

USB - USB Gadget on Linux

February, 2012. Embedded Linux Conference 2012. Agenda Introduction to USB USB Gadget API Existing Gadgets Design your own Gadget Demo Conclusio About the Author Software engineer at Adeneo Embedded Linux, Android Main activities: – BSP adaptation – Driv…

2024年3月18日---3月24日(全面进行)

根据月计划&#xff0c;为了要考虑把产品代码吃透。先对于计算几何&#xff0c;图像处理&#xff0c;测量学基础&#xff0c;slam进行 当然&#xff0c;也要把ue继续进行着。ue的rpg和底层渲染。收集下虚幻商城的免费资源&#xff0c;万一以后做独立游戏用得到。其他的可以暂时…

Android系统 关于ntp的修改(网络时间同步)

一&#xff0c;现象&#xff1a; 1. NTP介绍 NTP&#xff1a;网络时间协议&#xff0c;英文名称&#xff1a;Network Time Protocol&#xff08;NTP&#xff09;是用来使计算机时间同步化的一种协议&#xff0c;它可以使计算机对其服务器或时钟源&#xff08;如石英钟&#x…

MC78L05ACDR2G线性稳压器芯片中文资料规格书PDF数据手册引脚图参数图片价格

产品概述&#xff1a; MC78L00A系列线性稳压器价格便宜&#xff0c;易于使用&#xff0c;适用于各种需要最高100mA的调节电源的应用。与大功率MC7800和MC78M00系列一样&#xff0c;这款稳压器也提供内部电流限制和高温关断&#xff0c;因此非常坚固耐用。在很多应用中&#xf…

安全架构设计

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/136787420 一. 基本概念 1、威胁来源于物理环境、通信链路、网络系统、操作系统、应用系统、管理系统。 2、网络与信息安全风险类别可以分为人为蓄意破坏&#xff08;被动型攻击&#xff0c;主动型攻…

汽车电子零部件(5):全液晶组合仪表

前言: 作为传统汽车电子的核心零部件之一,汽车仪表在过去几年经历了巨大的变化。机械及液晶组合仪表市场急剧萎缩,全液晶组合仪表同样也是日落西山。数据会说话,去年中国市场乘用车前装标配机械及液晶组合仪表892.81万辆,相比20年下滑了40.29%。相比而言,基于域控制器架…

4500万英镑!英国深化发展量子计算背后“内有乾坤”

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 编辑丨慕一 编译/排版丨沛贤 深度好文&#xff1a;2200字丨15分钟阅读 近期&#xff0c;英国国家量子计算中心&#xff08;NQCC&#xff09;宣布量子计算实验台竞赛的结果&#xff0c;七家量子…

Python 编程中反斜杠 “\” 的作用:作为续行符和转义字符,处理文件路径和正则表达式时需特别注意。

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ Python 中的反斜杠 \ 可以被用作续行符&#xff0c;它允许你将一行代码分成多行来书写&#xff0c;以提高代码的可读性。这在处理长字符串、复杂的数学表达式或其他需要多行布局的代码时非常有用。 使…