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.rhkb.cn/news/372693.html

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

相关文章

JRE、JVM、JDK分别是什么。

JDK JDK的英文全称是Java Development Kit。JDK是用于制作程序和Java应用程序的软件开发环境。JDK 是 Java 开发工具包&#xff0c;它是 Java 开发者用来编写、编译、调试和运行 Java 程序的集合。JDK 包括了 Java 编译器&#xff08;javac&#xff09;、Java 运行时环境&…

首席数据官CDO证书报考指南:方式、流程、适考人群与考试难度

在信息泛滥的今天&#xff0c;数据已转变为企业不可或缺的宝贵资源。 面对海量的信息&#xff0c;如何提炼出价值&#xff0c;为企业带来实质性的收益&#xff1f;首席数据官&#xff08;CDO&#xff09;认证的出现正是为了满足这一需求&#xff0c;它不仅是个人专业能力的体现…

【网络安全】这些网络安全知识请牢记!

随着社会信息化深入发展&#xff0c;互联网对人类文明进步将发挥更大促进作用&#xff0c;但与此同时&#xff0c;互联网领域的问题也日益凸显&#xff0c;网络犯罪、网络攻击等时有发生&#xff0c;网络安全与每个人都息息相关&#xff0c;下面一起来了解网络安全知识吧&#…

如何降低电力运维成本,为企业的运维增效、能源数字化和节能降耗提供数据支持?

【电力运维存在问题】 随着全球范围内城镇化、数字化和工业化进程的加速与电力政策的改革&#xff0c;企业用电需求不断攀升&#xff0c;极大冲击了电力企业传统的运维模式&#xff0c;暴露出许多的问题&#xff1a; 变电所较为分散&#xff0c;缺乏统一管理&#xff1b;站内…

从数据仓库到数据湖(下):热门的数据湖开源框架

文章目录 一、前言二、Delta Lake三、Apache Hudi四、Apache Iceberg五、Apache Paimon六、对比七、笔者观点八、总结八、参考资料 一、前言 在上一篇从数据仓库到数据湖(上)&#xff1a;数据湖导论文章中&#xff0c;我们简单讲述了数据湖的起源、使用原因及其本质。本篇文章…

Linux:Ubuntu18.04下开机自启动QT图形化界面

Linux&#xff1a;Ubuntu18.04下开机自启动QT图形化界面 Chapter1 Linux&#xff1a;Ubuntu18.04下开机自启动QT图形化界面一、创建rc.local文件二、建立rc-local.service文件三、启动服务查看启动状态四、重启 Chapter2 将QT应用作为开机自启动&#xff08;Linux系统&#xff…

Simulink生成代码时端口名称乱码问题

写在最前&#xff1a; 在使用Simulink生成代码时发现端口名称与模型中定义的输如输出端口名称不一致&#xff0c;代码生成的端口名称为随机字符名称。 在生成的H文件中发现&#xff0c;端口定义的结构体名称与模型中实际定义的名称不符。 模型中的定义 检查后发现&#xff0c…

【已解决】腾讯云安装了redis,但是本地访问不到,连接不上

汇总了我踩过的所有问题。 查看配置文件redis.conf 1、把bind 127.0.0.1给注释掉&#xff08;前面加个#就是&#xff09;或者改成bind 0.0.0.0&#xff0c;因为刚下载时它是默认只让本地访问。&#xff08;linux查找文档里的内容可以输入/后面加需要匹配的内容&#xff0c;然后…

基于STM主题模型的主题提取分析-完整代码数据

直接看结果: 代码: import re from collections import defaultdict import random import matplotlib.pyplot as plt import numpy as npimport pandas as pd import numpy as np import re from sklearn.feature_extraction.text import CountVectorizer from nltk.corpus…

如何在 Ubuntu上搭建 LAMP

远程登录 Ubuntu系统环境 ssh (User)(IP) # 比如&#xff1a;ssh lennlouis192.168.207.128 为安全起见&#xff0c;建议你使用 root 登录 VPS 后创建一个具有 sudo 权限的帐号。 安装和配置 Apache 2 Apache Http Server 是一个开源的&#xff0c;非常流行&#xff0c;使用…

【Dell R730 折腾记录】风扇调速--在 Ubuntu 系统上开机自启动并每隔30分钟执行一次风扇定速脚本

前段时间升级了一下机柜里的服务器&#xff0c;替换掉了一台旧的 Dell 服务器&#xff0c;换上了这台 R730。但是无奈于噪音的袭扰&#xff0c;搁置了一段时间。我在这台机器上目前安装了一块 Intel Xeon E5-2630v3 芯片以及一张改过散热的 NVIDIA Tesla P4 计算卡。结果就是散…

关于ORACLE单例数据库中的logfile的切换、删除以及添加

一、有关logfile的状态解释 UNUSED&#xff1a; 尚未记录change的空白group&#xff08;一般会出现在loggroup刚刚被添加&#xff0c;或者刚刚使用了reset logs打开数据库&#xff0c;或者使用clear logfile后&#xff09; CURRENT: 当前正在被LGWR使用的gro…

K8S 上部署大数据相关组件

文章目录 一、前言二、Redis 一、前言 Artifact Hub 是一个专注于云原生应用的集中式搜索和发布平台。它旨在简化开发者在 CNCF&#xff08;Cloud Native Computing Foundation&#xff09;项目中寻找、安装和分享包与配置的过程。用户可以通过这个平台方便地发现、安装各类云原…

【CPP】CPP的命名空间输入输出缺省参数函数重载

目录 1 命名空间 -- namespace2 CPP的输入与输出(io)2.1 输入输出流的一些规定2.2 实操一下2.3 关于endl2.4 关于精度控制2.5 效率提高 3 缺省参数(默认参数)3.1 样例3.2 全缺省与半缺省3.3 缺省参数的意义 4 函数重载4.1 函数重载的基本使用4.2 函数重载调用歧义 这里是oldkin…

记录一次MySql锁等待 (Lock wait timeout exceeded)异常

[TOC](记录一次MySql锁等待 (Lock wait timeout exceeded)异常) Java执行一个SQL查询未提交&#xff0c;遇到1205错误。 java.lang.Exception: ### Error updating database. Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transactionCluster…

互联网应用主流框架整合之SpringCloud微服务治理

微服务架构理念 关于微服务的概念、理念及设计相关内容&#xff0c;并没有特别严格的边界和定义&#xff0c;某种意义上说&#xff0c;适合的就是最好的&#xff0c;在之前的文章中有过详细的阐述&#xff0c;微服务[v1.0.0][Spring生态概述]、微服务[设计与运行]、微服务[v1.…

SpringMVC--获取请求参数

1、通过的ServletAPI获取 只需要在控制器的方法的形参位置设置HTTPRequest request 类型的形参就i可以在控制器方法种使用request对象获取请求参数 RequestMapping("/servletAPI")public String getByServletAPI(HttpServletRequest request){HttpSession session…

JAVA集合Collection常用方法详解

一、Collection介绍 提到集合就不得不提一下数组&#xff0c;好多集合底层都是依赖于数组的实现。数组一旦初始化后&#xff0c;长度就确定了&#xff0c;存储数据对象不能达到动态扩展&#xff0c;其次数组存储元素不便于对数组进行添加、修改、删除操作&#xff0c;而且数组…

ubuntu关于docker部署 项目一站式教程

**假设已有ubuntu服务器并且登录root账号 ** **FinalShell中复制快捷键是 ****Ctrl+Shift+V** 卸载老版本docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker步骤 更新软件包sudo apt update sudo apt upgrade安装docker依赖sudo apt-get …

Ubuntu配置GitHub(第一次clone/push)

文章目录 1. 安装Git&检查连接2. 注册GitHub3. 生成&GitHub添加SSH3.1. 检查&删除已有id_rsa3.2. 生成SSH3.3. GitHub添加id_rsa.pub SSH3.4. 检查SSH 4. 继续开发可以参考参考 1. 安装Git&检查连接 安装 sudo apt-get install git检查SSH连接 ssh -T gitgi…