Unity UI框架

Unity3D UI框架


前言

对于UI的部分,在没使用框架的UI逻辑中,代码结构混乱,逻辑混乱,各个脚本之间相互引用,耦合性非常之高。在后期维护时,牵一发而动全身这是最致命也是最痛苦的,为了解决这一问题,我们需要规划一个方针即:有结构的组织形式。
列如国家管理各个省,省管市,市管区等等。。这样不用什么事都亲力亲为,结构清晰,复杂化变简单化。哪一层出了事直接定位哪一层。

基本思路

  • 以面板(panel)为单位,通过UIManager 进行统一化管理
  • 每个面板只需负责自己对应的子控件
  • 每个控件统一向最高层UIManager进行注册
    pic

UML关系图

pic2

实现

UIManager 类

  • 作用:
    • 负责了对子控件的管理 主要通过字典m_allWidget来实现
    • 是个单例类
    • 并对外提供 注册 注销 获取 等函数
    • 所有注册过的控件都可以在这里拿 实现了管理的统一性
using System.Collections.Generic;
using UnityEngine;

public class UIManager : MonoBehaviour {

    public static UIManager Instance;

    Dictionary<string, Dictionary<string, GameObject>> m_allWidget;

    private void Awake () {

        Instance = this;

        m_allWidget = new Dictionary<string, Dictionary<string, GameObject>> ();

    }

    //注册widget
    public void RegistGameObject (string _panelName, string _widgetName, GameObject _go) {
        if (!m_allWidget.ContainsKey (_panelName))
            m_allWidget[_panelName] = new Dictionary<string, GameObject> ();

        m_allWidget[_panelName].Add (_widgetName, _go);
    }

    //注销widget
    public void UnRegistGameObject (string _panelName, string _widgetName) {
        if (m_allWidget.ContainsKey (_panelName))
            if (m_allWidget[_panelName].ContainsKey (_widgetName))
                m_allWidget[_panelName].Remove (_widgetName);
    }

    //注销panel
    public void UnRegistPanel (string _panelName) {
        if (m_allWidget.ContainsKey (_panelName)) {
            m_allWidget[_panelName].Clear ();
            m_allWidget[_panelName] = null;
        }
    }

    /// <summary>
    /// 获取panel的子控件
    /// </summary>
    /// <param name="_panelName">panel名</param>
    /// <param name="_widgetName">widget名</param>
    /// <returns>返回注册过的游戏控件</returns>
    public GameObject GetGameObject (string _panelName, string _widgetName) {
        if (m_allWidget.ContainsKey (_panelName))
            return m_allWidget[_panelName][_widgetName];

        return null;
    }
}

UIBase.cs 类

  • 作用:
    • 所有UI控件的基类 凡是和UI相关的都要继承此类
    • 以panel为单位 凡是需要用到的控件,需在名字后以“N” 或者“C” 结尾(根据需求而定)- - 最后会演示
    • 封装子控件提供的函数 使UI继承者方便调用
using UnityEngine;
using UnityEngine.Events;

public class UIBase : MonoBehaviour {

    Transform[] m_allChildren;

    private void Awake () {
        AddComponent ();
    }

    private void AddComponent () {
        m_allChildren = transform.GetComponentsInChildren<Transform> ();
		
		//名字以N结尾的子控件添加标记脚本UIBehaviour
        for (int i = 0; i < m_allChildren.Length; i++)
            if (m_allChildren[i].name.EndsWith ("N"))
                m_allChildren[i].gameObject.AddComponent<UIBehaviour> ();
		
		名字以C结尾的子控件添加标记脚本UISubManager
        for (int i = 0; i < m_allChildren.Length; i++)
            if (m_allChildren[i].name.EndsWith ("C"))
                m_allChildren[i].gameObject.AddComponent<UISubManager> ();
    }

    /// <summary>
    /// 获取本panel的子控件
    /// </summary>
    /// <param name="_widgetName">控件名</param>
    /// <returns>返回面板的子控件</returns>
    public GameObject GetWidget (string _widgetName) {
        return UIManager.Instance.GetGameObject (transform.name, _widgetName);
    }

    /// <summary>
    /// 获取其他panel的子控件
    /// </summary>
    /// <param name="_widgetName">控件名</param>
    /// <param name="_panelName">面板名</param>
    /// <returns>返回其他面板的子控件</returns>
    public GameObject GetWidget (string _widgetName, string _panelName) {
        return UIManager.Instance.GetGameObject (_panelName, _widgetName);
    }

    /// <summary>
    /// 获取控件的UIBehaviour
    /// </summary>
    /// <param name="_widgetName">控件名字</param>
    /// <returns>返回UIBehaviour组件</returns>
    public UIBehaviour GetUIBehaviour (string _widgetName) {
        GameObject tempGo = GetWidget (_widgetName);
        if (tempGo != null)
            return tempGo.GetComponent<UIBehaviour> ();

        return null;
    }

    /// <summary>
    /// 获取子控件的UISubManager
    /// </summary>
    /// <param name="_cellName"></param>
    /// <returns></returns>
    public UISubManager GetUISubManager (string _cellName) {
        GameObject tempGo = GetWidget (_cellName);
        if (tempGo != null) return tempGo.GetComponent<UISubManager> ();
        return null;
    }

    //----------------------------------封装UIBehaviour提供的接口 供 UI继承者使用------------------------------------

    public void AddButtonListen (string _widgetName, UnityAction _action) {
        UIBehaviour tmpUIBehaviour = GetUIBehaviour (_widgetName);
        if (tmpUIBehaviour != null)
            tmpUIBehaviour.AddButtonListen (_action);
    }

    public void ChangeImage (string _widgetName, Sprite _sprite) {
        var tmp = GetUIBehaviour (_widgetName);
        if (tmp != null)
            tmp.ChangeImage (_sprite);
    }
    //==========================================================================================================

    //----------------------------------封装UISubManager提供的接口 供 UI继承者使用------------------------------------
    public void AddButtonListen (string _cellName, string _widgetName, UnityAction _action) {
        UISubManager tmpUm = GetUISubManager (_cellName);
        if (tmpUm != null)
            tmpUm.AddButtonListen (_widgetName, _action);
    }
    public void ChangeImage (string _cellName, string _widgetName, Sprite _sprite) {
        var tmp = GetUISubManager (_cellName);
        if (tmp != null)
            tmp.ChangeImage (_widgetName, _sprite);
    }
    //==========================================================================================================

}

UIBehaviour.cs 标记脚本

  • 作用:
    • 挂载了此脚本的子控件向UIManager注册 以达到被管理的目的
    • 为上层(UIBasee) 提供事件
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class UIBehaviour : MonoBehaviour {

    private void Awake () {
		Regist ();
    }
    
	private void Regist() {
		UIBase tmpUIBase = GetComponentInParent<UIBase> ();
        UIManager.Instance.RegistGameObject (tmpUIBase.name, gameObject.name, gameObject);
	}

    //----------------------------------以下自定义添加监听事件 为 UIBase提供接口-------------------------------------

    public void AddButtonListen (UnityAction _action) {
        Button tmpBtn = transform.GetComponent<Button> ();
        if (tmpBtn != null)
            tmpBtn.onClick.AddListener (_action);
    }

    public void AddInputFiledEndEditorListen (UnityAction<string> _action) {
        InputField tmpSlider = transform.GetComponent<InputField> ();
        if (tmpSlider != null)
            tmpSlider.onEndEdit.AddListener (_action);
    }

    public void ChangeImage (Sprite _sprite) {
        var tmpImg = transform.GetComponent<Image> ();
        if (tmpImg != null)
            tmpImg.sprite = _sprite;
    }
    
}

UISubManager.cs

  • 作用:
    • 二级管理者
    • 和标记脚本(UIBehaviour)同级 唯一区别是还管理了子控件
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class UISubManager : MonoBehaviour {

    Dictionary<string, GameObject> m_allChildren;

    private void Awake () {
        Regist ();
        InitializeChildren ();
    }

    //把自己注册到UIManager 其子控件归自己管 实现二级管理者
    void Regist () {
        UIBase tmpUIBase = gameObject.GetComponentInParent<UIBase> ();
        UIManager.Instance.RegistGameObject (tmpUIBase.name, gameObject.name, gameObject);
    }

    //根据标记“S”直接管理子控件 
    void InitializeChildren () {
        m_allChildren = new Dictionary<string, GameObject> ();

        Transform[] tmpChild = gameObject.GetComponentsInChildren<Transform> ();

        for (int i = 0; i < tmpChild.Length; i++) {
            if (tmpChild[i].name.EndsWith ("_S"))
                m_allChildren.Add (tmpChild[i].name, tmpChild[i].gameObject);
        }
    }

    //获取子控件
    public GameObject GetObject (string _widgetName) {
        return m_allChildren[_widgetName];
    }

    //--------------------------以下自定义添加监听事件 为 UIBase提供接口--------------------------
    public void AddButtonListen (string _widgetName, UnityAction _action) {
        //根据名字找子控件
        var tmpGo = GetObject (_widgetName);
        var tmpBtn = tmpGo.GetComponent<Button> ();
        if (tmpBtn != null)
            tmpBtn.onClick.AddListener (_action);

    }

    public void ChangeImage (string _widgetName, Sprite _sprite) {
        var tmp = GetObject (_widgetName).GetComponent<Image> ();
        if (tmp != null)
            tmp.sprite = _sprite;
    }
    //=======================================================================================
	
	//销毁时清空
    private void OnDestroy () {
        if (m_allChildren != null) {
            m_allChildren.Clear ();
            m_allChildren = null;
        }
    }

}

使用演示

测试脚本test.cs

  • 这里必须继承UIBase
using UnityEngine;

public class test : UIBase {

    public Sprite m_sprite;
    
    void Start () {
        //按键和回调函数绑定
        AddButtonListen ("Button_N", Foo);
    }

    void Foo () {
        //               二级管理者名        被改图片名
        ChangeImage ("SecondaryManager_C", "Image_S", m_sprite);
    }

}
  • 结构说明:
    • 一个panel为一个单位,test脚本只需挂载即可,其子控件不需添加任何脚本,需用到时打结尾标记即可
    • N标记为一级子控件(这里一级并不狭隘地对应panel的子控件位置必须在第一层也可是二三四层等等`)只要尾名为N就是 一级子控件
    • C标记为二级管理者(UIManager是一级管理者),可以只是个空物体,但结束名必须为C
    • S标记为二级子控件 (归二级管理者管制)
      在这里插入图片描述
  • 把UIManager.cs挂载到Global
    在这里插入图片描述
    在这里插入图片描述

总结

  • 屏蔽了复杂性,对使用者友好,如test里 只需两句话就搞定
  • 不用考虑控件或面板之间何时调用,只需关心实现的逻辑
  • 对于于框架拓展,只需要在UIBehavior或UISubManager里添加对应需求即可
  • 框架本身耦合性高

最后

学习笔记,仅供参考,如有不正,欢迎指正

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值