原文出自:https://www.cnblogs.com/SouthBegonia/p/16093683.html
添加了右键支持,但是只有单击,注意不要跟项目中其他鼠标右键的功能冲突,鼠标中键也需要的话自己改造一下就行了
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
#if UNITY_EDITOR
using UnityEditor;
#endif
/*
* ButtonEx扩展按钮组件
*
* 【功能】
* - 多种按钮交互事件
* - Button无损转ButtonEx(Button右上角三个点设置里调用)
*/
/// <summary>
/// 扩展按钮组件
/// </summary>
[AddComponentMenu("UI/ButtonEx")]
public class ButtonEx : Button
{
/// <summary>
/// 按钮交互事件枚举
/// </summary>
[System.Flags]
public enum EventType
{
Click = 1 << 0,
LongClick = 1 << 1,
Down = 1 << 2,
Up = 1 << 3,
Enter = 1 << 4,
Exit = 1 << 5,
DoubleClick = 1 << 6,
}
[SerializeField] private EventType m_EventType = EventType.Click;
/// <summary>
/// 长按判定时间
/// </summary>
public float onLongWaitTime = 0.2f;
/// <summary>
/// 是否重复抛出长按事件(false:长按onLongWaitTime后只触发一次onLongClick true:从onDown起,每onLongWaitTime触发一次onLongClick)
/// </summary>
public bool onLongContinue = false;
/// <summary>
/// 双击判定时间(两次OnDown的间隔时间小于此值即判定为一次双击,但完全不影响onClick的触发)
/// </summary>
public float onDoubleClickTime = 0.5f;
/*[SerializeField]
private ButtonClickedEvent m_OnClick = new ButtonClickedEvent(); //点击事件*/
[SerializeField] private ButtonClickedEvent m_OnEnterLongClick = new ButtonClickedEvent(); //长按事件(触发一次)
[SerializeField] private ButtonClickedEvent m_OnLongClick = new ButtonClickedEvent(); //长按事件(触发一次)
[SerializeField] private ButtonClickedEvent m_OnDown = new ButtonClickedEvent(); //按下事件
[SerializeField] private ButtonClickedEvent m_OnUp = new ButtonClickedEvent(); //抬起事件
[SerializeField] private ButtonClickedEvent m_OnEnter = new ButtonClickedEvent(); //进入事件
[SerializeField] private ButtonClickedEvent m_OnExit = new ButtonClickedEvent(); //移出事件
[SerializeField] private ButtonClickedEvent m_onDoubleClick = new ButtonClickedEvent(); //双击事件
[SerializeField] private ButtonClickedEvent m_onRightClick = new ButtonClickedEvent(); //右键点击事件
[SerializeField] private ButtonClickedEvent m_OnClickEx = new ButtonClickedEvent(); //左键点击事件
private Coroutine log;
private bool isPointerDown = false;
private bool isPointerInside = false;
private bool isEnterButton;
#region 对外属性
/// <summary>
/// 是否被按下
/// </summary>
public bool isDown
{
get { return isPointerDown; }
}
/// <summary>
/// 是否进入
/// </summary>
public bool isEnter
{
get { return isPointerInside; }
}
/// <summary>
/// 点击事件
/// </summary>
public ButtonClickedEvent onClickEx
{
get { return this.m_OnClickEx; }
set { this.m_OnClickEx = value; }
}
public ButtonClickedEvent onLongEnterClick
{
get
{
return this.m_OnEnterLongClick;
}
set
{
this.m_OnEnterLongClick = value;
}
}
/// <summary>
/// 长按事件
/// </summary>
public ButtonClickedEvent onLongClick
{
get { return m_OnLongClick; }
set { m_OnLongClick = value; }
}
/// <summary>
/// 双击事件
/// </summary>
public ButtonClickedEvent onDoubleClick
{
get { return m_onDoubleClick; }
set { m_onDoubleClick = value; }
}
/// <summary>
/// 右键点击事件
/// </summary>
public ButtonClickedEvent onRightClick
{
get { return m_onRightClick; }
set { m_onRightClick = value; }
}
/// <summary>
/// 按下事件
/// </summary>
public ButtonClickedEvent onDown
{
get { return m_OnDown; }
set { m_OnDown = value; }
}
/// <summary>
/// 松开事件
/// </summary>
public ButtonClickedEvent onUp
{
get { return m_OnUp; }
set { m_OnUp = value; }
}
/// <summary>
/// 进入事件
/// </summary>
public ButtonClickedEvent onEnter
{
get { return m_OnEnter; }
set { m_OnEnter = value; }
}
/// <summary>
/// 离开事件
/// </summary>
public ButtonClickedEvent onExit
{
get { return m_OnExit; }
set { m_OnExit = value; }
}
#endregion
private float lastClickTime;
protected override void Awake()
{
base.Awake();
this.onClickEx.AddListener(() => {DoStateTransition(SelectionState.Highlighted, false); });
}
private void Down()
{
if (!IsActive() || !IsInteractable())
return;
m_OnDown.Invoke();
if (lastClickTime != 0 && Time.time - lastClickTime <= onDoubleClickTime)
{
DoubleClick();
}
lastClickTime = Time.time;
log = StartCoroutine(grow());
}
private void Up()
{
if (!IsActive() || !IsInteractable() || !isDown)
return;
m_OnUp.Invoke();
if (log != null)
{
StopCoroutine(log);
log = null;
}
}
private void Enter()
{
if (!IsActive())
return;
m_OnEnter.Invoke();
isEnterButton = true;
StartCoroutine(LongEnterButton());
}
private float enterTime = 0f;
private IEnumerator LongEnterButton()
{
enterTime = Time.time;
while (isEnterButton)
{
if (Time.time - enterTime > onLongWaitTime)
{
m_OnEnterLongClick?.Invoke();
isEnterButton = false;
if (onLongContinue)
enterTime = Time.time;
else
break;
}
else
yield return null;
}
}
private void Exit()
{
if (!IsActive() || !isEnter)
return;
m_OnExit.Invoke();
this.isEnterButton = false;
}
private void LongClick()
{
if (!IsActive() || !isDown)
return;
m_OnLongClick.Invoke();
}
private void LongEnterClick()
{
if (!IsActive() || !this.isEnterButton)
return;
m_OnLongClick.Invoke();
}
private void DoubleClick()
{
if (!IsActive() || !isDown)
return;
m_onDoubleClick.Invoke();
}
private float downTime = 0f;
private IEnumerator grow()
{
downTime = Time.time;
while (isDown)
{
if (Time.time - downTime > onLongWaitTime)
{
LongClick();
if (onLongContinue)
downTime = Time.time;
else
break;
}
else
yield return null;
}
}
protected override void OnDisable()
{
isPointerDown = false;
isPointerInside = false;
base.OnDisable();
}
public override void OnPointerClick(PointerEventData eventData)
{
//中键点击直接返回
if (eventData.button == PointerEventData.InputButton.Middle)
{
return;
}
if (!IsActive() || !IsInteractable())
{
return;
}
UISystemProfilerApi.AddMarker("Button.onClick", this);
if (eventData.button == PointerEventData.InputButton.Left)
{
this.onClickEx.Invoke();
}
else
{
this.m_onRightClick.Invoke();
}
}
public override void OnPointerDown(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
return;
isPointerDown = true;
Down();
base.OnPointerDown(eventData);
}
public override void OnPointerUp(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
return;
Up();
isPointerDown = false;
base.OnPointerUp(eventData);
}
public override void OnPointerEnter(PointerEventData eventData)
{
base.OnPointerEnter(eventData);
isPointerInside = true;
Enter();
if (interactable)
{
DoStateTransition(SelectionState.Highlighted, true);
}
}
public override void OnPointerExit(PointerEventData eventData)
{
Exit();
isPointerInside = false;
base.OnPointerExit(eventData);
if (interactable)
{
DoStateTransition(SelectionState.Normal, true);
}
}
#region Button->ButtonEx转换相关
#if UNITY_EDITOR
[MenuItem("CONTEXT/Button/Convert To ButtonEx", true)]
static bool _ConvertToButtonEx(MenuCommand command)
{
return CanConvertTo<ButtonEx>(command.context);
}
[MenuItem("CONTEXT/Button/Convert To ButtonEx", false)]
static void ConvertToButtonEx(MenuCommand command)
{
ConvertTo<ButtonEx>(command.context);
}
[MenuItem("CONTEXT/Button/Convert To Button", true)]
static bool _ConvertToButton(MenuCommand command)
{
return CanConvertTo<Button>(command.context);
}
[MenuItem("CONTEXT/Button/Convert To Button", false)]
static void ConvertToButton(MenuCommand command)
{
ConvertTo<Button>(command.context);
}
protected static bool CanConvertTo<T>(Object context)
where T : MonoBehaviour
{
return context && context.GetType() != typeof(T);
}
protected static void ConvertTo<T>(Object context) where T : MonoBehaviour
{
var target = context as MonoBehaviour;
var so = new SerializedObject(target);
so.Update();
bool oldEnable = target.enabled;
target.enabled = false;
// Find MonoScript of the specified component.
foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
{
if (script.GetClass() != typeof(T))
continue;
// Set 'm_Script' to convert.
so.FindProperty("m_Script").objectReferenceValue = script;
so.ApplyModifiedProperties();
break;
}
(so.targetObject as MonoBehaviour).enabled = oldEnable;
}
#endif
#endregion
}
using System;
using UnityEngine;
using UnityEditor;
using UnityEditor.UI;
using System.Collections.Generic;
using EventType = ButtonEx.EventType;
[CustomEditor(typeof (ButtonEx), true)]
public class ButtonExEditor: SelectableEditor
{
/// <summary>
/// 按钮交互事件绘制方法的字典
/// </summary>
Dictionary<EventType, Action> callbackDrawersDic;
private SerializedProperty spType;
/// <summary>
/// 按钮交互事件属性的字典
/// </summary>
Dictionary<EventType, SerializedProperty> eventPropertiesDic;
private SerializedProperty sponLongWaitTime;
private SerializedProperty sponLongContinue;
private SerializedProperty sponDoubleClickTime;
protected override void OnEnable()
{
base.OnEnable();
//初始化各属性
InitProperties();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
GUILayout.Space(15);
// 绘制按钮相关参数
sponLongWaitTime.floatValue = EditorGUILayout.FloatField("长按判定时间", sponLongWaitTime.floatValue);
sponLongContinue.boolValue = EditorGUILayout.Toggle("长按期间是否持续触发", sponLongContinue.boolValue);
sponDoubleClickTime.floatValue = EditorGUILayout.FloatField("双击间隔判定时间", sponDoubleClickTime.floatValue);
// 绘制按钮交互事件栏
GUILayout.Space(10);
int oldType = spType.intValue;
spType.intValue = (int)((EventType)EditorGUILayout.EnumFlagsField(new GUIContent("按钮交互事件"), (EventType)spType.intValue));
foreach (EventType e in Enum.GetValues(typeof (EventType)))
{
if (0 < (spType.intValue & (int)e))
callbackDrawersDic[e]();
else if (0 < (oldType & (int)e))
eventPropertiesDic[e].FindPropertyRelative("m_PersistentCalls").FindPropertyRelative("m_Calls").ClearArray();
}
serializedObject.ApplyModifiedProperties();
}
private void InitProperties()
{
eventPropertiesDic = new Dictionary<EventType, SerializedProperty>()
{
{ EventType.Click, serializedObject.FindProperty("m_OnClickEx") },
{ EventType.LongClick, serializedObject.FindProperty("m_OnLongClick") },
{ EventType.Down, serializedObject.FindProperty("m_OnDown") },
{ EventType.Up, serializedObject.FindProperty("m_OnUp") },
{ EventType.Enter, serializedObject.FindProperty("m_OnEnter") },
{ EventType.Exit, serializedObject.FindProperty("m_OnExit") },
{ EventType.DoubleClick, serializedObject.FindProperty("m_onDoubleClick") },
};
callbackDrawersDic = new Dictionary<EventType, Action>();
foreach (EventType eventType in Enum.GetValues(typeof (EventType)))
{
callbackDrawersDic.Add(eventType, () =>
{
EditorGUILayout.LabelField(eventType.ToString(), EditorStyles.boldLabel);
EditorGUILayout.PropertyField(eventPropertiesDic[eventType]);
//可以根据EventType再特殊定制其他属性的显示
});
}
spType = serializedObject.FindProperty("m_EventType");
sponLongWaitTime = serializedObject.FindProperty("onLongWaitTime");
sponLongContinue = serializedObject.FindProperty("onLongContinue");
sponDoubleClickTime = serializedObject.FindProperty("onDoubleClickTime");
}
}
另外,如果需要图片切换的按钮的话,可以参考这个链接,上面已经整合:Unity学习笔记 关于Ugui Button按钮的选择状态详解以及可替代方案