Unity | 射线检测及EventSystem总结

news2024/12/24 9:55:09

目录

一、知识概述

1.Input.mousePosition

2.Camera.ScreenToWorldPoint

3.Camera.ScreenPointToRay

4.Physics2D.Raycast

二、射线相关

1.3D(包括UI)、射线与ScreenPointToRay

2.3D(包括UI)、射线与ScreenToWorldPoint

3.2D UI、射线与ScreenToWorldPoint

4.2D 检测视线中是否有障碍物

三、EventSystem

1.处理3D对象的交互事件

(1)利用EventSystem实现3D物体点击交互

(2)解决事件传递

2.处理2D对象(SpriteRender)的交互事件

3.处理UGUI的交互事件

4.利用EventTrigger组件代替接口来处理事件

5.总结


一、知识概述

1.Input.mousePosition

        在Unity中,Input.mousePosition 返回的是一个Vector3,它代表了鼠标在屏幕上的位置。这个位置的x和y分量代表了鼠标在屏幕上的水平和垂直坐标,而z分量默认情况下总是0,因为鼠标的位置是二维的,它没有深度信息。

2.Camera.ScreenToWorldPoint

        Camera.ScreenToWorldPoint 函数需要一个 Vector3 参数,其中 x 和 y 分别代表屏幕上的位置(通常是鼠标的位置),而 z 代表从摄像机到目标点的距离。在透视相机下,如果只传递了Input.mousePosition(鼠标的 x 和 y 坐标,而 z 坐标是默认的 0),那么转换出的世界坐标将会是摄像机自身的位置,因为从摄像机到它自己的距离是 0。 

        正确的做法是:设置 z 值为从摄像机到你感兴趣的对象的距离。这个距离不能是简单的摄像机的 z 值,因为摄像机的 z 值是相对于世界坐标系的,而 ScreenToWorldPoint 函数需要的是从摄像机到目标点的距离。 

        在正交视角(Orthographic)摄像机下,只传递了Input.mousePosition没问题,由于正交摄像机不具有透视变形,屏幕上的点转换到世界坐标的过程中不需要考虑深度信息,因为在正交投影中,深度(远近)不会影响物体的大小。

        其实,即使在正交视角下,ScreenToWorldPoint 依然需要一个 z 值,因为这个值会决定返回的世界坐标位于哪个深度上。 

         ScreenToWorldPoint通常用于在鼠标点击的位置放置一个对象,或者拖动物体等。

3.Camera.ScreenPointToRay

        Camera.ScreenPointToRay 方法用于从摄像机通过屏幕上的一个点创建一条射线。这个方法返回一个 Ray 对象,它包含了一个原点(摄像机的位置)和一个方向(从摄像机通过屏幕点的方向)。这个方法非常适合用于射线检测,因为你通常需要检测从摄像机出发,经过用户点击屏幕位置的直线上是否有任何物体。 

        常用于通过用户的鼠标点击或触摸来检测3D对象。

4.Physics2D.Raycast

        在Unity中,Physics2D.Raycast 是一个用于2D游戏的功能,它可以检测在给定的直线路径上是否有任何碰撞器(Collider)。这个功能常用于实现射线投射,比如检测玩家的视线中是否有障碍物,或者实现子弹的射击效果。

        对于UI元素的点击事件,常用Button或者使用【EventSystem + IPointerClickHandler接口】来处理。

二、射线相关

1.3D(包括UI)、射线与ScreenPointToRay

        对于3D物体,Unity中的鼠标点击射线检测需要依赖Camera。这主要通过使用Camera类的ScreenPointToRay方法来完成。该方法接收一个屏幕坐标并生成一条从相机位置出发,穿过屏幕点的射线。对于3D UI,依赖camera时,需要设置canvas render mode :Screen Space - Camera.

  • 摄像机为透视:Perspective。
  • 利用ScreenPointToRay。
  • 3D物体和3DUI都需要3D类型的Collider。
  • 实现鼠标点击到3D物体时,cube移动到点击位置。(后面也记录了利用射线+EventSystem实现该效果)
    void Update()
    {
        //3D
        if (Input.GetMouseButtonDown(0))
        {
            Debug.Log("Mouse Clicked:" + Input.mousePosition);
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            int layerMask = 1 << 0 | 1 << 5;
            if (Physics.Raycast(ray, out hit, 1000, layerMask))
            {
                Debug.Log("Hit:" + hit.collider.gameObject.name);
                if (hit.collider.gameObject.name == "RawImage")//3D UI,需要添加3D Collider
                {
                    hit.collider.gameObject.GetComponent<RawImage>().color = new Color(Random.Range(0, 1.0f), Random.Range(0, 1.0f), Random.Range(0, 1.0f), 1);
                }
                else  //3D物体
                {
                    hitPos = hit.point;
                }
            }
        }
        if (cube.transform.position != hitPos)
        {
            cube.transform.LookAt(Camera.main.transform);
            cube.transform.position = Vector3.Lerp(cube.transform.position, hitPos, Time.deltaTime);
        }
    }

2.3D(包括UI)、射线与ScreenToWorldPoint

        ScreenToWorldPoint传入z值(10)后,结果mouseWorldPosition的z值=camera的z+传入的z值。以下代码透视相机没问题,但正交下需要修改传入的z值以便能够点击到目标。

        if (Input.GetMouseButtonDown(0))
        {
            Debug.Log(Camera.main.nearClipPlane);
            // 将鼠标位置的屏幕坐标转换为世界坐标
            Vector3 mouseWorldPosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10));

            // 从摄像机的位置到鼠标所在的世界坐标的方向
            Vector3 direction = mouseWorldPosition - Camera.main.transform.position;
            Debug.DrawRay(Camera.main.transform.position, direction, Color.red, 4);//Debug.DrawRay 方法的第二个参数应该是方向向量(即终点位置减去起始位置),而不是终点位置本身

            Debug.Log("mouseWorldPosition:" + mouseWorldPosition + "  direction:" + direction);
            RaycastHit hit;
            // 发射射线
            if (Physics.Raycast(Camera.main.transform.position, direction, out hit))
            {
                // 如果射线和游戏对象碰撞,hit变量就会包含碰撞信息
                Debug.Log("Hit " + hit.collider.gameObject.name);
                // 这里可以添加更多的逻辑,比如处理点击事件
            }
        }

         Debug.DrawRay如果第二个参数写成终点:

Debug.DrawRay( Camera.main.transform.position, mouseWorldPosition, Color.red, 4);

        Unity会将这个终点坐标解释为一个方向向量,这会导致射线从原点(通常是世界坐标的(0,0,0))指向你传入的“终点”坐标,这显然不是你想要的效果:

3.2D UI、射线与ScreenToWorldPoint

  • 利用ScreenToWorldPoint,并且参数为Input.mousePosition,没有设置z值,只在正交相机下有效。
  • 利用RaycastHit2D只检测2D Collider,所以只对UI有效。
        if (Input.GetMouseButtonDown(0))
        {
            // 将鼠标点击的屏幕坐标转换为射线
            Vector3 ray = Camera.main.ScreenToWorldPoint(Input.mousePosition);

            // 进行射线投射,检测2D Collider
            RaycastHit2D hit = Physics2D.Raycast(ray, Vector2.zero);

            // 检查是否有物体被射线击中
            if (hit.collider != null)
            {
                Debug.Log("Hit " + hit.collider.gameObject.name);
            }
        }

4.2D 检测视线中是否有障碍物

        if(Input.GetMouseButtonDown(0))
        {
            if (player2D != null)
            {
                Debug.Log(player2D.transform.forward);

                Debug.Log(player2D.transform.position + player2D.transform.forward);
                RaycastHit2D hit = Physics2D.Raycast(player2D.transform.position, player2D.transform.forward, 1000);
                Debug.DrawLine(player2D.transform.position, player2D.transform.position + player2D.transform.forward * 1000, Color.red, 4);
              
                if (hit.collider != null)
                {
                    Debug.Log("Hit " + hit.collider.gameObject.name);
                }
            }
        }

三、EventSystem

        Unity的EventSystem事件系统是用来处理交互事件的。它负责管理和处理所有的输入事件,例如鼠标点击、触摸事件、键盘输入等。它可以处理3D、2D和UI交互。

        在一个典型的Unity项目中,通常会有一个名为"EventSystem"的GameObject,它包含了一个名为"StandaloneInputModule"或者其他类型InputModule(如TouchInputModule)的组件。这个模块负责在每一帧收集用户输入并将其转化为特定事件。

1.处理3D对象的交互事件

        Unity的EventSystem可以处理3D对象的交互事件,但是需要配合Physics Raycaster(用于3D物体)组件进行检测鼠标或触摸输入。

  • Physics Raycaster组件会在每一帧发送一条从摄像机到鼠标位置的射线,如果这条射线碰到了带有Collider组件的3D物体,那么这个物体就可以接收到EventSystem的事件。
  • 要让3D物体能够响应EventSystem的事件,需要在物体上添加一个实现了EventSystem接口的脚本。例如,可以添加一个实现了IPointerClickHandler接口的脚本,然后在OnPointerClick方法中处理点击事件。

(1)利用EventSystem实现3D物体点击交互

  • 确保场景中有EventSystem组件,并保持活跃。
  • 确保摄像机上有一个Physics Raycaster组件。这个组件负责发送射线来检测物体。Physics Raycaster组件并不是射线检测的必需组件,它只是用于让EventSystem能够自动处理鼠标和触摸事件。
  • 确保3D物体上有一个Collider组件。
//下方代码需要挂在要交互的3D物体上
public class CubePointerClick : MonoBehaviour,IPointerClickHandler
{
    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.Log("OnPointerClick");
        this.transform.localPosition += Vector3.forward;
    }
}

        Unity的事件系统在处理3D物体的点击事件时,会将事件传递给被点击物体的所有父物体,直到有一个物体处理了这个事件。所以如果3D物体上挂有上述脚本,并且有一个子物体(子物体没挂脚本,但是有collider组件),那么点击子物体时,也会触发父物体的OnPointClick。

(2)解决事件传递

方法一:检查被点击的物体是否是父物体

public class CubePointerClick : MonoBehaviour,IPointerClickHandler
{
    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.Log(eventData.pointerCurrentRaycast.gameObject.name);//打印点击的物体名字
        if (eventData.pointerCurrentRaycast.gameObject == gameObject)
        {
            this.transform.localPosition += Vector3.forward;
        }
    }
}

方法二:在子物体上添加一个空的IPointerClickHandler接口,来阻止事件的传递

public class TestSubObjClick : MonoBehaviour, IPointerClickHandler
{
    public void OnPointerClick(PointerEventData eventData)
    {
        //不处理点击事件
    }
}

2.处理2D对象(SpriteRender)的交互事件

        Unity的EventSystem同样可以处理2D对象的交互事件,但是需要配合Physics 2D Raycaster组件(挂在摄像机上)进行检测鼠标或触摸输入。

  • Physics 2D Raycaster组件会在每一帧发送一条从摄像机到鼠标位置的射线,如果这条射线碰到了带有Collider2D组件的2D物体,那么这个物体就可以接收到EventSystem的事件。
  • 要让2D物体能够响应EventSystem的事件,你需要在物体上添加一个实现了EventSystem接口的脚本。例如,你可以添加一个实现了IPointerClickHandler接口的脚本,然后在OnPointerClick方法中处理点击事件。

3.处理UGUI的交互事件

        Unity的EventSystem同样可以处理UGUI的交互事件,但是需要配合GraphicRaycaster组件进行检测鼠标或触摸输入。

  • GraphicRaycaster组件通常挂载在Canvas组件上,它会处理所有子UI元素的射线投射。
  • 创建Canvas时,会自动创建带有EventSystem组件的物体。

4.利用EventTrigger组件代替接口来处理事件

        之前的代码实现OnPointerClick点击事件都继承了 IPointerClickHandler接口,我们也可以不继承各种接口,直接使用EventTrigger组件来实现各种事件。EventTrigger组件可以挂在任何物体上,包括3D、2D及UI。

public class CubePointerClick : MonoBehaviour
{
    public void OnPointerClick()
    {
        this.transform.localPosition += Vector3.forward;
    }
}

        EventTrigger组件依赖于Unity中的Event System。当你使用UI元素(例如按钮、滑块等)或者自定义游戏对象上添加了EventTrigger组件后,在这些元素接收到用户交互(点击、拖动、滚动等)时,由InputModule收集到这些信息并通过EventSystem传递给相应对象上的EventTrigger进行处理。

        当然,用EventTrigger组件时,也要依赖Physics Raycaster组件 或 Physics 2D Raycaster组件 或 GraphicRaycaster等组件。

EventTrigger组件是用来定义和处理事件的,但是它并不负责检测鼠标或触摸输入。这是由Physics RaycasterPhysics 2D RaycasterGraphicRaycaster等组件来完成的。

  • Physics RaycasterPhysics 2D Raycaster是用来处理3D和2D物体的射线投射的,它们可以使你的3D和2D物体响应EventTrigger定义的事件。

  • GraphicRaycaster则是用来处理UI元素的射线投射的,它可以使你的UI元素响应EventTrigger定义的事件。

所以,如果你希望你的游戏对象能够响应EventTrigger定义的事件,你需要在相应的CanvasCamera上添加一个射线投射器组件。

5.总结

        在Unity中,StandaloneInputModule、Physics Raycaster、Physics 2D Raycaster、GraphicRaycaster和EventTrigger都是事件系统的一部分,它们各自负责不同的任务。

  • StandaloneInputModule是一个输入模块,它处理基于独立输入的事件,如鼠标和键盘输入。它将这些输入转换为事件,如OnPointerClick、OnPointerDown、OnPointerUp等,并将这些事件发送到EventSystem。
  • Physics Raycaster、Physics 2D Raycaster和GraphicRaycaster是射线投射器,它们负责检测鼠标或触摸输入与游戏对象的交互。例如,当你点击一个游戏对象时,射线投射器会检测到这个交互,并将相关的事件(如OnPointerClick)发送到这个游戏对象。
  • EventTrigger是一个事件触发器,它可以在游戏对象上定义和处理各种事件。当射线投射器检测到一个交互并发送一个事件时,EventTrigger会调用与这个事件相关的处理器。

        所以,如果你希望你的游戏对象能够响应鼠标或触摸输入,并执行自定义的事件处理逻辑,你需要在EventSystem上添加一个StandaloneInputModule,在相应的Canvas或Camera上添加一个射线投射器,并在游戏对象上添加一个EventTrigger。这样,当你点击或触摸游戏对象时,StandaloneInputModule会将这个输入转换为事件,射线投射器会检测到这个交互并将事件发送到游戏对象,然后EventTrigger会调用与这个事件相关的处理器。

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

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

相关文章

基于单片机锂电池电量检测数码管显示系统设计

**单片机设计介绍&#xff0c;基于单片机锂电池电量检测数码管显示系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机锂电池电量检测数码管显示系统设计的主要目标是实时、准确地检测锂电池的电量&#xff0c;并…

如何利用webpack来优化前端性能

当涉及前端性能优化时&#xff0c;Webpack 是一款不可或缺的工具。它不仅仅是一个模块打包工具&#xff0c;还提供了各种功能和插件&#xff0c;可以帮助开发人员优化前端应用程序的性能。在这篇文章中&#xff0c;我们将深入探讨如何有效地利用 Webpack 来优化前端性能&#x…

「15」色源:添加指定色彩的纯色画面素材

「15」色源添加指定色彩的纯色画面素材 在OBS软件里&#xff0c;在来源里可以增加色源&#xff0c;添加一个色块到您的场景中&#xff0c;作为一个背景色或辅助色。 图中的显示器就是加了色源的 实操步骤 第1步 添加色源 在「来源」的左下角&#xff0c;点「」选择「色源」&a…

使用Kaggle API快速下载Kaggle数据集

前言 在使用Kaggle网站下载数据集时&#xff0c;直接在网页上点击下载可能会很慢&#xff0c;甚至会出现下载失败的情况。本文将介绍如何使用Kaggle API快速下载数据集。 具体步骤 安装Kaggle API包 在终端中输入以下命令来安装Kaggle API相关的包&#xff1a; pip install…

「DevExpress中文教程」如何将DevExtreme JS HTML编辑器集成到WinForms应用

在本文中我们将演示一个混合实现&#xff1a;如何将web UI工具集成到WinForms桌面应用程序中。具体来说&#xff0c;我们将把DevExtreme JavaScript WYSIWYG HTML编辑器(作为DevExtreme UI组件套件的一部分发布的组件)集成到Windows Forms应用程序中。 获取DevExtreme v23.2正式…

Kafka重要配置参数全面解读(重要)

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Kafka重要配置参数全面解读(重要 前言auto.create.topics.enableauto.leader.rebalance.enablelog.retention.{hour|minutes|ms}offsets.topic.num.partitions 和 offsets.topic.replication.factorlo…

Excel:使用VLOOKUP函数,抓取指定数据,后一个列

Excel:使用VLOOKUP函数&#xff0c;抓取指定数据&#xff0c;后一个列 我们有这样一个数据源 要是实现这个页面的赋值 就是对应关系映射 使用 VLOOKUP(A2,Sheet2!$A$2:$B$9,2,FALSE)第一个参数是需要匹配的单元格。 第二个参数是数据源&#xff0c;我这里数据源用的是shee…

[Flutter]环境判断

方式一&#xff08;推荐&#xff09; 常量kReleaseMode&#xff0c;它会根据你的应用是以什么模式编译的来获取值。bool.fromEnvironment会从Dart编译时的环境变量中获取值。对于dart.vm.product这个特定的环境变量&#xff0c;它是由Dart VM设置的&#xff0c;用来标明当前是…

SnapGene 5 for Mac 分子生物学软件

SnapGene 5 for Mac是一款专为Mac操作系统设计的分子生物学软件&#xff0c;以其强大的功能和用户友好的界面&#xff0c;为科研人员提供了高效、便捷的基因克隆和分子实验设计体验。 软件下载&#xff1a;SnapGene 5 for Mac v5.3.1中文激活版 这款软件支持DNA构建和克隆设计&…

单摄像头、双目摄像头、多视图系统:了解自动驾驶汽车的传感器

1.鱼眼摄像机 其他摄像机的位置 2.激光雷达 3.Radar 4.sonar 声纳 各个传感器的对比 相机类型 一般来说&#xff0c;相机可以分为单目相机、双目相机和多视角相机。 单目相机&#xff1a;最常见的相机类型&#xff0c;只有一个镜头。双目相机&#xff1a;有两个镜头&…

浅谈Mysql(二)——慢sql、mysql锁、大事务的影响

一、慢sql的危害 不仅对当前查询影响大&#xff0c;查询时间过长&#xff1b;还对其他连接有影响&#xff0c;因为慢sql占用时间过长&#xff0c;导致其他线程&#xff0c;获取连接时间过长&#xff0c;进而导致网关超时等问题&#xff1b; 1.1 explian分析最主要看什么参数 …

20.变量的使用方式和注意事项

文章目录 一、变量的用法二、变量的注意事项三、总结 一、变量的用法 代码示例 public static void main(String[] args) {//1.基本用法// 定义变量&#xff0c;再进行输出int a 10;System.out.println(a);// 10System.out.println(a);// 10//2.变量参与计算int b 30;int c …

TitanIDE与传统 IDE 比较

与传统IDE的比较 TitanIDE 和传统 IDE 属于不同时代的产物&#xff0c;在手工作坊时代&#xff0c;一切都是那么的自然&#xff0c;开发者习惯 Windows 或 MacOS 原生 IDE。不过&#xff0c;随着时代的变迁&#xff0c;软件行业已经步入云原生时代&#xff0c;TitanIDE 是顺应…

联想 lenovoTab 拯救者平板 Y700 二代_TB320FC原厂ZUI_15.0.677 firmware 线刷包9008固件ROM root方法

联想 lenovoTab 拯救者平板 Y700 二代_TB320FC原厂ZUI_15.0.677 firmware 线刷包9008固件ROM root方法 ro.vendor.config.lgsi.market_name拯救者平板 Y700 ro.vendor.config.lgsi.en.market_nameLegion Tab Y700 #ro.vendor.config.lgsi.short_market_name联想平板 ZUI T # B…

车载以太网AVB交换机 gptp透明时钟 5口 全千兆 SW1500

全千兆车载以太网交换机 一、产品简要分析 5端口千兆车载以太网交换机&#xff0c;包含4个通道的1000BASE-T1接口使用罗森博格H-MTD和泰科MATEnet双接口&#xff0c;1个通道1000BASE-T标准以太网(RJ45接口)&#xff0c;可以实现车载以太网多通道交换&#xff0c;千兆和百兆车载…

macOS 13 Ventura (苹果最新系统) v13.6.6正式版

macOS 13 Ventura是苹果电脑的全新操作系统&#xff0c;它为用户带来了众多引人注目的新功能和改进。该系统加强了FaceTime和视频通话的体验&#xff0c;同时优化了邮件、Safari浏览器和日历等内置应用程序&#xff0c;使其更加流畅、快速和安全。特别值得一提的是&#xff0c;…

Gitlab 实现仓库完全迁移,包括所有提交记录、分支、标签

1 方案一&#xff1a;命令 cd <项目目录> git fetch --all git fetch --tags git remote rename origin old-origin #可以不保留 git remote add origin http://***(项目的新仓库地址) #git remote set-url origin <项目的新仓库地址> git push origin --all git…

Redis中的客户端(三)

客户端 身份验证 客户端状态的authenticated属性用于记录客户端是否通过了身份验证: typedef struct redisClient {// ...int authenticated;// ... } redisClient;如果authnticated的值为0&#xff0c;那么表示客户端未通过身份验证&#xff1b;如果authenticated的值为1&a…

【Java程序设计】【C00345】基于Springboot的船舶监造管理系统(有论文)

基于Springboot的船舶监造管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 项目获取 &#x1f345;文末点击卡片获取源码&#x1f345; 开发环境 运行环境&#xff1a;推荐jdk1.8&#xff1b; 开发工具&#xff1a;eclipse以及i…

2024/3/28 IOday1

编写一条学生链表&#xff0c;写一些能够像链表里边添加数据的函数 实现&#xff1a;将链表中的所有内容保存到文件中去 以及 读取文件中的所有内容&#xff0c;加载到链表里 #include <stdio.h> #include <string.h> #include <stdlib.h> typedef int datat…