前言
Unity3D自带的UGUI中,默认有很多功能,能够帮助我们快速实现游戏原型。但有些细节需要我们自己继承并定义新的功能。
需求
实现类似于下面的拖动物品栏,要求点住按钮的时候缩小按钮,松开不移动的时候还原大小并监听回调,松开或者拖动的时候还原大小但不监听回调。
实现
1,拖动栏:拖动——遮罩——布局
2,第一步比较容易实现,但对于缩放,我原本是用 EventTrigger来实现,结果发现事件监听优先级高于按钮,也就是说我再监听到按钮点击的时候,就监听不到拖动的回调。
于是我准备使用一种比较讨巧的办法,绕过这个限制,就是手动去更改ScrollRect中content的RectTransform,大致代码如下:
using UnityEngine.EventSystems;
private ScrollRect mainScroll;
private Transform mainContent;
#region 拖动相关
private bool isDraw = false;//是否处于按钮拖动状态
private float moveBasic;//移动基础数值
private float moveDis;//移动距离
private float damping;//衰减
private bool isMoveUp = false;//移动方向
private float moveDrawStart;//拖动初始
/// <summary>
/// 触摸拖动
/// </summary>
private void DrawEventTrigger(Transform _draw, Action _action)
{
var _eT = _draw.GetComponent<UIDrawEventTrigger>();
if (_eT == null) _eT = _draw.gameObject.AddComponent<UIDrawEventTrigger>();
EventTrigger.Entry entry0 = new EventTrigger.Entry();
entry0.eventID = EventTriggerType.PointerEnter;
entry0.callback.AddListener(delegate {
//Debug.LogError("拖动开始~~~~~~~~~~~~~~~~");
isDraw = false;
moveDis = damping = 0;
mainScroll.decelerationRate = 0;
});
//点击缩小
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerDown;
entry.callback.AddListener(delegate { if (!isDraw) UIDotween.Inst.ScaleTween(_draw.GetComponent<RectTransform>(), 0.95f, 0.2f); });
EventTrigger.Entry entry2 = new EventTrigger.Entry();
entry2.eventID = EventTriggerType.PointerUp;
entry2.callback.AddListener(
delegate
{
UIDotween.Inst.ScaleTween(_draw.GetComponent<RectTransform>(), 1, 0.2f);
if (!isDraw) _action();
else isDraw = false;
isMoveUp = moveDis > 0;
moveDis = Mathf.Abs(moveDis);
mainScroll.decelerationRate = moveDrawStart;
//Debug.LogError("拖动结束" + moveDis * moveDrawStart + " " + isMoveUp);
});
//在此记录拖动的系数,方便记录余下的拖动
EventTrigger.Entry entry3 = new EventTrigger.Entry();
entry3.eventID = EventTriggerType.Drag;
entry3.callback.AddListener(
delegate
{
isDraw = true;
_draw.GetComponent<RectTransform>().localScale = Vector2.one;
Vector2 _ve = mainContent.localPosition;
_ve.y += _eT.f;
if (_ve.y < 0) _ve.y = 0;
mainContent.localPosition = _ve;
moveDis = _ve.y - moveBasic;
moveBasic = _ve.y;
});
_eT.triggers.Add(entry0);
_eT.triggers.Add(entry);
_eT.triggers.Add(entry2);
_eT.triggers.Add(entry3);
}
/// <summary>
/// 拖动更新
/// </summary>
private void OnDrawUpdate()
{
if (!isDraw && moveDis > 0)
{
Vector2 _ve = mainContent.localPosition;
_ve.y += moveDis * moveDrawStart * (float.Parse)(GetComponent<Text>("InputA/Text").text) * (isMoveUp ? 1 : -1);
moveDis -= (float.Parse)(GetComponent<Text>("InputB/Text").text) + damping;
damping += (float.Parse)(GetComponent<Text>("InputC/Text").text);
var _y = GiftBag.GetComponent<RectTransform>().sizeDelta.y + 105;
if (!ShopProp.Inst.isBuyPackNo) _y = 0;
if (_ve.y <= 0 || _ve.y >= _y + 1085) moveDis = 0;
_ve.y = Mathf.Clamp(_ve.y, 0, _y + 1085);
mainContent.localPosition = _ve;
}
}
#endregion
功能倒是实现了,就是这个拖动的感觉和数值一直调试不好。索性换一种方案,从按钮本身入手。
3,写一个新类继承Button,改写其中的按下与抬起状态,具体代码如下:
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// 拖动按钮
/// </summary>
public class UIDrawBtu : Button
{
protected UIDrawBtu()
{
m_callBack = new ButtonClickedEvent();
}
protected ButtonClickedEvent m_callBack;
public ButtonClickedEvent OnClickPress
{
get { return m_callBack; }
set { m_callBack = value; }
}
protected float m_pressLimit = 0.01f;
protected bool isPress = false; // 标识是否处于按下状态
protected Vector3 clickPos;
protected bool isCallBack;
private float scale = 1;
private float speed = 0.05f;
private bool isMin = false;
//按住时,协成循环判断是否按住并调用一次
protected virtual IEnumerator Press()
{
yield return new WaitForSeconds(m_pressLimit);
if (gameObject.activeSelf)
{
while (isPress && interactable && gameObject.activeSelf)
{
//进行Update判断
if (Vector3.Distance(clickPos, Input.mousePosition) > 3)
{
Debug.LogError(clickPos + " " + Input.mousePosition);
isPress = false;
isCallBack = false;
}
else
{
if (scale > 0.8f && !isMin)
{
scale -= speed;
if (scale <= 0.8f) isMin = true;
transform.localScale = Vector3.one * scale;
}
}
yield return new WaitForSeconds(m_pressLimit);
}
}
}
public override void OnPointerDown(PointerEventData eventData)
{
base.OnPointerDown(eventData);
Debug.LogError("Down");
clickPos = Input.mousePosition;
isCallBack = true;
isPress = true;
scale = 1;
isMin = false;
transform.localScale = Vector3.one;
if (gameObject.activeSelf)
{
StartCoroutine(Press());
//StartCoroutine(CheckClick());
}
}
//此方法一移动就会调用,且down之后才会二次调用
public override void OnPointerUp(PointerEventData eventData)
{
base.OnPointerUp(eventData);
Debug.LogError("Up");
scale = 1;
isMin = false;
isPress = false;
transform.localScale = Vector3.one;
StopAllCoroutines();
if(isCallBack && Vector3.Distance(clickPos, eventData.position) < 3) m_callBack.Invoke();
}
}
其实按钮本身并没有类似于Update的刷新方法,这里使用携程与while(true){}配合的方式,来实现刷新功能。
顺便提一句,如果要使用循环的话,一定要设置出口,不然就会造成卡死。
4,最后去场景中试试效果,首先是直接点击:
测试结果:点击缩小,松开还原。
然后是在拖动栏目中:
测试 结果:点击缩小,不动松开还原回调,移动还原不回调;拖动效果不受限制.
测试工程已上传到我的资源中,有需要的可以自行下载。