Unity | 射线检测及EventSystem总结

目录

一、知识概述

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(){//3Dif (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 ColliderRaycastHit2D 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.hqwc.cn/news/576308.html

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

相关文章

经纬恒润RTaW-Pegase:车载网络通信建模与时间特性分析工具

▎RTaW简介 RTaW-Pegase是由法国国家信息与自动化研究所&#xff08;INRIA&#xff09;旗下的RTaW公司开发的产品。它主要用于构建和优化汽车、航空航天以及工业领域的通信网络&#xff0c;包括时间敏感网络&#xff08;TSN&#xff09;、CAN&#xff08;FD&#xff0c;XL&…

Mac电脑怎么录屏?两大神器拯救你的录制需求

mac电脑作为苹果公司推出的一款备受欢迎的计算机设备&#xff0c;拥有出色的性能和简洁的设计。很多时候&#xff0c;我们需要在mac电脑上录制屏幕&#xff0c;以便制作教程、分享游戏过程或者保存重要信息。可是很多用户不知道mac电脑怎么录屏。本文将介绍两种在mac电脑上进行…

github拉取的项目添加至自己的仓库

想把GitHub的开源项目拉到本地进行二开&#xff0c;研究了一下上传到gitee的步骤&#xff1a; 步骤 gitee新建仓库&#xff0c;仓库名与本地文件夹的名称一致&#xff0c;建好后gitee的页面也会有显示git命令 打开项目目录&#xff0c;右键打开git bash&#xff08;或者在git…

Vue 03 组件通信

Vue学习 Vue 0301 浏览器本地存储localStorageSessionStorage案例 todolist的完善 02 组件自定义事件Custom Events基本使用解绑自定义事件注意事项①② 总结案例 todolist的完善 03 全局事件总线GlobalEventBus案例 todolist的完善 04 消息的订阅与发布案例 todolist的完善 05…

抽象类和接口以及Object类

抽象类 在面对对象的概念中&#xff0c;所有对象的创建都是通过类来创建的。但是并不是所有的类都会创建对应的对象。如果一个类中没有足够的数据创建一个对象&#xff0c;这样的类就是抽象类。 如同经常使用的Animal动物类&#xff0c;Dog类&#xff0c;Cat类&#xff0c;Do…

selenium实战之爬取虎牙直播列表页

文章目录 声明实现流程给你主播列表页面分析登录遮罩层处理解析直播列表的数据分页处理 完整的代码 声明 前面有了 selenium的基础&#xff0c;这里就拿虎牙直播页面来做一个实战测试&#xff0c;这是作为学习&#xff0c;测试使用&#xff0c;并不用作为商业用途&#xff0c;不…

实战 | 微调训练TrOCR识别弯曲文本

导 读 本文主要介绍如何通过微调训练TrOCR实现弯曲文本识别。 背景介绍 TrOCR&#xff08;基于 Transformer 的光学字符识别&#xff09;模型是性能最佳的 OCR 模型之一。在我们之前的文章中&#xff0c;我们分析了它们在单行打印和手写文本上的表现。 TrOCR—基于Transforme…

HarmonyOS 应用开发之Want的定义与用途

Want 是一种对象&#xff0c;用于在应用组件之间传递信息。 其中&#xff0c;一种常见的使用场景是作为 startAbility() 方法的参数。例如&#xff0c;当UIAbilityA需要启动UIAbilityB并向UIAbilityB传递一些数据时&#xff0c;可以使用Want作为一个载体&#xff0c;将数据传递…

C#实现身份证格式验证(自建异常实现提醒)

基本信息 中国居民身份证的格式包括18位数字&#xff0c;这些数字分别代表不同的信息&#xff1a; 第1、2位数字表示省份代码。 第3、4位数字表示城市代码。 第5、6位数字表示区县代码。 第7至14位数字表示出生年、月、日&#xff08;其中7、8、9、10位是年&#xff0c;11、12…

ZK友好代数哈希函数安全倡议

1. 引言 前序博客&#xff1a; ZKP中的哈希函数如何选择ZK-friendly 哈希函数&#xff1f;snark/stark-friendly hash函数Anemoi Permutation和Jive Compression模式&#xff1a;高效的ZK友好的哈希函数Tip5&#xff1a;针对Recursive STARK的哈希函数 随着Incrementally Ve…

Uibot6.0 (RPA财务机器人师资培训第5天 ) 报销汇总机器人案例实战

训练网站&#xff1a;泓江科技 (lessonplan.cn)https://laiye.lessonplan.cn/list/ec0f5080-e1de-11ee-a1d8-3f479df4d981https://laiye.lessonplan.cn/list/ec0f5080-e1de-11ee-a1d8-3f479df4d981https://laiye.lessonplan.cn/list/ec0f5080-e1de-11ee-a1d8-3f479df4d981https…

独立游戏《星尘异变》UE5 C++程序开发日志3——UEC++特供的数据类型

本篇日志将介绍FString&#xff0c;FText、FName的用法和相互转换&#xff0c;以及容器TMap&#xff0c;TArray的增删查改 一、字符串相关数据类型&#xff1a;FString、FText、FName FString是最接近std::string的类型&#xff0c;字符串本身可以看做一个存储char型的动态数…