496 lines
14 KiB
C#
496 lines
14 KiB
C#
|
/********************************************************************
|
|||
|
文件: ButtonEx.cs
|
|||
|
作者: 梦语
|
|||
|
邮箱: 1982614048@qq.com
|
|||
|
创建时间: 2024/03/29 17:28:19
|
|||
|
最后修改: 梦语
|
|||
|
最后修改时间: 2024/04/22 20:20:24
|
|||
|
功能: 高级按钮扩展
|
|||
|
*********************************************************************/
|
|||
|
using Sirenix.OdinInspector;
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
#if UNITY_EDITOR
|
|||
|
using UnityEditor;
|
|||
|
#endif
|
|||
|
using UnityEngine;
|
|||
|
using UnityEngine.Events;
|
|||
|
using UnityEngine.EventSystems;
|
|||
|
using UnityEngine.UI;
|
|||
|
using UnityTimer;
|
|||
|
|
|||
|
namespace Ether
|
|||
|
{
|
|||
|
public class ButtonExPointerData : Selectable
|
|||
|
{
|
|||
|
public GameObject PointerItem; //点击的Item
|
|||
|
|
|||
|
public Vector3 lastPosition; //上次的位置
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 是否在拖拽
|
|||
|
/// </summary>
|
|||
|
public bool IsDrag
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
Vector3 curPos = PointerItem.transform.position;
|
|||
|
|
|||
|
Vector3 offset = curPos - lastPosition;
|
|||
|
|
|||
|
if (Mathf.Abs(offset.x) <= 10 && Mathf.Abs(offset.y) <= 10 && Mathf.Abs(offset.z) <= 10)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Serializable]
|
|||
|
public class ButtonEx : SerializedMonoBehaviour,
|
|||
|
IPointerDownHandler, IPointerUpHandler,
|
|||
|
IPointerEnterHandler, IPointerExitHandler
|
|||
|
{
|
|||
|
#region 面板数据
|
|||
|
[Header("音效")]
|
|||
|
[Space]
|
|||
|
|
|||
|
[LabelText("点击音效")]
|
|||
|
[ValueDropdown("@ConstName.AudioNameList")]
|
|||
|
public string clickSound;
|
|||
|
[LabelText("移入音效")]
|
|||
|
[ValueDropdown("@ConstName.AudioNameList")]
|
|||
|
public string enterSound;
|
|||
|
|
|||
|
[Space]
|
|||
|
[Space]
|
|||
|
|
|||
|
[LabelText("是否点击缩放")]
|
|||
|
public bool isScale = true;
|
|||
|
|
|||
|
[LabelText("点击缩放大小"), Range(0, 2)]
|
|||
|
[ShowIf("isScale", true)]
|
|||
|
public float clickScale = 0.95f;
|
|||
|
|
|||
|
[Space]
|
|||
|
[LabelText("是否允许长按")]
|
|||
|
public bool allowLongPress = true;
|
|||
|
|
|||
|
[ShowIf("allowLongPress", true), LabelText("触发长按时间")]
|
|||
|
public float longPressDuration = 1f;
|
|||
|
|
|||
|
[ShowIf("allowLongPress", true), LabelText("是否允许长按循环触发")]
|
|||
|
public bool allowLongPressWhile = true;
|
|||
|
|
|||
|
[Space]
|
|||
|
[LabelText("是否允许双击")]
|
|||
|
public bool allowDoubleClick = false;
|
|||
|
|
|||
|
[ShowIf("allowDoubleClick", true), Tooltip("第二次点击与第一次点击的间隔"), LabelText("点击间隔"), Range(0.2f, 1.5f)]
|
|||
|
public float doubleClickDuration = 0.5f;
|
|||
|
|
|||
|
[Space]
|
|||
|
|
|||
|
[SerializeField, LabelText("使用不规则点击"), SetProperty("UseAnomalousClick")]
|
|||
|
private bool useAnomalousClick = false;
|
|||
|
public bool UseAnomalousClick
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return useAnomalousClick;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
useAnomalousClick = value;
|
|||
|
Debug.Log("UseAnomalousClick");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[LabelText("是否打印Log")]
|
|||
|
public bool isShowLog = false;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 事件
|
|||
|
/// <summary>
|
|||
|
/// 按钮点击事件
|
|||
|
/// </summary>
|
|||
|
private ActionEvent m_OnClick = new ActionEvent();
|
|||
|
public ActionEvent OnClick
|
|||
|
{
|
|||
|
get { return m_OnClick; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 按钮左键点击事件
|
|||
|
/// </summary>
|
|||
|
private ActionEvent m_OnLeftClick = new ActionEvent();
|
|||
|
public ActionEvent OnLeftClick
|
|||
|
{
|
|||
|
get { return m_OnLeftClick; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 按钮右键点击事件
|
|||
|
/// </summary>
|
|||
|
private ActionEvent m_OnRightClick = new ActionEvent();
|
|||
|
public ActionEvent OnRightClick
|
|||
|
{
|
|||
|
get { return m_OnRightClick; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 按钮点击事件
|
|||
|
/// </summary>
|
|||
|
private ActionEvent m_OnDoubleClick = new ActionEvent();
|
|||
|
public ActionEvent OnDoubleClick
|
|||
|
{
|
|||
|
get { return m_OnDoubleClick; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 按钮长按点击事件
|
|||
|
/// </summary>
|
|||
|
private ActionEvent m_OnLongClick = new ActionEvent();
|
|||
|
public ActionEvent OnLongClick
|
|||
|
{
|
|||
|
get { return m_OnLongClick; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 按钮进入事件
|
|||
|
/// </summary>
|
|||
|
private ActionEvent m_OnEnter = new ActionEvent();
|
|||
|
public ActionEvent OnEnter
|
|||
|
{
|
|||
|
get { return m_OnEnter; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 按钮移出事件
|
|||
|
/// </summary>
|
|||
|
private ActionEvent m_OnExit = new ActionEvent();
|
|||
|
public ActionEvent OnExit
|
|||
|
{
|
|||
|
get { return m_OnExit; }
|
|||
|
}
|
|||
|
|
|||
|
public void Awake()
|
|||
|
{
|
|||
|
BaseScale = transform.localScale;
|
|||
|
pointerData = new ButtonExPointerData()
|
|||
|
{
|
|||
|
PointerItem = gameObject,
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
ButtonExPointerData pointerData; //按钮数据
|
|||
|
|
|||
|
public Vector3 BaseScale { get; private set; }
|
|||
|
private Vector3 normalScale = Vector3.zero; //基础缩放
|
|||
|
|
|||
|
private bool isOnClickDown = false; //是否点击按下
|
|||
|
private bool isOnClickTrigger = false; //是否触发点击
|
|||
|
private bool isDoubleClicking = false; //是否正在双击状态
|
|||
|
private bool isLongPressing = false; //是否长按状态
|
|||
|
|
|||
|
private Timer longPressTimer; //长按定时器
|
|||
|
private Timer doubleTimer; //双击定时器
|
|||
|
|
|||
|
|
|||
|
#region 按下
|
|||
|
public void OnPointerDown(PointerEventData eventData)
|
|||
|
{
|
|||
|
normalScale = transform.localScale;
|
|||
|
//if (eventData.button != PointerEventData.InputButton.Left)
|
|||
|
// return;
|
|||
|
pointerData.lastPosition = transform.position;
|
|||
|
OnPointerDownEvent();
|
|||
|
}
|
|||
|
|
|||
|
private void OnPointerDownEvent()
|
|||
|
{
|
|||
|
if (isShowLog)
|
|||
|
{
|
|||
|
Debug.Log("按钮按下");
|
|||
|
}
|
|||
|
isOnClickDown = true;
|
|||
|
isOnClickTrigger = true;
|
|||
|
isLongPressing = false;
|
|||
|
if (isScale)
|
|||
|
{
|
|||
|
transform.localScale = new Vector2(normalScale.x * clickScale, normalScale.y * clickScale);
|
|||
|
}
|
|||
|
|
|||
|
StartLongPress();
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 抬起
|
|||
|
public void OnPointerUp(PointerEventData eventData)
|
|||
|
{
|
|||
|
if (!isActiveAndEnabled)
|
|||
|
return;
|
|||
|
|
|||
|
//if (eventData.button != PointerEventData.InputButton.Left)
|
|||
|
// return;
|
|||
|
if (isOnClickTrigger && !isLongPressing)
|
|||
|
{
|
|||
|
if (isDoubleClicking)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (allowDoubleClick)
|
|||
|
{
|
|||
|
isDoubleClicking = true;
|
|||
|
doubleTimer = Timer.Register(doubleClickDuration, () =>
|
|||
|
{
|
|||
|
isDoubleClicking = false;
|
|||
|
if (isOnClickTrigger)
|
|||
|
{
|
|||
|
OnPointerDoubleClick(eventData);
|
|||
|
OnPointerUpEvent(eventData); //因为上面return掉了,所以第二次的up没有执行,得手动调用
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnPointerClick(eventData);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnPointerClick(eventData);
|
|||
|
}
|
|||
|
}
|
|||
|
OnPointerUpEvent(eventData);
|
|||
|
}
|
|||
|
|
|||
|
private void OnPointerUpEvent(PointerEventData eventData)
|
|||
|
{
|
|||
|
isOnClickDown = false;
|
|||
|
isOnClickTrigger = false;
|
|||
|
isLongPressing = false;
|
|||
|
if (isScale)
|
|||
|
{
|
|||
|
transform.localScale = normalScale;
|
|||
|
}
|
|||
|
|
|||
|
StopLongPress();
|
|||
|
|
|||
|
if (eventData.dragging)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (isShowLog)
|
|||
|
{
|
|||
|
Debug.Log("按钮抬起");
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 点击
|
|||
|
public void OnPointerClick(PointerEventData eventData)
|
|||
|
{
|
|||
|
if (eventData.dragging)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (isShowLog)
|
|||
|
{
|
|||
|
Debug.Log($"{transform.name}按钮点击");
|
|||
|
}
|
|||
|
|
|||
|
if (m_OnClick.Count == 0)
|
|||
|
{
|
|||
|
if (eventData.button == PointerEventData.InputButton.Left)
|
|||
|
{
|
|||
|
m_OnLeftClick.Invoke();
|
|||
|
}
|
|||
|
else if (eventData.button == PointerEventData.InputButton.Right)
|
|||
|
{
|
|||
|
m_OnRightClick.Invoke();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_OnClick.Invoke();
|
|||
|
}
|
|||
|
|
|||
|
if (!string.IsNullOrEmpty(clickSound))
|
|||
|
{
|
|||
|
AudioManager.Inst.PlayEffectAudio(clickSound);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void OnPointerDoubleClick(PointerEventData eventData)
|
|||
|
{
|
|||
|
if (eventData.dragging)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (isShowLog)
|
|||
|
{
|
|||
|
Debug.Log($"{transform.name}按钮双击");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
m_OnDoubleClick.Invoke();
|
|||
|
|
|||
|
if (!string.IsNullOrEmpty(clickSound))
|
|||
|
{
|
|||
|
AudioManager.Inst.PlayEffectAudio(clickSound);
|
|||
|
}
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 移入
|
|||
|
public void OnPointerEnter(PointerEventData eventData)
|
|||
|
{
|
|||
|
//Debug.Log("移入按钮");
|
|||
|
m_OnEnter?.Invoke();
|
|||
|
if (!string.IsNullOrEmpty(enterSound))
|
|||
|
{
|
|||
|
AudioManager.Inst.PlayEffectAudio(enterSound);
|
|||
|
}
|
|||
|
|
|||
|
if (isOnClickDown)
|
|||
|
{
|
|||
|
isOnClickTrigger = true;
|
|||
|
|
|||
|
if (isScale)
|
|||
|
{
|
|||
|
transform.localScale = new Vector2(normalScale.x * clickScale, normalScale.y * clickScale);
|
|||
|
}
|
|||
|
|
|||
|
StartLongPress();
|
|||
|
}
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 移出
|
|||
|
public void OnPointerExit(PointerEventData eventData)
|
|||
|
{
|
|||
|
isOnClickTrigger = false;
|
|||
|
isLongPressing = false;
|
|||
|
// if (isOnClickDown && isScale)
|
|||
|
// {
|
|||
|
// transform.localScale = normalScale;
|
|||
|
// }
|
|||
|
|
|||
|
StopLongPress();
|
|||
|
|
|||
|
m_OnExit?.Invoke();
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 长按
|
|||
|
/// <summary>
|
|||
|
/// 开始长按
|
|||
|
/// </summary>
|
|||
|
private void StartLongPress()
|
|||
|
{
|
|||
|
if (allowLongPress)
|
|||
|
{
|
|||
|
longPressTimer = Timer.Register(longPressDuration, () =>
|
|||
|
{
|
|||
|
if (pointerData.IsDrag)
|
|||
|
{
|
|||
|
isOnClickDown = false;
|
|||
|
isOnClickTrigger = false;
|
|||
|
isLongPressing = false;
|
|||
|
StopLongPress();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
isLongPressing = true;
|
|||
|
OnPointerLongClick();
|
|||
|
}, isLooped: allowLongPressWhile);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 长按触发
|
|||
|
/// </summary>
|
|||
|
private void OnPointerLongClick()
|
|||
|
{
|
|||
|
if (isShowLog)
|
|||
|
{
|
|||
|
Debug.Log("按钮长按!");
|
|||
|
}
|
|||
|
m_OnLongClick.Invoke();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 停止长按
|
|||
|
/// </summary>
|
|||
|
private void StopLongPress()
|
|||
|
{
|
|||
|
longPressTimer?.Cancel();
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
[MenuItem("GameObject/UIEx/ButtonEx", priority = 0)]
|
|||
|
private static void CreateButtonEx(MenuCommand menuCmd)
|
|||
|
{
|
|||
|
GameObject selection = Selection.activeGameObject;
|
|||
|
|
|||
|
GameObject gameObject;
|
|||
|
|
|||
|
if (selection != null)
|
|||
|
{
|
|||
|
Debug.Log($"选择物体:{selection.name}");
|
|||
|
|
|||
|
// 获取物体的根 Canvas
|
|||
|
Canvas rootCanvas = selection.GetComponentInParent<Canvas>();
|
|||
|
Transform parentTransform;
|
|||
|
|
|||
|
if (rootCanvas != null)
|
|||
|
{
|
|||
|
parentTransform = selection.transform;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
var canvas = ObjectFactory.CreateGameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
|||
|
var eventSystem = ObjectFactory.CreateGameObject("EventSystem", typeof(UnityEngine.EventSystems.EventSystem), typeof(UnityEngine.EventSystems.StandaloneInputModule));
|
|||
|
|
|||
|
parentTransform = canvas.transform;
|
|||
|
}
|
|||
|
|
|||
|
gameObject = ObjectFactory.CreateGameObject("New ButtonEx", typeof(Image), typeof(ButtonEx));
|
|||
|
gameObject.transform.SetParent(parentTransform, false);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
var canvas = ObjectFactory.CreateGameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
|||
|
var eventSystem = ObjectFactory.CreateGameObject("EventSystem", typeof(UnityEngine.EventSystems.EventSystem), typeof(UnityEngine.EventSystems.StandaloneInputModule));
|
|||
|
|
|||
|
canvas.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
|
|||
|
Transform parentTransform = canvas.transform;
|
|||
|
gameObject = ObjectFactory.CreateGameObject("New ButtonEx", typeof(Image), typeof(ButtonEx));
|
|||
|
gameObject.transform.SetParent(parentTransform, false);
|
|||
|
}
|
|||
|
|
|||
|
gameObject.transform.localPosition = Vector3.zero;
|
|||
|
gameObject.transform.localScale = new Vector3(1, 1, 1);
|
|||
|
|
|||
|
EditorUtility.FocusProjectWindow();
|
|||
|
Selection.activeObject = gameObject;
|
|||
|
EditorGUIUtility.PingObject(Selection.activeObject);
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|