【Unity】简易UI框架(带通知弹窗)

主要包含:统一控制Panel全屏界面面板及Popup弹窗的显示隐藏,通知消息弹窗,按钮自动添加点击音效

UIManager:统一管理类,统一控制各个界面的显示及隐藏

界面隐藏时,是将界面SetActive(false)了(没有写刷新函数,如果界面已显示,再次调用显示时,最好刷新一次界面),也可以直接Destroy(gameObject)销毁,下次显示时重新创建

在加载创建UI界面时,会给每个Button和Toggle自动添加点击音效(配合音效管理类使用),这里只用了一个通用音效,如果需要多个不同音效,可以给每个界面加一个标记(比如Base类添加一个枚举值,或者界面命名加一个后缀,用name.EndsWith("sign")区分),根据标记绑定对应音效

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using System.Linq;
using DG.Tweening;

public class UIManager : MonoSingleton<UIManager>
{
    public Camera uiCamera { get; private set; }

    Transform panelNode;
    Transform popupNode;
    Transform barNode;

    void Awake()
    {
        Init();
    }

    void Start()
    {
        uiCamera = MainManager.Instance.GetComponentInChildren<Camera>();
        ShowPanel<UIStartupPanel>(true);
    }

    void Init()
    {
        panelNode = transform.Find("PanelRoot");
        if (panelNode == null)
            panelNode = transform;
        popupNode = transform.Find("PopupRoot");
        if (popupNode == null)
            popupNode = transform;
        barNode = transform.Find("BarNode");
        if (barNode == null)
            barNode = transform;
    }

    #region --- Panel & Popup ---

    Dictionary<Type, UIBase> uiDic = new Dictionary<Type, UIBase>();

    public T LoadUI<T>(params object[] args) where T : UIBase
    {
        Type uiType = typeof(T);

        if (uiDic.ContainsKey(uiType))
        {
            uiDic[uiType].Destroy();
            uiDic.Remove(uiType);
        }

        T uiPrefab = ResourcesLoader.LoadObject<T>(ConstConfig.uiPrefabPath, uiType.Name);
        if (uiPrefab == null)
            return null;

        T ui = Instantiate(uiPrefab, transform);
        if (uiPrefab is UIPanelBase)
        {
            ui.transform.SetParent(panelNode);
        }
        else if (uiPrefab is UIPopupBase)
        {
            ui.transform.SetParent(popupNode);
        }
        else if (uiPrefab is UIBarBase)
        {
            ui.transform.SetParent(barNode);
        }

        uiDic.Add(uiType, ui);

        AddButtonClickSound(ui.transform);

        ui.Init(args);
        return ui;
    }

    public T GetUI<T>() where T : UIBase
    {
        Type uiType = typeof(T);

        if (!uiDic.ContainsKey(uiType))
            LoadUI<T>();

        if (uiDic.ContainsKey(uiType))
            return uiDic[uiType] as T;

        return null;
    }

    public bool ShowPanel<T>(bool hideOther, params object[] args) where T : UIPanelBase
    {
        if (hideOther)
            HideAllPanel();

        //if (showTopBar)
        //    ShowUI<UITopBar>();
        //else
        //    HideUI<UITopBar>();

        return ShowUI<T>(args);
    }
    public bool ShowUI<T>(params object[] args) where T : UIBase
    {
        Type uiType = typeof(T);

        if (!uiDic.ContainsKey(uiType))
            LoadUI<T>();

        if (uiDic.ContainsKey(uiType))
            return ShowUI(uiDic[uiType], args);

        return false;
    }
    public bool ShowUI(UIBase ui, params object[] args)
    {
        if (ui == null)
            return false;
        ui.Show(args);
        return true;
    }
    public bool ShowNotify(UINotifyInfo notifyInfo)
    {
        return ShowUI<UINotifyPopup>(notifyInfo);
    }
    public bool ShowNotify(string notifyInfoStr)
    {
        return ShowUI<UINotifyPopup>(new UINotifyInfo() { infoStr = notifyInfoStr });
    }

    public bool HideUI<T>(float delayTime = 0) where T : UIBase
    {
        Type uiType = typeof(T);

        if (uiDic.ContainsKey(uiType))
            return HideUI(uiDic[uiType], delayTime);

        return false;
    }
    public bool HideUI(UIBase ui, float delayTime)
    {
        if (ui == null)
            return false;

        if (delayTime > 0)
            ui.HideDelay(delayTime);
        else
            ui.Hide();

        return true;
    }

    public void HideAllPanel()
    {
        var panelDic = uiDic.Where(pair => pair.Value is UIPanelBase);
        foreach (var item in panelDic)
        {
            item.Value.Hide();
        }
    }
    public void HideAllPopup()
    {
        var popupDic = uiDic.Where(pair => pair.Value is UIPopupBase);
        foreach (var item in popupDic)
        {
            item.Value.Hide();
        }
    }

    public bool DestroyUI<T>() where T : UIBase
    {
        Type uiType = typeof(T);
        if (uiDic.ContainsKey(uiType))
        {
            UIBase toDestroyUI = uiDic[uiType];
            uiDic.Remove(uiType);
            return DestroyUI(toDestroyUI);
        }
        return false;
    }
    public bool DestroyUI(UIBase ui)
    {
        if (ui == null)
            return false;

        if (ui.isShow)
            ui.Hide();

        ui.Destroy();
        return true;
    }

    public void DestroyAllUI()
    {
        foreach (var item in uiDic.Values)
        {
            item.Destroy();
        }
        uiDic.Clear();
    }

    #endregion

    #region --- AddButtonClickSound ---

    public static void GetAllButtonAddListener(Action btnAction)
    {
        //获取场景所有物体
        Button[] allButtons = Resources.FindObjectsOfTypeAll<Button>();

        for (int i = 0; i < allButtons.Length; i++)
        {
            allButtons[i].onClick.AddListener(() => btnAction());
        }
        Debug.Log("<color=green>当前场景共有Button组件 : </color>" + allButtons.Length);
    }

    public static void AddButtonClickSound(Transform targetObj)
    {
        Button[] buttons = targetObj.GetComponentsInChildren<Button>();

        for (int i = 0; i < buttons.Length; i++)
        {
            int index = i;
            buttons[index].onClick.AddListener(() => {
                AudioConteoller.Instance.PlaySoundOnce(ConstConfig.uiBtnClipName);
                buttons[index].transform.localScale = Vector3.one;
                buttons[index].transform.DOKill();
                buttons[index].transform.DOShakeScale(0.3f, 0.5f);
            });
        }

        Toggle[] toggles = targetObj.GetComponentsInChildren<Toggle>();

        for (int i = 0; i < toggles.Length; i++)
        {
            int index = i;
            toggles[index].onValueChanged.AddListener((b) =>
            {
                if (b)
                {
                    AudioConteoller.Instance.PlaySoundOnce(ConstConfig.uiBtnClipName);
                    toggles[index].transform.localScale = Vector3.one;
                    toggles[index].transform.DOKill();
                    toggles[index].transform.DOShakeScale(0.3f, 0.5f);
                }
            });
        }
    }

    #endregion

}

Base基类:所有UI界面都需要继承该类,便于统一管理

可以在基类中定义通用的显示隐藏动画,及其他UI通用函数

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIBase : MonoBehaviour
{
    public bool isShow { get { return gameObject.activeSelf; } }

    public virtual void Init(params object[] args)
    {
        //gameObject.SetActive(false);
    }

    public virtual void Destroy()
    {
        Destroy(gameObject);
    }

    public virtual void Show(params object[] args)
    {
        if (!gameObject.activeSelf)
            gameObject.SetActive(true);
    }

    public virtual void Hide()
    {
        if (gameObject.activeSelf)
            gameObject.SetActive(false);
    }

    public virtual void HideDelay(float delayTime)
    {
        if (delayTime <= 0)
            Hide();
        else
            Invoke("Hide", delayTime);
    }

}

Panel和Popup类

Panel面板:界面中只能同时显示一个Panel,显示下个Panel时需要隐藏当前Panel

Popup弹窗:界面中可以同时显示多个Popup,在显示多个Popup时可以控制子物体顺序,以此改变显示层级顺序

可以在Panel和Popup类中分别定义内部通用函数及显示隐藏动画

public class UIPanelBase : UIBase
{

}
public class UIPopupBase : UIBase
{

}

 MessageNotifer:通知消息管理器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using System.Linq;

//弹窗消息类
public class MessageInfo
{
    public string infoStr { get; private set; }
    public Color color { get; private set; }

      //持续显示时间
    public float time { get; private set; }

    public MessageInfo(string messageStr, Color messageColor, float showTime)
    {
        infoStr = messageStr;
        color = messageColor;
        time = showTime;
    }
}

public class MessageNotifer : MonoBehaviour
{

    #region --- Show Message Popup ---

    [Header("Message Popup")]
    [SerializeField] UINotifyMessageBar messagePopup;

    public void ShowMessagePopup(string messageInfoStr)
    {
        ShowMessagePopup(new MessageInfo(messageInfoStr, Color.white, 10));
    }
    public void ShowMessagePopup(string messageInfoStr, Color messageInfoColor, float showTime)
    {
        ShowMessagePopup(new MessageInfo(messageInfoStr, messageInfoColor, showTime));
    }
    public void ShowMessagePopup(MessageInfo messageInfo)
    {
        if (messageInfo == null)
            return;

        messagePopup.ShowMessage(messageInfo);
    }

    public void ClearMessagePopup()
    {
        messagePopup.Clear();
    }

    #endregion

    #region --- Show Message Bar ---

    [Header("Message Bar")]
    //最多显示数量
    [SerializeField] int maxShowCount = 5;

    [SerializeField] Transform messageNode;

    //消息弹窗预设
    [SerializeField] UINotifyMessageBar messageBarPrefab;

    //待显示消息列表
    Queue<MessageInfo> messageInfoQueue = new Queue<MessageInfo>();
    //所有可用弹窗列表
    List<UINotifyMessageBar> messageBarPool = new List<UINotifyMessageBar>();

    // -----------

    public void ShowMessageBar(string messageInfoStr)
    {
        ShowMessageBar(new MessageInfo(messageInfoStr, Color.black, 1));
    }
    public void ShowMessageBar(string messageInfoStr, Color messageInfoColor, float showTime)
    {
        ShowMessageBar(new MessageInfo(messageInfoStr, messageInfoColor, showTime));
    }
    public void ShowMessageBar(MessageInfo messageInfo)
    {
        if (messageInfo == null)
            return;

        messageInfoQueue.Enqueue(messageInfo);

        List<UINotifyMessageBar> showMessageBarList = messageBarPool.FindAll(bar => bar.isShow);

        if (showMessageBarList.Count < maxShowCount)
            StartCoroutine(ShowMessageInfoBar());
    }

    IEnumerator ShowMessageInfoBar()
    {
        if (messageInfoQueue.Count == 0)
            yield break;

        MessageInfo toShowInfo = messageInfoQueue.Dequeue();

        UINotifyMessageBar messageBar = GetEmptyMessagePopup();
        if (messageBar == null)
            yield break;

        //设置子物体顺序, 即最新显示的弹窗在最上面
        messageBar.transform.SetSiblingIndex(0);

        yield return StartCoroutine(messageBar.StartShowMessage(toShowInfo));

        messageBar.gameObject.SetActive(false);

        yield return null;

        if (messageInfoQueue.Count > 0)
            StartCoroutine(ShowMessageInfoBar());
    }

    // ------

    UINotifyMessageBar GetEmptyMessagePopup()
    {
        UINotifyMessageBar messageBar = null;
        messageBar = messageBarPool.FirstOrDefault(bar => !bar.isShow);
        if (messageBar == null)
        {
            messageBar = Instantiate(messageBarPrefab, messageNode);
            if (messageBar != null)
                messageBarPool.Add(messageBar);
        }
        if (messageBar != null)
            messageBar.gameObject.SetActive(true);
        return messageBar;
    }

    #endregion

}

UINotifyMessageBar:通知消息弹窗,在屏幕指定区域,展示通知消息,不可点击交互,由MessageNotifer统一管理,不在Panel和Popup范围内

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

public class UINotifyMessageBar : MonoBehaviour
{
    public bool isShow { get; private set; }

    CanvasGroup canvasGroup;
    Text infoText;
    //淡入/淡出时间
    const float fadeTime = 0.5f;

    void Awake()
    {
        canvasGroup = GetComponent<CanvasGroup>();
        infoText = GetComponentInChildren<Text>();

        Clear();
    }

    public void ShowMessage(MessageInfo messageInfo)
    {
        Clear();
        StartCoroutine(StartShowMessage(messageInfo));
    }

    public IEnumerator StartShowMessage(MessageInfo messageInfo)
    {
        isShow = true;

        canvasGroup.alpha = 0;

        infoText.text = messageInfo.infoStr;
        infoText.color = messageInfo.color;

        //开始显示
        canvasGroup.DOFade(1, fadeTime);
        //持续一段时间
        yield return new WaitForSeconds(fadeTime + messageInfo.time);

        //开始隐藏
        canvasGroup.DOFade(0, fadeTime);
        yield return new WaitForSeconds(fadeTime);

        canvasGroup.DOKill();
        isShow = false;
        canvasGroup.alpha = 0;
        infoText.text = "";
    }

    public void Clear()
    {
        StopAllCoroutines();
        canvasGroup.DOKill();

        isShow = false;
        canvasGroup.alpha = 0;
        infoText.text = "";
    }

}

此外,还可以增加可交互通用弹窗,继承Popup基类:

弹窗显示Title文字说明,有确认和取消两个按钮,每次展示时重新绑定确认/取消按钮事件,方便多个场景通用

实现比较简单,就不贴代码了

发布了104 篇原创文章 · 获赞 74 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_39108767/article/details/103405692
今日推荐