Unity扩展UGUI组件多态按钮MultimodeButton

这篇博客介绍了如何基于Unity的UGUI系统,扩展一个名为MultimodeButton的组件,该组件在每次点击时会切换不同的按钮贴图并触发相应事件。作者通过反编译Unity的Button源码并进行修改,实现了按钮状态切换的功能,同时提供了详细的代码实现和项目实际使用的效果展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Unity扩展UGUI组件多态按钮MultimodeButton

需求描写

多态按钮,即按钮每触发一次就会切换一张按钮贴图,并触发一个切换事件。
这个逻辑并不复杂,有很多方式可以实现。这里记录一下,依托UGUI框架所实现的方式,使用时如同普通Button一样,会比较顺手。

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组件的基础上做些修改来达到效果。当然并不会修改源码,只是将反编译的代码复制一份到自己创建的脚本中,在根据自己的需求修改。

Multimode 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
    {
        /// <summary> 所有状态图片 </summary>
        public Sprite[] m_stateSprites = new Sprite[2];

        /// <summary> 当前状态索引 </summary>
        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;
        }

        /// <summary> 重置为初始状态 </summary>
        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()方法对比,就是多了个贴图循环切换的逻辑。

项目实际使用效果图

XX
1
2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天富儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值