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;…

GEE代码实例教程详解:地表温度长时间序列分析

简介 在本篇博客中&#xff0c;我们将使用Google Earth Engine (GEE) 对地表温度 (LST) 进行长时间序列分析。通过结合Landsat 4-9的数据&#xff0c;我们将探索1982年至2024年间地表温度的变化趋势。 背景知识 Landsat数据集 Landsat数据集提供了多时相、多光谱的地表观测…

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

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

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

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

每天一个数据分析题(四百十六)- 线性回归模型

根据模型假设&#xff0c;线性回归模型中误差项的方差为 A. 常数 B. 函数 C. 随机变量 D. 以上都不是 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专项练习题库 内容涵盖Python&#xff0c;SQL&#xff0c;统计学&#…

【大模型】多模型在大模型中的调度艺术:解锁效率与协同的新境界

多模型在大模型中的调度艺术&#xff1a;解锁效率与协同的新境界 引言一、多模型与大模型的概念解析二、多模型调度的必要性三、多模型调度的关键技术3.1 负载均衡与动态分配3.2 模型间通信与协作3.3 模型选择与优化 四、多模型运行优化策略4.1 异构计算平台的利用4.2 模型压缩…

20240708每日前端---------提升网站设计水平的15个CSS技巧,来试试吧

框阴影效果向元素添加阴影可以增强其深度和视觉吸引力。 .box {box-shadow: 0 4px 8px rgba(0,0,0,0.1); }平滑的过渡动画 CSS 过渡是增强网页交互性的简单方法。 当鼠标悬停在按钮上时&#xff0c;按钮的背景颜色会平滑地过渡到新颜色。 .button {transition: background-co…

Windows电脑PC使用adb有线跟无线安装apk包

在Android开发中&#xff0c;经常需要使用ADB&#xff08;Android Debug Bridge&#xff09;来安装APK包到Android设备上&#xff0c;无论是通过有线连接还是无线连接。以下将分别介绍如何通过有线和无线方式使用ADB安装APK包。 有线连接安装APK 启用开发者选项和USB调试&…

npm ERR! code ENOTEMPTY npm ERR! syscall rename npm ERR!

报错&#xff1a; npm ERR! code ENOTEMPTY npm ERR! syscall rename npm ERR! path /home/user/.local/lib/node_modules/pkg npm ERR! dest /home/user/.local/lib/node_modules/.pkg-piikcue3 npm ERR! errno -39 npm ERR! ENOTEMPTY: directory not empty, rename ‘/home/…

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

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

初学Spring之 HelloSpring 篇

创建一个 Hello 类 get/set 方法、toString 方法&#xff08;快捷键&#xff1a;alt insert&#xff09; package com.demo.pojo;public class Hello {private String str;public String getStr() {return str;}public void setStr(String str) {this.str str;}Overridepub…

算法学习笔记(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选择器 用于活动链接

更加优雅的下载文件 --- http header Content-Disposition 学习

更加优雅的下载文件 --- http header Content-Disposition 学习 在响应头中在请求头中a 标签的 download 属性小结 Content-Disposition 在响应头中&#xff0c;告诉浏览器如何处理返回的内容&#xff0c;在表单提交中&#xff0c;说明表单字段信息。 在响应头中 用在响应头中…

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实现的在线代码仓库…

GEE代码实例教程详解:长时间序列NDVI分析

简介 本篇博客将介绍如何使用Google Earth Engine (GEE) 对长时间序列的Landsat数据进行归一化植被指数&#xff08;NDVI&#xff09;分析。通过此分析&#xff0c;可以监测和评估1982年至2024年间的植被变化趋势。 背景知识 Landsat数据集 Landsat是美国地质调查局和美国航…