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()
    {
        //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会调用与这个事件相关的处理器。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机基础,挑战全网最全解析

1.什么是计算机&#xff1f; 2.冯诺依曼结构 3.进制 4.摩尔斯码和布莱叶盲文 摩尔斯码 布莱叶盲文

如何使用群晖WebDAV实现固定公网地址同步Zotero文献管理器

文章目录 前言1. Docker 部署 Trfɪk2. 本地访问traefik测试3. Linux 安装cpolar4. 配置Traefik公网访问地址5. 公网远程访问Traefik6. 固定Traefik公网地址 前言 Trfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件&#xff0c;能轻易的部署微服务。它支持多种后端 (D…

蓝桥杯嵌入式学习笔记(6):IIC程序设计

目录 前言 1. IIC基本原理 2. 电路原理 3. 代码编程 3.1 预备工作 3.2 AT24C02写读功能编写 3.2.1 AT24C02写操作实现 3.2.2 AT24C02读操作实现 3.3 MCP4017写读功能编写 3.3.1 MCP4017写操作实现 3.3.2 MCP4017读操作实现 3.4 main.c编写 3.4.1 头文件引用 3.4.…

基于javaweb(springboot+mybatis)网上酒类商城项目设计和实现以及文档报告

基于javaweb(springbootmybatis)网上酒类商城项目设计和实现以及文档报告 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞…

Redis数据类型介绍和使用

数据类型 String&#xff08;字符串&#xff09;&#xff1a;最基本的数据类型&#xff0c;可以存储任何类型的数据&#xff0c;如文本、数字等。Hash&#xff08;哈希&#xff09;&#xff1a;用于存储字段-值对的散列集合&#xff0c;适用于存储对象。List&#xff08;列表&…

鱼哥赠书活动第14期:看完这本《数字化运维》掌握数字化运维方法,构建数字化运维体系

鱼哥赠书活动第14期&#xff1a;看完这本《数字化运维》掌握数字化运维方法&#xff0c;构建数字化运维体系 主要内容&#xff1a;读者对象&#xff1a;赠书抽奖规则:往期赠书福利&#xff1a; 数字化转型已经成为大势所趋&#xff0c;各行各业正朝着数字化方向转型&#xff0c…

如何在群晖NAS搭建bitwarden密码管理软件并实现无公网IP远程访问

前言 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊如何在群晖NAS搭建bitwarden密码管理软件并实现无公网IP远程访问&#xff0c;希望大家能觉得实用&#xff01; 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&am…

【数据结构】链表习题之环形链表的约瑟夫问题

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《数据结构》 &#x1f389;道阻且长&#xff0c;行则将至 前言 今天这道题目时牛客上的题目&#xff0c;名为环形链表的约瑟夫问题&#xff0c;很有趣的的一道题目 环形链表的约瑟…

申请免费域名证书

目录 背景&#xff1a; 域名证书是什么&#xff1a; 域名证书有哪些&#xff1a; 部署域名证书有什么用&#xff1a; 免费的域名证书在哪里申请&#xff1a; 背景&#xff1a; 域名是一个IP地址上的“面具” 。一个域名的目的是便于记忆和沟通的一组服务器的地址(网站&…

OpenHarmony开发知识点记录之ABI

OpenHarmony系统支持丰富的设备形态&#xff0c;支持多种架构指令集&#xff0c;支持多种操作系统内核&#xff1b;为了应用在各种OpenHarmony设备上的兼容性&#xff0c;本文定义了"OHOS" ABI&#xff08;Application Binary Interface&#xff09;的基础标准&#…

路由协议RIP(悄悄话)

实验要求&#xff1a;总部和两个分支&#xff0c;拓扑如下图&#xff0c;利用rip路由协议使得各个pc设备可以通信 RIP理解&#xff1a;相邻路由定期交换内部路由协议&#xff0c;最后达到稳定状态&#xff0c;如果发生网络发生变化&#xff0c;重复交换路由步骤直到稳定状态&a…

LinkedIn 互联网架构扩展简史

LinkedIn成立于 2003 年&#xff0c;其目标是连接到您的网络以获得更好的工作机会。第一周只有 2,700 名会员。时间快进了很多年&#xff0c;LinkedIn 的产品组合、会员基础和服务器负载都取得了巨大的增长。 如今&#xff0c;LinkedIn 在全球运营&#xff0c;拥有超过 3.5 亿会…

研华工控机610L学习笔记2:visualstudio与第一个C#程序

今日继续学习工控机 C# 编程相关知识&#xff1a; 这篇结束后我将先进行一段时间的C#的学习研究&#xff0c;并写一些C#的笔记 后续再更新工控机编程设计相关 目录 1、安装visualstudio&#xff1a; 2、创建第一个C#程序&#xff1a; 3、寻找C#解决方案源文件&#xff1a; …

梦幻西游端游全新升级瀚海游戏玩法 一单35 小白一手机没脑子实际操作 日入3000

大家好&#xff0c;很多人都听过抖音游戏外国投资者方案&#xff0c;但是大部分人做视频&#xff0c;收益都非常低。 今天给带来的项目是“梦幻西游端游全新升级瀚海游戏玩法&#xff0c;一单35&#xff0c;轻松日入3000”&#xff0c;这个项目不用去被割韭菜&#xff0c;我自…

IDEA | 资源文件中文乱码问题解决

问题 IDEA打开资源文件&#xff0c;显示乱码问题。 解决方案 1、电脑是mac&#xff0c;点击IDEA->【Preferences】->【Editor】->【File Encodings】 2、选择【Properties Files】中的UTF-8&#xff0c;并勾选Transparent native-to-ascii conversion。 3、最后点击…

P5727 【深基5.例3】冰雹猜想

【深基5.例3】冰雹猜想 - 洛谷https://www.luogu.com.cn/problem/P5727这种方法比较繁琐&#xff0c;预先定义固定的数组长度&#xff0c;很局限&#xff1a; public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.next…

图神经网络和图卷积网络

图神经网络要点 1. GNNs adopt a “graph-in, graph-out” architecture meaning that these model types accept a graph as input, with information loaded into its nodes, edges and global-context, and progressively transform these embeddings, without changing th…

黑马头条知识点总结

黑马头条知识点总结 文章目录 黑马头条知识点总结前言一、使用的所有技术栈二、初始化项目 2.1加密盐登录2.2网关2.3配置nginx三。文章通过freemarker生成html文件存入minio中四。内容安全阿里云接口5.使用延迟任务发布审核文章 4.9.3)redis分布式锁在工具类CacheService中添加…

HWOD:名字的漂亮度

一、题目 描述 给出一个字符串&#xff0c;该字符串仅由小写字母组成&#xff0c;定义这个字符串的漂亮度是其所有字母漂亮度的总和 每个字母都有一个漂亮度&#xff0c;范围在1到26之间。没有任何两个不同字母拥有相同的漂亮度。字母忽略大小写。 给出多个字符串&#xff…

【快速解决】解决谷歌自动更新的问题,禁止谷歌自动更新,如何防止chrome自动升级 chrome浏览器禁止自动升级设置方法

目录 问题描述 解决方法 1、搜索栏搜索控制面板 2、搜索&#xff1a;服务 ​编辑 3、点击Windows工具 4、点击服务 ​5、禁止谷歌更新 问题描述 由于我现在需要装一个谷歌的驱动系统&#xff0c;但是目前的谷歌驱动系统的版本都太旧了&#xff0c;谷歌自身的版本又太新了…