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
|
||
}
|
||
}
|