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 Raycaster、Physics 2D Raycaster或GraphicRaycaster等组件来完成的。
Physics Raycaster和Physics 2D Raycaster是用来处理3D和2D物体的射线投射的,它们可以使你的3D和2D物体响应EventTrigger定义的事件。
GraphicRaycaster则是用来处理UI元素的射线投射的,它可以使你的UI元素响应EventTrigger定义的事件。所以,如果你希望你的游戏对象能够响应
EventTrigger定义的事件,你需要在相应的Canvas或Camera上添加一个射线投射器组件。
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会调用与这个事件相关的处理器。
相关文章:
Unity | 射线检测及EventSystem总结
目录 一、知识概述 1.Input.mousePosition 2.Camera.ScreenToWorldPoint 3.Camera.ScreenPointToRay 4.Physics2D.Raycast 二、射线相关 1.3D(包括UI)、射线与ScreenPointToRay 2.3D(包括UI)、射线与ScreenToWorldPoint …...
职业经验 2024 年测试求职手册
原贴地址: 2024 年测试求职手册 TesterHome 经历年前年后差不多 2 个月左右时候的求职,是时候总结复盘一下了,本打算在自己有着落再复盘,但是一想那时候似乎价值就没现在去做显得有意义一些,这篇帖子更多的是让大家看下有没有心…...
Spring Boot与Redis深度整合:实战指南
Spring Boot 整合 Redis 相当简单,它利用了 Spring Data Redis 项目,使得我们可以在 Spring Boot 应用中轻松地操作 Redis。以下是如何整合 Redis 到 Spring Boot 应用的基本步骤: 1. 添加依赖 首先,在你的 pom.xml 文件中添加 …...
微服务(基础篇-006-Docker安装-CentOS7)
目录 05-初识Docker-Docker的安装_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LQ4y127n4?p46&spm_id_frompageDriver&vd_source60a35a11f813c6dff0b76089e5e138cc 0.安装Docker 1.CentOS安装Docker 1.1.卸载(可选) 1.2.安装dock…...
前端-css-01
1.CSS 长度单位和颜色设置 1.1CSS 中的长度单位 px 像素 em 字体大小的倍数(字体默认是16px) % 百分比 1.2CSS 中的颜色设置方式 1.2.1使用颜色名表示颜色 red、orange、yellow、green、cyan、blue、purple、pink、deeppink、skyblue、greenyellow .…...
Java学习36-Java 多线程安全:懒汉式和饿汉式
JAVA种有两种保证线程安全的方式,分别叫懒汉式Lazy Initialization和饿汉式Eager Initialization,以下是他们的区别: 线程安全性: 懒汉式本身是非线程安全的,因为多个线程可能同时检查实例是否为null,并尝…...
sql常用之CASE WHEN THEN
sql常用之CASE WHEN THEN SQL中的 CASE 类似编程语言里的 if-then-else 语句,用做逻辑判断。可以用于SELECT语句中,也可以用在WHERE,GROUP BY 和 ORDER BY 子句;可以单独使用,也可以和聚合函数结合使用。 语法&#…...
【PduR路由】IPduM模块详细介绍
目录 1.IpduM功能简介 2.IpduM模块依赖的其他模块 2.1RTE (BSW Scheduler) 2.2PDU Router 2.3COM 3.IpduM功能详解 3.1 功能概述 3.2 I-PDU多路复用I-PDU Multiplexing 3.2.1 Definitions and Layout 3.2.2通用功能描述 General 3.2.3模块初始化 Initialization 3.…...
【MySQL】6.MySQL主从复制和读写分离
主从复制 主从复制与读写分离 通常数据库的读/写都在同一个数据库服务器中进行; 但这样在安全性、高可用性和高并发等各个方面无法满足生产环境的实际需求; 因此,通过主从复制的方式同步数据,再通过读写分离提升数据库的并发负载…...
Lucene及概念介绍
Lucene及概念介绍 基础概念倒排索引索引合并分析查询语句的构成 基础概念 Document:我们一次查询或更新的载体,对比于实体类 Field:字段,是key-value格式的数据,对比实体类的字段 Item:一个单词࿰…...
密码算法概论
基本概念 什么是密码学? 简单来说,密码学就是研究编制密码和破译密码的技术科学 例题: 密码学的三个阶段 古代到1949年:具有艺术性的科学1949到1975年:IBM制定了加密标准DES1976至今:1976年开创了公钥密…...
实时数仓之实时数仓架构(Hudi)
目前比较流行的实时数仓架构有两类,其中一类是以FlinkDoris为核心的实时数仓架构方案;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对FlinkHudi湖仓一体架构进行介绍,这套架构的特点是可以基于一套数据完全实现Lambda架构。实时数…...
2022-04-15_for循环等_作业
for循环 编写程序数一下 1到 100 的所有整数中出现多少个数字9计算1/1-1/21/3-1/41/5 …… 1/99 - 1/100 的值,打印出结果求10 个整数中最大值在屏幕上输出9*9乘法口诀表二分查找 编写程序数一下 1到 100 的所有整数中出现多少个数字9 #include <stdio.h>in…...
脑机辅助推导算法
目录 一,背景 二,华容道中道 1,问题 2,告诉脑机如何编码一个正方形格子 3,让脑机汇总信息 4,观察图,得到启发式算法 5,根据启发式算法求出具体解 6,可视化 一&am…...
【原创教程】三菱FX PLC控制FR-E740变频器
变频器的使用 1. 使用三菱FX PLC 控制变频器时,接线图请按下图所示接线。 各个端子的说明如下: R、S、T:变频器电源,E740变频器电源位3相380V。 STF:正转启动, STF信号ON时为正转、OFF时为停止指令。 STR :反转启动,STR信号ON时为反转、OFF时为停止指令。 RH、RM、RL…...
重读Java设计模式: 深入探讨建造者模式,构建复杂对象的优雅解决方案
引言 在软件开发中,有时需要构建具有复杂结构的对象,如果直接使用构造函数或者 setter 方法逐个设置对象的属性,会导致代码变得冗长、难以维护,并且容易出错。为了解决这个问题,我们可以使用建造者模式。 一、建造者…...
C语言数据结构易错知识点(6)(快速排序、归并排序、计数排序)
快速排序属于交换排序,交换排序还有冒泡排序,这个太简单了,这里就不再讲解。 归并排序和快速排序都是采用分治法实现的排序,理解它们对分支思想的感悟会更深。 计数排序属于非比较排序,在数据集中的情况下可以考虑使…...
使用 React Router v6.22 进行导航
使用 React Router v6.22 进行导航 React Router v6.22 是 React 应用程序中最常用的路由库之一,提供了强大的导航功能。本文将介绍如何在 React 应用程序中使用 React Router v6.22 进行导航。 安装 React Router 首先,我们需要安装 React Router v6…...
单链表的插入和删除
一、插入操作 按位序插入(带头结点): ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。 typedef struct LNode{ElemType data;struct LNode *next; }LNode,*LinkList;//在第i 个位置插插入元素e (带头结点) bool Li…...
全量知识系统 之“程序”详细设计 之 “絮”---开端“元素周期表”表示的一个“打地鼠”游戏
全量知识系统 之“程序”详细设计 概述-概要和纪要 序 絮(一个极简的开场白--“全量知识系统”自我介绍) 将整个“人生”的三个阶段 比作“幼稚园”三班 : 第一步【想】-- “感性”思维游戏:打地鼠 。学前教育-新生期&#x…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
