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

2024-03-18 05:36

本文主要是介绍【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开关。

这篇关于【Unity入门】详解Unity中的射线与射线检测的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/821334

相关文章

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

Python中局部变量和全局变量举例详解

《Python中局部变量和全局变量举例详解》:本文主要介绍如何通过一个简单的Python代码示例来解释命名空间和作用域的概念,它详细说明了内置名称、全局名称、局部名称以及它们之间的查找顺序,文中通... 目录引入例子拆解源码运行结果如下图代码解析 python3命名空间和作用域命名空间命名空间查找顺序命名空

SpringRetry重试机制之@Retryable注解与重试策略详解

《SpringRetry重试机制之@Retryable注解与重试策略详解》本文将详细介绍SpringRetry的重试机制,特别是@Retryable注解的使用及各种重试策略的配置,帮助开发者构建更加健... 目录引言一、SpringRetry基础知识二、启用SpringRetry三、@Retryable注解

springboot项目中常用的工具类和api详解

《springboot项目中常用的工具类和api详解》在SpringBoot项目中,开发者通常会依赖一些工具类和API来简化开发、提高效率,以下是一些常用的工具类及其典型应用场景,涵盖Spring原生... 目录1. Spring Framework 自带工具类(1) StringUtils(2) Coll

Python中的魔术方法__new__详解

《Python中的魔术方法__new__详解》:本文主要介绍Python中的魔术方法__new__的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、核心意义与机制1.1 构造过程原理1.2 与 __init__ 对比二、核心功能解析2.1 核心能力2.2

在PyCharm中安装PyTorch、torchvision和OpenCV详解

《在PyCharm中安装PyTorch、torchvision和OpenCV详解》:本文主要介绍在PyCharm中安装PyTorch、torchvision和OpenCV方式,具有很好的参考价值,... 目录PyCharm安装PyTorch、torchvision和OpenCV安装python安装PyTor

SpringBoot条件注解核心作用与使用场景详解

《SpringBoot条件注解核心作用与使用场景详解》SpringBoot的条件注解为开发者提供了强大的动态配置能力,理解其原理和适用场景是构建灵活、可扩展应用的关键,本文将系统梳理所有常用的条件注... 目录引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、@ConditionalOn

Qt spdlog日志模块的使用详解

《Qtspdlog日志模块的使用详解》在Qt应用程序开发中,良好的日志系统至关重要,本文将介绍如何使用spdlog1.5.0创建满足以下要求的日志系统,感兴趣的朋友一起看看吧... 目录版本摘要例子logmanager.cpp文件main.cpp文件版本spdlog版本:1.5.0采用1.5.0版本主要

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文

MySQL中的交叉连接、自然连接和内连接查询详解

《MySQL中的交叉连接、自然连接和内连接查询详解》:本文主要介绍MySQL中的交叉连接、自然连接和内连接查询,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、引入二、交php叉连接(cross join)三、自然连接(naturalandroid join)四