多态按钮,即按钮每触发一次就会切换一张按钮贴图,并触发一个切换事件。
这个逻辑并不复杂,有很多方式可以实现。这里记录一下,依托UGUI框架所实现的方式,使用时如同普通Button一样,会比较顺手。
首先,因为我装载了VS的反编译工具,可以直接将UGUI的Button组件反编译出源码:
using System;
using System.Collections;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
namespace UnityEngine.UI
{
[AddComponentMenu("UI/Button", 30)]
public class Button : Selectable, IPointerClickHandler, IEventSystemHandler, ISubmitHandler
{
[Serializable]
public class ButtonClickedEvent : UnityEvent
{
}
[FormerlySerializedAs("onClick")]
[SerializeField]
private ButtonClickedEvent m_OnClick = new ButtonClickedEvent();
public ButtonClickedEvent onClick
{
get
{
return m_OnClick;
}
set
{
m_OnClick = value;
}
}
protected Button()
{
}
private void Press()
{
if (IsActive() && IsInteractable())
{
UISystemProfilerApi.AddMarker("Button.onClick", this);
m_OnClick.Invoke();
}
}
public virtual void OnPointerClick(PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Left)
{
Press();
}
}
public virtual void OnSubmit(BaseEventData eventData)
{
Press();
if (IsActive() && IsInteractable())
{
DoStateTransition(SelectionState.Pressed, instant: false);
StartCoroutine(OnFinishSubmit());
}
}
private IEnumerator OnFinishSubmit()
{
float fadeTime = base.colors.fadeDuration;
float elapsedTime = 0f;
while (elapsedTime < fadeTime)
{
elapsedTime += Time.unscaledDeltaTime;
yield return null;
}
DoStateTransition(base.currentSelectionState, instant: false);
}
}
}
我所要做的就是在原Button组件的基础上做些修改来达到效果。当然并不会修改源码,只是将反编译的代码复制一份到自己创建的脚本中,在根据自己的需求修改。
下面就是经过我修改,达到需求的程序代码:
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace my_code
{
[RequireComponent(typeof(Image))]
public class MultimodeButton : Selectable, IPointerClickHandler, IEventSystemHandler, ISubmitHandler
{
/// 所有状态图片
public Sprite[] m_stateSprites = new Sprite[2];
/// 当前状态索引
public int CurrentIndex { get; private set; }
[Serializable]
public class MultimodeButtonClickedEvent : UnityEvent<int> { }
[FormerlySerializedAs("onClick"), SerializeField]
private MultimodeButtonClickedEvent m_OnClick = new MultimodeButtonClickedEvent();
public MultimodeButtonClickedEvent onClick { get { return m_OnClick; } set { m_OnClick = value; } }
protected MultimodeButton()
{
CurrentIndex = 0;
}
protected override void Awake()
{
transition = Transition.None;
}
/// 重置为初始状态
public void OnReset()
{
CurrentIndex = m_stateSprites.Length;
Press();
}
private void Press()
{
if (IsActive() && IsInteractable())
{
UISystemProfilerApi.AddMarker("Button.onClick", this);
if (++CurrentIndex >= m_stateSprites.Length) CurrentIndex = 0;//确认循环
image.sprite = m_stateSprites[CurrentIndex];//image切换状态图片
m_OnClick.Invoke(CurrentIndex);
}
}
public virtual void OnPointerClick(PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Left)
{
Press();
}
}
public virtual void OnSubmit(BaseEventData eventData)
{
Press();
if (IsActive() && IsInteractable())
{
DoStateTransition(SelectionState.Pressed, instant: false);
StartCoroutine(OnFinishSubmit());
}
}
private IEnumerator OnFinishSubmit()
{
float fadeTime = base.colors.fadeDuration;
float elapsedTime = 0f;
while (elapsedTime < fadeTime)
{
elapsedTime += Time.unscaledDeltaTime;
yield return null;
}
DoStateTransition(base.currentSelectionState, instant: false);
}
}
}
两个代码相差不大,其中的逻辑应该很容易理解,这里只把几个比较重要的核心逻辑解释一下:
RequireComponent(typeof(Image))
:多态按钮因为要修改UI的贴图,所以Image
是必须的;m_stateSprites
:多个状态贴图的存储数组;CurrentIndex
:当前状态在数组中的索引;MultimodeButtonClickedEvent
:按钮所使用的事件对象类,相比一般的Button,其实就是多了一个状态索引的参数,传入的就是CurrentIndex
;transition = Transition.None;
:个人习惯,通过拖拽到物体上使用时,就不用在手动设置了;OnReset()
:一个重置函数,可以通过调用这个函数,将按钮重置回CurrentIndex = 0;
的初始状态;Press()
:与一般Button.Press()
方法对比,就是多了个贴图循环切换的逻辑。