第二十八章 Unity射线检测

2023-11-16

本章节我们介绍一下射线。射线就是从一个固定点向一个方向发射出一条直线,在发射过程中需要判断该射线有没有与游戏物体发送碰撞。射线既可以用来检测射击游戏中武器指向目标;又可以判断鼠标是否指向游戏物体。射线的创建方式,一般使用代码来实现。接下来,我们就来创建一个新的“SampleScene3.unity”场景。这里注意的是,射线检测都是以物理系统为基础的,因此只有添加碰撞体组件的游戏物体才能被射线检测到。庆幸的是,在Unity中,创建的Cube或者Sphere,都是自动附带相应的碰撞体组件。

我们创建了三个球体Sphere1,Sphere2,Sphere3,然后我们由Sphere1为起点向X轴负方向(上图左边)发射一条射线。那么这条射线就应该可以检测到Sphere2和Sphere3。

接下来,我们创建一个“RayScript.cs”脚本文件,附加到Sphere1上面,内容如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayScript : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            // 射线的起点(Sphere1的位置)
            Vector3 origin = transform.position;
            // 射线的方向(X轴负方向)
            Vector3 direction = Vector3.left;
            // 射线碰撞的目标对象
            RaycastHit hitInfo;
            // 射线的最大长度
            float maxDistance = 100;

            // 创建射线,返回是否检测到碰撞对象
            bool raycast = Physics.Raycast(origin, direction, out hitInfo, maxDistance);

            // 如果发生碰撞,碰撞信息就被存储到 hitInfo 中
            if (raycast)
            {
                // 获取碰撞点坐标
                Vector3 point = hitInfo.point;
                Debug.Log("碰撞点坐标:" + point);

                // 获取碰撞目标的名称
                string name = hitInfo.collider.name;
                Debug.Log("碰撞对象名称:" + name);

                // 获取目标的碰撞体组件
                Collider coll = hitInfo.collider;

                //获取目标的Transgorm组件
                Transform trans = hitInfo.transform;
            }
        }
    }
}

上面的代码非常简单,我们使用四个参数来,通过Physics.Raycast方法创建一条射线,然后使用第三个参数RaycastHit hitInfo就是我们需要的碰撞目标的信息。我们可以通过这个对象获取到射线和目标的碰撞点位置信息,也可以获取目标的游戏对象名称,以及它碰撞体collider组件或者transform变换组件。其实,Physics.Raycast方法还有第五个参数int layerMask,用来指定检测图层,而忽略其他图层。在我们上一章节中,就提到过,碰撞检测可以使用层layer来进行限制。我们可以指定层与层之间的游戏对象发生碰撞,同样这里也适用于射线的碰撞检测。这里我们就不再详细介绍这个参数了。

在游戏开发中,由于射线不可见,所以有时候,我们无法判断射线碰撞的有效性。这个时候,我们可以借助Debug.DrawLine()函数和Debug.DrawRay()来模拟射线。首先介绍DrawLine,

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

参数为start 直线的起点,end 直线的终点,color 直线的颜色,duration 直线的持续时间。

depthTest 直线是否被靠近摄像机的对象遮挡。

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

参数为start 射线的起点,dir 射线的方向和长度,color 射线的颜色,duration 射线的持续时间,depthTest 摄像是否被靠近摄像机的对象遮挡。

接下来,我们就使用Debug.DrawRay方法来模拟上面代码案例中的摄像,增加如下代码

// 画一条蓝线来模拟射线
Debug.DrawRay(origin, direction * 100, Color.blue, 100);

接下来,我们重新Play工程,然后按下A键,回到Scene视图(不是Game视图)中查看。

我们可以看到由Sphere1发射出来的一条蓝色的模拟射线了。

由上图我们可知,射线不仅穿过了黄球Sphere2,还穿过了绿球Sphere3了。如何能得到绿色Sphere3呢?这个就需要借助Physics.RaycastAll函数。这个函数与Physics.Raycast函数的使用是相似的,但是返回的结果是不一样的。该函数的返回值是RaycastHit数组。接下来,我们重新修改一下代码,如下所示

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            // 射线的起点(Sphere1的位置)
            Vector3 origin = transform.position;
            // 射线的方向(X轴负方向)
            Vector3 direction = Vector3.left;
            // 射线碰撞的目标对象
            //RaycastHit hitInfo;
            // 射线的最大长度
            float maxDistance = 100;

            // 创建射线,返回是否检测到碰撞对象
            //bool raycast = Physics.Raycast(origin, direction, out hitInfo, maxDistance);
            RaycastHit[] hitInfos = Physics.RaycastAll(origin, direction, maxDistance);

            // 如果发生碰撞,碰撞信息就被存储到 hitInfo 中
            //if (raycast)
            foreach(RaycastHit hitInfo in hitInfos)
            {
                // 获取碰撞点坐标
                Vector3 point = hitInfo.point;
                Debug.Log("碰撞点坐标:" + point);

                // 获取碰撞目标的名称
                string name = hitInfo.collider.name;
                Debug.Log("碰撞对象名称:" + name);

                // 获取目标的碰撞体组件
                Collider coll = hitInfo.collider;

                //获取目标的Transgorm组件
                Transform trans = hitInfo.transform;
            }

            // 画一条蓝线来模拟射线
            Debug.DrawRay(origin, direction * 100, Color.blue, 100);
        }
    }

我们重新Play工程,控制台输出截图如下

在实际游戏开发中,我们有时候需要检测一定范围内是否发生碰撞。比如说,我们要检测周围100米内是否存在某些游戏对象,如果存在,就向其主动发起攻击。此时,我们使用一条射线就无法完成这样的要求。Unity为我们提供了丰富的不同形状的射线检测。这里,我们可以使用Physics.OverlapSphere创建球体碰撞检测,或者使用Physics.OverlapBox创建立方体检测。他们返回的是Collider[]数组,例如我们使用射线检测附近100米内的所有物体

Collider[] colliders = Physics.OverlapSphere(transform.postion, 100.0f);

foreach(Collider collider in colliders){ …… }

最后,我们在介绍一下摄像的另一种使用方式,就是鼠标点击选中场景中的游戏对象。它的原理非常简单,就是由相机位置向鼠标点击位置发射一条射线,然后进行碰撞检测。接下来,我们就来创建一个“RayClickScript.cs”脚本,将其附加到相机上面。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayClickScript : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        // 鼠标左键按下
        if (Input.GetMouseButtonDown(0))
        {
            // 从相机位置发射一条射线经过屏幕上的鼠标点击位置
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            // 声明一个射线碰撞信息类
            RaycastHit hit;

            // 进行碰撞检测
            bool res = Physics.Raycast(ray, out hit);

            // 如果产生了碰撞
            if(res){
                Debug.Log("碰撞点:" + hit.point);
                Debug.Log("碰撞目标:" + hit.transform.name);
            }
        }
    }
}

然后我们Play工程,使用鼠标点击绿球Sphere3,

接下来,我们做一个有趣事情。我们点击Plane平面上一点,然后让绿球Sphere3移动到那一点。如何来完成这件有趣的事情呢?代码改动如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayClickScript : MonoBehaviour
{
    // 绿球Sphere3
    private GameObject sphere3;

    // 是否移动
    private bool isMove = false;

    // 目标点
    private Vector3 target = Vector3.zero;

    // Start is called before the first frame update
    void Start()
    {
        // 获取绿球Sphere3
        sphere3 = GameObject.Find("Sphere3");
    }

    // Update is called once per frame
    void Update()
    {
        // 鼠标左键按下
        if (Input.GetMouseButtonDown(0))
        {
            // 从相机位置发射一条射线经过屏幕上的鼠标点击位置
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            // 声明一个射线碰撞信息类
            RaycastHit hit;

            // 进行碰撞检测
            bool res = Physics.Raycast(ray, out hit);

            // 如果产生了碰撞
            if (res && hit.transform.name == "Plane")
            {
                // 目标点(Y轴上保持不变)
                isMove = true;
                target = new Vector3(hit.point.x, sphere3.transform.position.y, hit.point.z);
                //Debug.Log("碰撞点:" + hit.point);
                //Debug.Log("碰撞目标:" + hit.transform.name);
            }
        }

        // 如果发生碰撞就让绿球移动到目标点
        if (isMove)
        {
            // 绿球朝向目标点
            sphere3.transform.LookAt(target);

            // 角色移动到目标点的距离
            float distance = Vector3.Distance(target, sphere3.transform.position);

            // 没有到达目标点就一直移动下去
            if (distance > 0.1f)
            {
                // 旋转后向前移动即可
                sphere3.transform.Translate(transform.forward * 0.2f);
            }
            else
            {
                // 移动结束
                isMove = false;
            }
        }
    }
}

上面的代码,我们就不解释了,直接Play运行查看效果

本课程涉及的内容已经共享到百度网盘:https://pan.baidu.com/s/1e1jClK3MnN66GlxBmqoJWA?pwd=b2id

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第二十八章 Unity射线检测 的相关文章

  • CGIC文件上传----菜鸟笔记

    CGIC上传文件 一 如何利用CGIC上传自己的文件 原理 当在浏览器点击 提交 表单时候 就会上传文件内容并调用你所编写cgic程序 然后靠cgic代码保存你文件 html代码如下

随机推荐

  • CVPR 2021 Sequential Graph Convolutional Network for Active Learning

    深度学习在计算机视觉方面展现出非常大的进步 其代价是大规模的标注数据集 数据标注是耗时的 需要人工和雇佣成本 在许多领域 数据标注更具挑战性 如医学成像领域 此外 在优化深层神经网络架构时 数据的代表性存在差距 为了克服这些问题 主动学习已
  • HTTP协议之Libcurl

    目录 转载 https www cnblogs com xietianjiao p 13260021 html 一 libcurl简介 二 libcurl的使用 三 libcurl等第三方库的通用编译方法 四 调用libcurl编程访问百度
  • Elasticsearch(八)搜索优化

    Elasticsearch 6 4 2 1 理解字段分析过程 一个常被问到的问题是 为什么指定的文档没有被搜索到 很多情况下 这都归因于映射的定义和分析例程的配置存在问题 针对分析过程的调试 Elasticsearch提供了专用的REST
  • h5py存取简例

    当数据太大 好像是 gt 2G scipy io savemat 会报错 考虑换用 h5py 这种格式 matlab 也可以读 见 4 Code import numpy as np import h5py a np arange 12 r
  • SiamMask 测试程序分析

    之前分析了 DaSiamRPN 的测试代码 侧重于执行细节 到了 SiamMask 似乎主题应该有所升华 故事的明线为跟踪器构成 暗线为训练流图 相比于 DaSiamRPN SiamMask 不仅网络结构是现代化的 系统设计也更具匠心 这便
  • MATLAB——参数根轨迹的绘制

  • C# 接口(Interface)

    简介 接口定义了所有类继承接口时应遵循的语法合同 接口定义了属性 方法和事件 这些都是接口的成员 接口只包含了成员的声明 成员的定义是派生类的责任 接口提供了派生类应遵循的标准结构 接口使得实现接口的类或结构在形式上保持一致 抽象类在某种程
  • 双fifo流水线实现3x1024数组数据按列相加

    Vivado版本 2019 2 MATLAB Modelsim版本 Modelsim SE 64 10 7 实验内容 双fifo流水线实现3x1024数组数据按列相加 FIFO First Input First Output 既先入先出
  • 小程序 云函数中file转base64

    mp4文件转base64 云函数中下载文件 const res await cloud downloadFile fileID fileID const base64 data video mp4 base64 res fileConten
  • 计算机编程语言:解释型语言与编译型语言的理解

    一 计算机编程语言 主要分为3类 高级语言 抽象层次更高的便于记忆和表示的英文代码 汇编语言 抽象层次较高的对应机器硬件的cpu指令集 英文缩的助记 符号代码 机器语言 抽像层次最低的由0 1序列所表示的机器码 计算机底层只能识别0 1 所
  • TIMIT数据集无法打开?sph格式转换为wav

    打开TIMIT数据集发现提示无法打开文件 上网搜索发现文件虽然后缀是WAV 但是其实是sph格式 是无法打开的 需要转换为wav 找到一种python方法转换格式 但是不知道为什么sphfile库下载安装了就是无法引用 然后又找到了一个ma
  • Linux 安装Zookeeper

    Linux 安装Zookeeper 下载 wget https mirrors tuna tsinghua edu cn apache zookeeper zookeeper 3 4 14 解压 tar zxvf zookeeper 3 4
  • qt学习笔记2:信号和槽

    信号和槽 实现点击按钮关闭窗口 按钮 gt 点击 gt 窗口 gt 关闭 connect 信号的发送者 发送的具体信号 信号的接收者 信号的处理 信号的处理就是槽 一个是信号的发送方 一个是信号的接收方 信号槽有一个优点 松散耦合 即发送方
  • Vue 复杂json数据在el-table表格中展示(el-table分割数据)

    文章目录 前言 问题背景 实现复杂json数据在el table表格展示 el table column分割线 el table column高度 前言 在做复杂的动态表单 实现业务动态变动 比如有一条需要动态添加的el form item
  • Day29_10 JavaWeb之编码处理、Jsp及Cookie的使用(记住密码)

    目录 一 编码处理 编码处理的意义 请求编码及响应编码 二 Jsp Jsp的概述 html 转换为jsp 三 Cookie Cookie的概述 Cookie细节 四 记住密码的实现 了解什么是会话跟踪技术 记住密码功能实现的思路 一 编码处
  • Python中安装pandas出现问题总结

    1 安装pandas总报超时 这个方法一般都能解决问题 解决方法 pip install 包名 i http pypi douban com simple trusted host pypi douban com 这个是因为你下载的包不对
  • app 自动化测试 - 多设备并发 -appium+pytest+ 多线程

    1 appium python 实现单设备的 app 自动化测试 启动 appium server 占用端口 4723 电脑与一个设备连接 通过 adb devices 获取已连接的设备 在 python 代码当中 编写启动参数 通过 py
  • tmux内外vim主题theme不一致

    每次在tmux里边用vim都觉得好丑 明明外边设置theme了 但是到里边就识别不了 在 tmux conf里添加set g default terminal screen 256color 依然不行 解决方案 使用tmux 2 打开tmu
  • cdh下spark2-yarn运行sparkstreaming获取kafka数据使用spark-streaming-kafka-0-10_2.11报错解决

    报错问题 20 07 15 17 20 51 INFO utils AppInfoParser Kafka version 0 9 0 kafka 2 0 0 20 07 15 17 20 51 INFO utils AppInfoPars
  • 第二十八章 Unity射线检测

    本章节我们介绍一下射线 射线就是从一个固定点向一个方向发射出一条直线 在发射过程中需要判断该射线有没有与游戏物体发送碰撞 射线既可以用来检测射击游戏中武器指向目标 又可以判断鼠标是否指向游戏物体 射线的创建方式 一般使用代码来实现 接下来