Unity3d---对UI事件接口的一些测试和机制(坑)的总结

Unity3d---对UI事件接口的一些测试和机制(坑)的总结

开文来记录一下自己摸索到的UI事件接口的一些运作机制(坑),就不赘述具体怎么使用这些事件接口了(不做教学...),本文主要是记自己发现的一些坑点

主要针对于在复杂的UI嵌套关系下,鼠标指针和拖拽操作的事件接口的调用关系 ,一些容易出错的地方进行记录

欢迎各路神仙留言指正,若能不吝赐教,鄙人先谢为敬

版本:Unity 2019.3.0


emmmmmm

怎么好像之前Unity2018的跨父对象层级的Drop方法还无法正常调用来着,做背包系统的时候真的坑死了,现在2019这个坑是被官方填了?



1.还是提一下什么是UI事件接口

在UGUI中通过 using UnityEngine.EventSystems;

可以让继承自Mono的脚本实现一些丰富的事件接口, 这些被实现接口方法会在进行UI交互的一些特定的行为出现时被调用

主要包括Point鼠标指针操作,Drag/Drop拖拽操作,Select点选操作,Input鼠标键盘输入类

UI事件接口主要用于UI上的一些物体(Image,Button一类),也可以应用到场景中一些3D物体

UI事件接口能够在 PC鼠标 和 触屏 两种操作模式下被正确的使用

在使用中UI事件接口与EventTrigger实现的交互事件区别在于, 当某个UI物体需要被设计为一个对象 ,在 针对它的特定的交互行为出现 调用该对象自身的一些方法 ,实现一些交互功能时, 应使用UI事件接口 的实现方法,将脚本挂在该物体上。而当 某个UI物体不需要被设计为一个对象 ,在 针对它的特定的交互行为出现时,需要去调用别的物体上某个组件的方法 来实现一些交互功能时, 应使用EventTrigger的实现方法,添加EventTrigger,添加交互行为并绑定别的物体上特定组件的特定方法

UI事件接口和EventTrigger能被正确调用的预备条件一致,即:

针对UI物体时,场景中应创建EventSystem,UI物体的RectTransform需要有一定的宽高

针对3D物体时,场景中应创建EventSystem,摄像机应添加Physics Raycaster组件,3D物体需要有Collider组件



2.准备一下测试

场景物体摆放即父子关系

分别给两个Panel添加一个名为 Test_IEP的脚本

给所有 Button 和 Image 都添加一个名为 Test_IEC的脚本




3.IPointer 鼠标点击事件接口测试

---3.1 父子均实现 IPointerDownHandler, IPointerUpHandler (出现覆盖问题)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestIEP : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
	public void OnPointerUp(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnPointerUp");
    public void OnPointerDown(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnPointerDown");


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestIEC : MonoBehaviour,IPointerDownHandler,IPointerUpHandler
	public void OnPointerUp(PointerEventData eventData)
		Debug.Log("C:" + gameObject.name + "  OnPointerUp");
	public void OnPointerDown(PointerEventData eventData)
		Debug.Log("C:" + gameObject.name + "  OnPointerDown");


------3.1.1 分别点击Panel没有被子对象覆盖的区域,和子对象

我们点击Panel没有被覆盖子对象的部分和子对象

在父子均实现IPointerDownHandler, IPointerUpHandler时,点击子对象覆盖住父对象的范围内时,只触发子对象的接口方法

------3.1.2 同级别下有遮挡时点击重和部分

同级别均实现IPointerDownHandler, IPointerUpHandler,UI物体有重和时,点击重和部分,仅触发最上层被点击到的UI对象的接口方法

补充一下:Hierarchy菜单,UI物体中越靠下的物体,才是越接近最上层的

---3.2 仅父对象实现 IPointerDownHandler, IPointerUpHandler (按钮是个特例)

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestIEC : MonoBehaviour


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestIEP : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
	public void OnPointerUp(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnPointerUp");
    public void OnPointerDown(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnPointerDown");


点击按钮,父对象的接口方法没有被触发
但点击Image正常触发父对象的接口方法

仅父对象实现 IPointerDownHandler, IPointerUpHandler 时,点击子对象中的按钮仍无法触发父对象的接口方法(可以理解为按钮组件本身就有IPointer接口)依然覆盖掉了点击向父对象的传递。但点击到子对象中Image的一类本身不具有交互性的子对象,可以正常触发父对象的接口方法


我们取消按钮的可交互性,点击按钮依然无法触发父对象的接口方法
将按钮组件UnEnable时,点击按钮能够触发父对象的方法

---3.3 重合时仅下层实现 IPointerDownHandler, IPointerUpHandler (遮挡依然存在)

不论下层是否是按钮还是Image,点击重合部分,下层物体实现的接口方法都不会被触发

自然上下层都实现肯定只调用上层实现的接口方法

---3.4 只实现IPointerUpHandler时(无法正常被调用)

只实现IPointerUpHandler根本无法正常调用,必须实现IPointerDownHandler,才能正常传递,调用IPointerUpHandler的接口方法

---3.5 IPointerClickHandler(与3.1,3.2出现的情况类似)

参见3.1 和 3.2 测试结果类似,出现子对象覆盖,同级只触发最上层,以及按钮的特例覆盖效应



4.Drag/Drop 拖拽接口测试

先补充几点:

1. UI事件接口的onDrag方法 在开始拖动后,如果 中途停止拖动(不松开鼠标但不移动)不会被调用 ,而我们有测试过 onMouseDrag方法,开始拖动后即使中途拖动停止,依然会被调用

2.即使在极端状况下, IPointerDownHandler 一定会先于 IBeginDragHandler被调用

3.可以通过 eventData.pointerDrag.name 获取拖动启发的对象 ,并经常结合onDrop使用

4. IBeginDragHandler,IDragHandler,IEndDragHandler调用时均调用启发拖拽的物体 ,而 IDropHandler是拖拽到的物体 (结束拖拽是鼠标停留的物体)

5.通过 eventData.Dragging可以判断是否处于拖拽状态

---4.1 父子对象 均实现IBeginDragHandler,IDragHandler,IEndDragHandler,IDropHandler(依然有遮挡问题,跨层级拖拽正常)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestIEP : MonoBehaviour,IBeginDragHandler,IDragHandler,IEndDragHandler,IDropHandler
	public void OnBeginDrag(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnBeginDrag");
	public void OnDrag(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnDrag");
	public void OnDrop(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnDrop"+"  frmo:"+eventData.pointerDrag.name);
	public void OnEndDrag(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnEndDrag");


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestIEC : MonoBehaviour,IBeginDragHandler, IDragHandler, IEndDragHandler, IDropHandler
	public void OnBeginDrag(PointerEventData eventData)
		Debug.Log("C:" + gameObject.name + "  OnBeginDrag");
	public void OnDrag(PointerEventData eventData)
		Debug.Log("C:" + gameObject.name + "  OnDrag");
	public void OnDrop(PointerEventData eventData)
		Debug.Log("C:" + gameObject.name + "  OnDrop" + "  frmo:" + eventData.pointerDrag.name);
	public void OnEndDrag(PointerEventData eventData)
		Debug.Log("C:" + gameObject.name + "  OnEndDrag");


panel_1内部
panel_1到panel_2
pane_1到panel_2中的Button
panel_1到自己内部的button
panel_1内部button到panel_1
button到重合部分
panel_1中的button到panel_2
panel_1中的button到panel_2中的button
panel_2到重合部分
重和部分到button_1

父子对象 均实现IBeginDragHandler,IDragHandler,IEndDragHandler,IDropHandler时,注意遮挡问题,子对象对父对象的遮挡,重和部分的遮挡,以及onDrop一定先于endDrag调用。其他没有什么特别的地方

---4.2 仅父物体实现IBeginDragHandler,IDragHandler,IEndDragHandler,IDropHandler(这次谁也挡不住了)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestIEP : MonoBehaviour,IBeginDragHandler,IDragHandler,IEndDragHandler,IDropHandler
	public void OnBeginDrag(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnBeginDrag");
	public void OnDrag(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnDrag");
	public void OnDrop(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnDrop"+"  frmo:"+eventData.pointerDrag.name);
	public void OnEndDrag(PointerEventData eventData)
		Debug.Log("P:" + gameObject.name + "  OnEndDrag");


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;