Unity中UGUI接口的实现和触发条件
UnityEngine.EventSystems
介绍
这是我最常用到的一些接口。当UGUI本身提供的一些脚本无法实现需求时,我就会用以下的接口组合成一个适合的脚本。
其中可以大体的分为以下几个部分:
Pointer鼠标指针点击接口
- IPointerEnterHandler指针进入
- IPointerDownHandler指针按下
- IPointerClickHandler指针点击
- IPointerUpHandler指针抬起
- IPointerExitHandler指针离开
Drag拖拽接口
- IBeginDragHandler拖动开始
- IDragHandler拖动
- IEndDragHandler拖动结束
- IDropHandler接收拖动
- IInitializePotentialDragHandler初始化潜在的拖动
选择组事件
必须设置选择对象后才能触发EventSystem.current.SetSelectedGameObject(gameobject)
- ISelectHandler选择
- IDeselectHandler取消选择
- IUpdateSelectedHandler选中的物体每帧触发
滚轮滚动
- IScrollHandler
InputManager关联组事件(同选择组要求)
- IMoveHandler移动(Horizontal和Vertical按键)
- ICancelHandlerEsc键
- ISubmitHandlerEnter键
代码
using UnityEngine;
using UnityEngine.EventSystems;
public class UGUITest : MonoBehaviour,
IPointerEnterHandler, IPointerDownHandler, IPointerClickHandler, IPointerUpHandler, IPointerExitHandler,
IBeginDragHandler, IDragHandler, IEndDragHandler, IDropHandler, IInitializePotentialDragHandler,
ISelectHandler, IDeselectHandler, IUpdateSelectedHandler,
IScrollHandler,
IMoveHandler, ICancelHandler, ISubmitHandler
{
#region Pointer
/// <summary>
/// 指针进入事件
/// ------
/// 当鼠标光标移入该对象时触发
/// </summary>
/// <param name="eventData"></param>
public virtual void OnPointerEnter(PointerEventData eventData) { Debug.Log("PointerEnter"); }
/// <summary>
/// 指针按下事件
/// ------
/// 鼠标点击A对象,按下鼠标时A对象响应此事件
/// </summary>
/// <param name="eventData"></param>
public virtual void OnPointerDown(PointerEventData eventData) { Debug.Log("PointerDown"); }
/// <summary>
/// 指针点击事件
/// ------
/// 在组件可视的区域按下且抬起时指针处于区域内(按下离开区域后抬起不会触发)
/// </summary>
/// <param name="eventData"></param>
public virtual void OnPointerClick(PointerEventData eventData) { Debug.Log("PointerClick"); }
/// <summary>
/// 指针抬起事件
/// ------
/// 鼠标点击A对象,抬起鼠标时响应,无论鼠标在何处抬起(即不在A对象中),都会在A对象中响应此事件
/// 注:响应此事件的前提是A对象必须响应过OnPointerDown事件
/// </summary>
/// <param name="eventData"></param>
public virtual void OnPointerUp(PointerEventData eventData) { Debug.Log("PointerUp"); }
/// <summary>
/// 指针离开事件
/// ------
/// 当鼠标光标移出该对象时触发
/// </summary>
/// <param name="eventData"></param>
public virtual void OnPointerExit(PointerEventData eventData) { Debug.Log("PointerExit"); }
#endregion
#region Drag
/// <summary>
/// 拖动开始事件
/// ------
/// 当鼠标在A对象按下后,不抬起,开始拖拽时 A对象响应此事件
/// 此事件在OnInitializePotentialDrag之后响应 OnDrag之前响应
/// </summary>
/// <param name="eventData"></param>
public virtual void OnBeginDrag(PointerEventData eventData) { Debug.Log("BeginDrag"); }
/// <summary>
/// 拖动中事件
/// ------
/// 当鼠标拖拽A对象时,A对象每帧响应一次此事件
/// 注:如果不实现此接口,则后面的三个接口方法都不会触发
/// </summary>
/// <param name="eventData"></param>
public virtual void OnDrag(PointerEventData eventData) { Debug.Log("Drag"); }
/// <summary>
/// 拖动结束事件
/// ------
/// 当鼠标拖拽A对象后,鼠标抬起时 A对象响应此事件
/// </summary>
/// <param name="eventData"></param>
public virtual void OnEndDrag(PointerEventData eventData) { Debug.Log("EndDrag"); }
/// <summary>
/// 接收拖动事件
/// ------
/// A、B对象必须均实现IDropHandler接口,且A至少实现IDragHandler接口
/// 当鼠标从A对象上开始拖拽,在B对象上抬起时 B对象响应此事件
/// 此时name获取到的是B对象的name属性
/// eventData.pointerDrag表示发起拖拽的对象(GameObject)
/// </summary>
/// <param name="eventData"></param>
public virtual void OnDrop(PointerEventData eventData) { Debug.Log("Drop"); }
/// <summary>
/// 初始化潜在的拖动事件
/// ------
/// 当鼠标在A对象按下还没开始拖拽时 A对象响应此事件
/// 注:此接口事件与IPointerDownHandler接口事件类似
/// 二者的执行顺序:先执行IPointerDownHandler,然后执行此接口事件
/// </summary>
/// <param name="eventData"></param>
public virtual void OnInitializePotentialDrag(PointerEventData eventData) { Debug.Log("InitializePotentialDrag"); }
#endregion
#region 选择组事件
/***必须设置选择对象后才能触发--EventSystem.current.SetSelectedGameObject(gameobject)***/
/// <summary>
/// 选择事件
/// </summary>
/// <param name="eventData"></param>
public virtual void OnSelect(BaseEventData eventData) { Debug.Log("Select"); }
/// <summary>
/// 取消选择事件
/// </summary>
/// <param name="eventData"></param>
public virtual void OnDeselect(BaseEventData eventData) { Debug.Log("Deselect"); }
/// <summary>
/// 选中物体每帧触发事件
/// </summary>
/// <param name="eventData"></param>
public virtual void OnUpdateSelected(BaseEventData eventData) { Debug.Log("UpdateSelected"); }
#endregion
/// <summary>
/// 滚动事件
/// ------
/// 滑轮在上面滚动时触发
/// </summary>
/// <param name="eventData"></param>
public virtual void OnScroll(PointerEventData eventData) { Debug.Log("Scroll"); }
#region InputManager关联组事件(同选择组要求)
/// <summary>
/// 移动事件(上下左右)
/// ------
/// 与InputManager里的Horizontal和Vertical按键相对应。Input.GetAxisRaw
/// </summary>
/// <param name="eventData"></param>
public virtual void OnMove(AxisEventData eventData) { Debug.Log("Move"); }
/// <summary>
/// 取消事件
/// ------
/// 按下InputManager里的Cancel对应的按键(PC、Mac默认:Esc键)。Input.GetButtonDown
/// </summary>
/// <param name="eventData"></param>
public virtual void OnCancel(BaseEventData eventData) { Debug.Log("Cancel"); }
/// <summary>
/// 提交事件
/// ------
/// 按下InputManager里的Submit对应的按键(PC、Mac默认:Enter键)。Input.GetButtonDown
/// </summary>
/// <param name="eventData"></param>
public virtual void OnSubmit(BaseEventData eventData) { Debug.Log("Submit"); }
#endregion
}
示例
有一个相关示例,附上链接:https://blog.csdn.net/f_957995490/article/details/103928986
UnityEngine.UI
下面的接口我也没有用过多少,但网文中有就测试了几个,简单记录。
IMaterialModifier
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 材质修改接口
/// </summary>
public class MaterialModifier : MonoBehaviour, IMaterialModifier
{
public Material GetModifiedMaterial(Material baseMaterial)
{
Debug.Log("IMaterialModifier.GetModifiedMaterial");
Debug.Log(baseMaterial);
return null;
}
public Material m_material = null;
private void Awake()
{
Debug.Log(GetModifiedMaterial(m_material));
}
}
无论是在编辑还是运行情况下,只要改变UI中的材质,便会被调用,函数中的参数baseMaterial便是要改变的目标材质。
ICanvasRaycastFilter
using UnityEngine;
/// <summary>
/// 光线投射过滤器
/// </summary>
public class CanvasRaycastFilter : MonoBehaviour, ICanvasRaycastFilter
{
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
Debug.Log("ICanvasRaycastFilter.IsRaycastLocationValid");
Debug.Log(sp + " " + eventCamera.name);
return false;
//throw new NotImplementedException();
}
}
运行时,只要鼠标在该UI对象的范围内,便会被持续调用。其中参数sp为屏幕坐标系下光标的实时坐标;参数eventCamera为,管理该UI对象的Canvas组件中所记录的Camera对象,如果Canvas组件的Render Mode属性是在Screen Space_Overlay状态时,该参数则是为空(NULL)值。
ICanvasElement
using System;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 需要canvas绘制的对象都实现该接口,如text, image等
/// </summary>
public class CanvasElement : MonoBehaviour, ICanvasElement
{
Transform ICanvasElement.transform
{
get
{
Debug.Log("ICanvasElement.transform");
throw new NotImplementedException();
}
}
void ICanvasElement.GraphicUpdateComplete()
{
Debug.Log("ICanvasElement.GraphicUpdateComplete");
throw new NotImplementedException();
}
bool ICanvasElement.IsDestroyed()
{
Debug.Log("ICanvasElement.IsDestroyed");
throw new NotImplementedException();
}
void ICanvasElement.LayoutComplete()
{
Debug.Log("ICanvasElement.LayoutComplete");
throw new NotImplementedException();
}
void ICanvasElement.Rebuild(CanvasUpdate executing)
{
Debug.Log("ICanvasElement.Rebuild");
throw new NotImplementedException();
}
}
IClippable
using System;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 可裁剪功能
/// </summary>
public class Clippable : MonoBehaviour, IClippable
{
public RectTransform rectTransform
{
get
{
Debug.Log("IClippable.rectTransform");
throw new NotImplementedException();
}
}
public void Cull(Rect clipRect, bool validRect)
{
Debug.Log("IClippable.Cull");
throw new NotImplementedException();
}
public void RecalculateClipping()
{
Debug.Log("IClippable.RecalculateClipping");
throw new NotImplementedException();
}
public void SetClipRect(Rect value, bool validRect)
{
Debug.Log("IClippable.SetClipRect");
throw new NotImplementedException();
}
}
IClipper
using System;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 执行裁切功能接口
/// </summary>
public class Clipper : MonoBehaviour, IClipper
{
void IClipper.PerformClipping()
{
Debug.Log("IClipper.PerformClipping");
throw new NotImplementedException();
}
}
ILayoutController / ILayoutGroup:ILayoutController / ILayoutGroup:ILayoutController
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 渲染时和IMaterialModifier以其调用的
/// </summary>
public class LayoutController : MonoBehaviour, ILayoutController
{
public void SetLayoutHorizontal()
{
Debug.Log("ILayoutController.SetLayoutHorizontal");
}
public void SetLayoutVertical()
{
Debug.Log("ILayoutController.SetLayoutVertical");
}
}
ILayoutElement
using System;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 布局元素的接口功能
/// </summary>
public class LayoutElement : MonoBehaviour, ILayoutElement
{
public float flexibleHeight
{
get
{
Debug.Log("ILayoutElement.flexibleHeight");
throw new NotImplementedException();
}
}
float ILayoutElement.flexibleWidth
{
get
{
Debug.Log("ILayoutElement.flexibleWidth");
throw new NotImplementedException();
}
}
int ILayoutElement.layoutPriority
{
get
{
Debug.Log("ILayoutElement.layoutPriority");
throw new NotImplementedException();
}
}
float ILayoutElement.minHeight
{
get
{
Debug.Log("ILayoutElement.minHeight");
throw new NotImplementedException();
}
}
float ILayoutElement.minWidth
{
get
{
Debug.Log("ILayoutElement.minWidth");
throw new NotImplementedException();
}
}
float ILayoutElement.preferredHeight
{
get
{
Debug.Log("ILayoutElement.preferredHeight");
throw new NotImplementedException();
}
}
float ILayoutElement.preferredWidth
{
get
{
Debug.Log("ILayoutElement.preferredWidth");
throw new NotImplementedException();
}
}
void ILayoutElement.CalculateLayoutInputHorizontal()
{
Debug.Log("ILayoutElement.CalculateLayoutInputHorizontal");
}
void ILayoutElement.CalculateLayoutInputVertical()
{
Debug.Log("ILayoutElement.CalculateLayoutInputVertical");
}
}
ILayoutIgnorer
using System;
using UnityEngine;
using UnityEngine.UI;
public class LayoutIgnorer : MonoBehaviour, ILayoutIgnorer
{
public bool ignoreLayout
{
get
{
Debug.Log("ILayoutIgnorer.ignoreLayout");
throw new NotImplementedException();
}
}
}
IMaskable
using System;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 透明遮罩接口
/// </summary>
public class Maskable : MonoBehaviour, IMaskable
{
void IMaskable.RecalculateMasking()
{
Debug.Log("IMaskable.RecalculateMasking");
throw new NotImplementedException();
}
}
除了最前面两个接口,其他的暂时没有时间测试出结果。