简述

最近在用UGUI的时候遇到了鼠标穿透的问题,就是说在UGUI和3D场景混合的情况下,点击UI区域同时也会 触发3D中物体的鼠标事件。比如下图中

这里给Cube加了一个鼠标点击改变颜色的代码,如下

    void Update(){if(Input.GetMouseButtonDown(0)){GetComponent<Renderer>().material.color = new Color(Random.value, Random.value, Random.value, 1.0f);}}

运行一下,会发现只要有鼠标点击(任何位置点击),Cube的颜色就会改变,根据代码我们知道这也是必然的,但是问题是如果Cube是一个3D世界中的mesh或者terrain,而button是UI的话也同样会出现同样的问题。

在游戏开发中我们的UI是始终出现在屏幕的,如果在一个战斗场景中用户点了UI战斗场景中的物体也会作出响应肯定是有问题的!

其实关于这个问题网上有不少解决方法了,但是总感觉没有一个是适合我的需求,或者说没有一个最好的答案。

其中提到最多的是利用EventSystem.current.IsPointerOverGameObject()来判断,这个方法的意义是判断鼠标是否点到了GameObject上面,这个GameObject包括UI也包括3D世界中的任何物体,所以他只能判断用户是都点到了东西。对于本文中的问题意义不是很大。那么这个问题到底该怎么解决呢?

原理

解决方法最终还是离不开射线检测,不过UGUI中已经封装了针对UI部分的射线碰撞的功能,那就是GraphicRaycaster类。里面有个Raycast方法如下,最终就是将射线碰撞到的点添加进resultAppendList数组。

        public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList){if (canvas == null)return;// Convert to view space
            Vector2 pos;if (eventCamera == null)pos = new Vector2(eventData.position.x / Screen.width, eventData.position.y / Screen.height);elsepos = eventCamera.ScreenToViewportPoint(eventData.position);// If it's outside the camera's viewport, do nothingif (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f)return;float hitDistance = float.MaxValue;Ray ray = new Ray();if (eventCamera != null)ray = eventCamera.ScreenPointToRay(eventData.position);if (canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None){float dist = eventCamera.farClipPlane - eventCamera.nearClipPlane;if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All){RaycastHit hit;if (Physics.Raycast(ray, out hit, dist, m_BlockingMask)){hitDistance = hit.distance;}}if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All){RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, dist, m_BlockingMask);if (hit.collider != null){hitDistance = hit.fraction * dist;}}}m_RaycastResults.Clear();Raycast(canvas, eventCamera, eventData.position, m_RaycastResults);for (var index = 0; index < m_RaycastResults.Count; index++){var go = m_RaycastResults[index].gameObject;bool appendGraphic = true;if (ignoreReversedGraphics){if (eventCamera == null){// If we dont have a camera we know that we should always be facing forwardvar dir = go.transform.rotation * Vector3.forward;appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;}else{// If we have a camera compare the direction against the cameras forward.var cameraFoward = eventCamera.transform.rotation * Vector3.forward;var dir = go.transform.rotation * Vector3.forward;appendGraphic = Vector3.Dot(cameraFoward, dir) > 0;}}if (appendGraphic){float distance = 0;if (eventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)distance = 0;else{// http://geomalgorithms.com/a06-_intersect-2.htmldistance = (Vector3.Dot(go.transform.forward, go.transform.position - ray.origin) / Vector3.Dot(go.transform.forward, ray.direction));// Check to see if the go is behind the camera.if (distance < 0)continue;}if (distance >= hitDistance)continue;var castResult = new RaycastResult{gameObject = go,module = this,distance = distance,index = resultAppendList.Count,depth = m_RaycastResults[index].depth,sortingLayer =  canvas.sortingLayerID,sortingOrder = canvas.sortingOrder};resultAppendList.Add(castResult);}}}

从这个方法开始深入查看Unity UGUI源码你会发现,其实每个组件在创建的时候已经被添加进了一个公共列表,UGUI 源码中的GraphicRegistry类就是专门干这件事的。再看下Graphic类中的OnEnable方法

        protected override void OnEnable(){base.OnEnable();CacheCanvas();GraphicRegistry.RegisterGraphicForCanvas(canvas, this);#if UNITY_EDITORGraphicRebuildTracker.TrackGraphic(this);
#endifif (s_WhiteTexture == null)s_WhiteTexture = Texture2D.whiteTexture;SetAllDirty();SendGraphicEnabledDisabled();}

看这句GraphicRegistry.RegisterGraphicForCanvas(canvas, this);就是注册需要做射线检测的UI组件。再看他内部是如何工作的

        public static void RegisterGraphicForCanvas(Canvas c, Graphic graphic){if (c == null)return;IndexedSet<Graphic> graphics;instance.m_Graphics.TryGetValue(c, out graphics);if (graphics != null){graphics.Add(graphic);return;}graphics = new IndexedSet<Graphic>();graphics.Add(graphic);instance.m_Graphics.Add(c, graphics);}

不过,问题又来了,为什么是添加进列表的对象都是Graphic类型呢?这跟ScrollRect,Button,Slider这些有关吗?其实,这就跟UGUI的类继承关系有关了,其实我们使用的UGUI中的每个组件都是继承自Graphic或者依赖一个继承自Graphic的组件

看一下UGUI的类层次结构就会一目了然,如下

看图就会更加清楚,在这我们可以把我们用到的UGUI的所有组件分为两类,1.是直接继承自Graphic的组件。2.是依赖于1的组件"[RequireComponent(typeof(Griphic))]",仔细想想会发现,所有组件都属于这两种中的某一种。

所以对所有Graphic进行Raycast其实就相当于对所有UI组件进行Raycast。

结合上面的知识所以,解决这个问题最好的方法是根据,UGUI的射线碰撞来做。这样会比较合理。

解决方案

这里我们直接在使用Input.GetMouseButtonDown(0)的地方加了一个检测函数,CheckGuiRaycastObjects,如下

    bool CheckGuiRaycastObjects(){PointerEventData eventData = new PointerEventData(Main.Instance.eventSystem);eventData.pressPosition = Input.mousePosition;eventData.position = Input.mousePosition;List<RaycastResult> list = new List<RaycastResult>();Main.Instance.graphicRaycaster.Raycast(eventData, list);//Debug.Log(list.Count);return list.Count > 0;}

不过在使用时需要先获取两个加粗显示的变量,graphicRaycaster和eventSystem。

这两个变量分别对应的是Canvas中的GraphicRaycaster组件,和创建UI时自动生成的“EventSystem”中的EventSystem组件,用的是自己指定一下就可以。

然后在使用的时候可以这样:

    void Update () {if (CheckGuiRaycastObjects()) return;//Debug.Log(EventSystem.current.gameObject.name);if (Input.GetMouseButtonDown(0)){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;if (Physics.Raycast(ray, out hit)){//do some thing
            }}}    

还有一个需要注意的地方就是,在做UI的时候一般会用一个Panel做跟目录,这个panel也会被添加到GraphicRegistry中的公共列表中,如果是这样的话记得把list.Count>0改成list.Count>1,或者直接删除Panel上的继承自Graphic的组件。

这样在结合着EventSystem.current.IsPointerOverGameObject()来使用就比较好了。

本文固定连接:http://www.cnblogs.com/fly-100/p/4570366.html

ok了,现在舒服多啦!

转载于:https://www.cnblogs.com/fly-100/p/4570366.html

Unity UGUI —— 鼠标穿透UI问题(Unity官方的解决方法)相关推荐

  1. Unity UGUI 鼠标悬停一段时间显示Text文字

    Unity UGUI 鼠标悬停一段时间显示Text文字 using System.Collections; using System.Collections.Generic; using UnityE ...

  2. Unity UGUI 效果 之 UI 元素 多边形UI (例如雷达图,圆形,不规则多边形 UI等)显示 的简单实现的几种方法整理

    Unity UGUI 效果 之 UI 元素 多边形UI (例如雷达图,圆形,不规则多边形 UI等)显示 的简单实现的几种方法整理 目录 Unity UGUI 效果 之 UI 元素 多边形UI (例如雷 ...

  3. linux电脑滚轮不能用,图文详解电脑鼠标滚轮不动了怎么办_电脑鼠标滚轮不能用的三种解决方法-系统城...

    近期,许多小伙伴发现电脑鼠标滚轮失灵了,无缘无故的就无法使用,浏览网页玩游戏都没办法继续进行,真的很麻烦.针对电脑鼠标滚轮不动了的故障,小编收集整理具体原因和解决方法给大家,希望大家采纳! 原因一:鼠 ...

  4. ubuntu18.04鼠标可以移动但是无法点击解决方法

    ubuntu18.04鼠标可以移动但是无法点击解决方法 问题: 在使用vscode编辑C++工程的时候,vscode出现卡顿现象,然后鼠标就无法点击了,但是可以自由移动,并且可以使用键盘. 环境: 台 ...

  5. spark master web ui 端口8080被占用解决方法

    spark master web ui 端口8080被占用解决方法 Spark master web ui 默认端口为8080,当系统有其它程序也在使用该接口时,启动master时也不会报错,spar ...

  6. 计算机右键有时不好用,Win10鼠标右键失灵怎么办?Win10鼠标右键没反应不能用的解决方法...

    鼠标的左键和右键都是重要的,两者缺一不可.如果在点击鼠标右键的时候发现没反应不能用的情况,那么该如何解决呢?下面装机之家分享一下Win10鼠标右键没反应不能用的解决方法,由于鼠标右键失灵的原因有很多, ...

  7. MacBook鼠标指针乱窜-不受控制问题的解决方法

    MacBook鼠标指针乱窜-不受控制问题的解决方法 这是个很头疼的问题,终于解决了 用的MacBook Pro最近出现了奇怪的问题.出问题时,鼠标不受控制,屏幕上鼠标指针乱窜,还时不时自动点击,犹如电 ...

  8. 计算机管理里面没有鼠标,win7系统设备管理器鼠标属性没有电源管理的详细解决方法_win7教程_uc电脑园...

    随着电脑的使用率越来越高,我们有时候可能会遇到win7系统设备管理器鼠标属性没有电源管理的问题,如果我们遇到了win7系统设备管理器鼠标属性没有电源管理的问题,要怎么处理win7系统设备管理器鼠标属性 ...

  9. Unity UGUI优化与原理【unity官方】

    来源( 来源:unity官方 Optimizing Unity UI ) 官方链接: [1]  https://unity3d.com/cn/learn/tutorials/temas/best-pr ...

  10. 为什么手机升级系统反应慢了_win7系统鼠标右键刷新反应慢原因【解决方法】...

    在win7系统当中,我们在执行一些操作之后,回到桌面上,我们都会习惯点击右键刷新桌面,不过如果桌面刷新的时候反应很慢,甚至需要十几才有反应,这绝对事件很烦恼的事情,那么win7系统鼠标右键刷新反应慢怎 ...

最新文章

  1. python贴吧爬虫-Python 爬虫练习: 爬取百度贴吧中的图片
  2. PAT甲级1121 Damn Single :[C++题解]哈希表、结构体
  3. Rhythmk 学习 Hibernate 08 - Hibernate annotation 关联关系注解
  4. mysql ppl_浅析pplx库的设计与实现。
  5. VMware 常见使用问题梳理
  6. 【hortonworks/registry】NoClassDefFoundError: org/apache/zookeeper/Watcher
  7. PropertyGrid类别排序
  8. 奶瓶(beini) 又一蹭网神器 1.2.2增强版带600万密码字典
  9. 粉色的html 标签,22个粉色主调的网页设计欣赏
  10. PostgreSQL 简介
  11. LeetCode -剑指Offer 06 - 从尾到头打印链表 - java - 细喔
  12. j1900适合装哪版群晖_4000-10000元适合家庭使用的台式电脑选购指南(9月)
  13. 除了中国知网和谷歌文学还有哪些好的有权威的资源站?
  14. Tensorflow 2.X h5转pb
  15. 如何使用phpdoc生成PHP文档
  16. Vue中props属性
  17. I2C 连接 12864 OLED 屏幕
  18. C++题目分享之小鱼比可爱
  19. finally的一个妙用
  20. 拼多多2020秋招【提前批】笔试

热门文章

  1. 多标签图像分类任务的评价方法——mAP
  2. 1、pandas入门示例
  3. 冯诺依曼计算机主机,冯诺依曼计算机的基本思想是什么?
  4. json标准格式举例_JSON格式简介及一些对应函数
  5. r语言算巢式设计方差分析_R语言进行方差分析
  6. C++ set 排序 修改元素之后不会改变原来的排序
  7. 什么是运行时Runtime、运行时库Runtime Library、运行时环境Runtime environment
  8. Docker教程小白实操入门(12)--如何使用build指令根据Dockerfile文件构建一个镜像
  9. 【vedio】html 视频有声音没有图像
  10. 基于springboot的高校后勤系统