赞
踩
今天来聊一下关于Button的事件拓展,这里只是拿Button来举例,Unity中其他的UI组件如Toggle、Slider等都也适用。
我们知道在Button中我们可以通过onClick的方式来添加点击事件,但在游戏开发过程中我们往往对Button有着更多的功能需求,比如说双击、长按、按钮按下、按钮弹起等。这里举一个游戏中实际的例子,在游戏背包中的道具,单击道具时我们需要显示道具的tips框,双击时我们会去使用道具,长按时我们则可以拖动道具,当长按弹起时则道具回到原位或移动到新格子内。虽然这里的背包道具不是按钮,但在单个UI组件上集合了单击、双击、长按、按钮弹起等事件的响应。接下来将介绍如何拓展UI组件来实现这些功能。
首先我们来认识一下Selectable这个类,Selectable是所有交互组件的基类,Unity原生的Button组件就是继承了Selectable,我们要拓展Button功能也是对Selectable下的OnPointerDown、OnPointerUp等接口进行重写。
话不多说先上代码,代码有点长大家可以先跳过细节,后面慢慢讲解。
- using System;
- using UnityEngine;
- using UnityEngine.UI;
- using UnityEngine.EventSystems;
-
-
- public class ExButton : Button
- {
- private enum EnumExButtonState
- {
- /// <summary>空</summary>
- None,
- /// <summary>鼠标按下</summary>
- PointerDown,
- /// <summary>鼠标按下</summary>
- PointerUp,
- /// <summary>单击</summary>
- Click,
- /// <summary>双击</summary>
- DoubleClick,
- /// <summary>长按开始</summary>
- PressBegin,
- /// <summary>长按</summary>
- Press,
- /// <summary>长按结束</summary>
- PressEnd,
- }
-
- /// <summary>按钮状态</summary>
- private EnumExButtonState mButtonState = EnumExButtonState.None;
- /// <summary>鼠标按下时间</summary>
- private float mPointerDownTime = 0.0f;
- [SerializeField]
- /// <summary>双击间隔时间</summary>
- private float mDoubleClickInterval = 0.2f;
- [SerializeField]
- /// <summary>长按开始时间</summary>
- private float mPressBeginTime = 0.3f;
- [SerializeField]
- /// <summary>长按间隔时间,0为每帧调用</summary>
- private float mPressIntervalTime = 0.2f;
- /// <summary>长按缓存时间</summary>
- private float mPressCacheTime = 0f;
-
- public Action OnClick { get; set; }
- public Action OnDoubleClick { get; set; }
- public Action OnPressBegin { get; set; }
- public Action OnPress { get; set; }
- public Action OnPressEnd { get; set; }
-
- public override void OnPointerDown(PointerEventData eventData)
- {
- base.OnPointerDown(eventData);
-
- if (OnDoubleClick != null)
- {
- if (mButtonState == EnumExButtonState.None)
- {
- mButtonState = EnumExButtonState.PointerDown;
- mPointerDownTime = Time.time;
- }
- else if (mButtonState == EnumExButtonState.PointerUp)
- {
- if (Time.time - mPointerDownTime < mDoubleClickInterval)
- {
- mButtonState = EnumExButtonState.DoubleClick;
- return;
- }
- else
- {
- mButtonState = EnumExButtonState.PointerDown;
- mPointerDownTime = Time.time;
- }
- }
- }
-
- if (OnPressBegin != null || OnPress != null || OnPressEnd != null)
- {
- if (mButtonState != EnumExButtonState.DoubleClick)
- {
- mButtonState = EnumExButtonState.PointerDown;
- mPointerDownTime = Time.time;
- }
- }
-
- if (OnClick != null)
- {
- mButtonState = EnumExButtonState.PointerDown;
- }
- }
-
- public override void OnPointerUp(PointerEventData eventData)
- {
- base.OnPointerUp(eventData);
-
- if (OnDoubleClick != null)
- {
- if (mButtonState == EnumExButtonState.PointerDown)
- {
- mButtonState = EnumExButtonState.PointerUp;
- return;
- }
- else if (mButtonState == EnumExButtonState.DoubleClick)
- {
- return;
- }
- }
-
- if (OnPressBegin != null || OnPress != null || OnPressEnd != null)
- {
- if (mButtonState == EnumExButtonState.Press)
- {
- mButtonState = EnumExButtonState.PressEnd;
- return;
- }
- }
-
- if (OnClick != null)
- {
- if (mButtonState == EnumExButtonState.PointerDown)
- mButtonState = EnumExButtonState.PointerUp;
- }
- }
-
- private void Update()
- {
- ProcessUpdate();
- ResponseButtonState();
- }
-
- private void ProcessUpdate()
- {
- if (OnDoubleClick != null) { }
-
- if (OnPressBegin != null || OnPress != null || OnPressEnd != null)
- {
- if (mButtonState == EnumExButtonState.PointerDown)
- {
- if (Time.time - mPointerDownTime > mPressBeginTime)
- {
- mButtonState = EnumExButtonState.PressBegin;
- mPressCacheTime = 0f;
- return;
- }
- }
- }
-
- if (OnClick != null)
- {
- if (mButtonState == EnumExButtonState.PointerUp)
- {
- if (OnDoubleClick != null)
- {
- if (Time.time - mPointerDownTime > mDoubleClickInterval)
- mButtonState = EnumExButtonState.Click;
- }
- else
- {
- mButtonState = EnumExButtonState.Click;
- }
- }
- }
- }
-
- private void ResponseButtonState()
- {
- switch (mButtonState)
- {
- case EnumExButtonState.None:
- break;
- case EnumExButtonState.Click:
- OnClick?.Invoke();
- mButtonState = EnumExButtonState.None;
- break;
- case EnumExButtonState.DoubleClick:
- OnDoubleClick?.Invoke();
- mButtonState = EnumExButtonState.None;
- break;
- case EnumExButtonState.PressBegin:
- OnPressBegin?.Invoke();
- mButtonState = EnumExButtonState.Press;
- break;
- case EnumExButtonState.Press:
- {
- mPressCacheTime += Time.deltaTime;
- if (mPressCacheTime >= mPressIntervalTime)
- {
- mPressCacheTime = mPressCacheTime - mPressIntervalTime;
- OnPress?.Invoke();
- }
- break;
- }
- case EnumExButtonState.PressEnd:
- OnPressEnd?.Invoke();
- mButtonState = EnumExButtonState.None;
- break;
- default:
- break;
- }
- }
- }
ExButton组件功能的拓展需要继承自Button类,并且重写OnPointerDown、OnPointerUp方法(这里根据需求只重写了OnPointerDown、OnPointerUp方法,大家可以根据自己的需求重写Selectable下的方法)。根据需求我们要实现点击、双击、长按、长按开始、长按结束事件的回调,所以在代码中我们提供了OnClick、OnDoubleClick、OnPressBegin、OnPress、OnPressEnd回调方法。
- public Action OnClick { get; set; }
- public Action OnDoubleClick { get; set; }
- public Action OnPressBegin { get; set; }
- public Action OnPress { get; set; }
- public Action OnPressEnd { get; set; }
逻辑采用了单状态机来实现,在OnPointerDown、OnPointerUp、Update方法中去改变成员mButtonState的状态,最终在ResponseButtonState方法中根据mButtonState的状态去进行事件的回调。
在逻辑中也进行了事件回调的优化处理,当OnClick、OnDoubleClick、OnPressBegin、OnPress、OnPressEnd所有回调都被注册时,会优先处理OnDoubleClick,其次是OnPressBegin、OnPress、OnPressEnd,最后才是OnClick。例如当OnDoubleClick未被注册时,则会跳过OnDoubleClick对应的逻辑判断,提前处理并响应其他回调事件。
- public override void OnPointerDown(PointerEventData eventData)
- {
- base.OnPointerDown(eventData);
- }
-
- public override void OnPointerUp(PointerEventData eventData)
- {
- base.OnPointerUp(eventData);
- }
在重写OnPointerDown、OnPointerUp后如需保留原有功能,需要调用base.OnPointerDown、base.OnPointerUp方法。只有调用基类方法,按钮的按下颜色改变才会有效果,当然这些基类方法也可以在别处调用。
Selectable文档连接:https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/script-Selectable.html
Selectable类API文档连接:https://docs.unity3d.com/Packages/com.unity.ugui@2.0/api/UnityEngine.UI.Selectable.html?q=selectable
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。