Unity3D基础UI框架,Unity MVVM

最新:

参考公司lua的mvvm写了一个c#版的,wang-er-s/Framework: a unity mvvm framework, building... (github.com),欢迎提建议

后续会新开一篇文章补充开发思路

------------------------------弃用---------------------------------

之前学了刘国柱老师的UI框架加上我自己的理解做了一个UI框架正好可以使用

这里附上刘国柱老师的博客 http://www.cnblogs.com/LiuGuozhu/ 受益颇深

基本UI框架的类图就是这样了

大体是根据MVC加上我的魔改而成的框架,至少我自己暂时觉得还是思路挺清晰的

Panel的切换是使用的Canvas Group的更改透明度,在加上使用

此方法来更改显示顺序和显隐的

其中的界面反向切换是使用的栈后入先出的特性

反向切换是指一个窗口弹出小窗口后,父窗口的Raycast需要取消,下面贴上核心代码

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

public class UIManager :MonoBehaviour {

    /// <summary>
    /// 单例模式:
    /// 1.定义一个静态的对象 在外界访问 内部构造
    /// 2.构造方法私有化
    /// </summary>
    #region 单例模式

    private static UIManager _instance;

    public static UIManager Instance
    {
        get
        {
            if (_instance == null)
            {
                UIManager[] instances = FindObjectsOfType<UIManager>();
                if (instances != null)
                {
                    _instance = instances[0];
                    for (var i = 1; i < instances.Length; i++)
                    {
                        Destroy(instances[i].gameObject);
                    }
                }
            }
            return _instance;
        }
    }

    #endregion


    private Transform _canvas;
    public Transform Canvas
    {
        get
        {
            if (_canvas == null)
                _canvas = GameObject.Find("Canvas").transform;
            return _canvas;
        }
    }
    //存储所有面板prefab的地址
    private Dictionary<UIPanelType, string> _DicPanelPath;
    //存储所有实例化的面板身上的BasePanel组件
    private Dictionary<UIPanelType, BasePanel> _DicPanel;
    //存储所有显示的面板上的BasePanel组件
    private Dictionary<UIPanelType, BasePanel> _DicShowPanel;
    //表示显示面板的栈
    private Stack<BasePanel> _StackPanel;
    //全屏显示的节点
    private Transform _TraFullScreen = null;
    //固定显示的节点
    private Transform _TraFixed = null;
    //弹出节点
    private Transform _TraPopUp = null;
    脚本节点
    //private Transform _TraScript = null;


    private void Awake()
    {
        ParseUIPanelPathJson();
        _StackPanel = new Stack<BasePanel>();
        _DicPanel = new Dictionary<UIPanelType, BasePanel>();
        _DicShowPanel = new Dictionary<UIPanelType, BasePanel>();
        _TraFullScreen = Canvas.Find("FullScreen");
        _TraFixed = Canvas.Find("Fixed");
        _TraPopUp = Canvas.Find("PopUp");
        DontDestroyOnLoad(Canvas.gameObject);
        //_TraScript = Canvas.Find("Script");
    }



    /// <summary>
    /// 把类型为反向切换的窗体入栈
    /// </summary>
    /// <param name="type"></param>
    private void PushPanel(UIPanelType type)
    {
        BasePanel basePanel = GetPanel(type);
        if (_StackPanel.Count >= 1)
        {
            BasePanel topPanel = _StackPanel.Peek();
            topPanel.OnPause();
        }
        _StackPanel.Push(basePanel);
        basePanel.OnEnter();
    }

    /// <summary>
    /// 把类型为反向切换的窗体出栈
    /// </summary>
    /// <param name="type"></param>
    private void PopPanel(UIPanelType type)
    {
        //如果栈里面为空,则返回
        if (_StackPanel.Count <= 0) return;
        BasePanel basePanel = GetPanel(type);
        BasePanel topPanel = _StackPanel.Peek();
        //如果当前要出栈的界面跟栈顶界面相同时 才会出栈
        if(basePanel.gameObject == topPanel.gameObject)
        {
            _StackPanel.Pop();
            topPanel.OnExit();
            //如果出栈后栈里面还有界面则恢复
            if(_StackPanel.Count >= 1)
            {
                _StackPanel.Peek().OnResume();
            }
        }
        else
        {
            Debug.LogError("栈顶界面不是此界面,请检查进出栈!!");
        }
        
    }

    /// <summary>
    /// 显示界面
    /// </summary>
    /// <param name="type"></param>
    public void ShowUIForms(UIPanelType type)
    {
        BasePanel basePanel = GetPanel(type);
        switch (basePanel.uiType.uiFormShowMode)
        {
            case UIFormShowMode.Normal:
                EnterUIFormsCache(type);
                break;
            //如果是反切换类型则入栈
            case UIFormShowMode.ReverseChange:
                PushPanel(type);
                break;
            //如果是隐藏其他的类型,清空显示的界面
            case UIFormShowMode.HideOther:
                HideOtherPanel(type);
                break;
        }
    }

    /// <summary>
    /// 隐藏界面 如果显示界面字典里面存在此界面
    /// </summary>
    /// <param name="type"></param>
    public void HideUIForms(UIPanelType type)
    {
        BasePanel basePanel = GetPanel(type);

        switch (basePanel.uiType.uiFormShowMode)
        {
            case UIFormShowMode.Normal:
                HideUIFormsCache(type);
                break;
            //如果是反切换类型则出栈
            case UIFormShowMode.ReverseChange:
                PopPanel(type);
                break;
            case UIFormShowMode.HideOther:
                HideUIFormsCache(type);
                break;
        }
    }

    /// <summary>
    /// 隐藏除此之外的Panel,如果不赋值则清空所有
    /// </summary>
    /// <param name="type"></param>
    public void HideOtherPanel(UIPanelType type = UIPanelType.Null)
    {
        foreach (KeyValuePair<UIPanelType,BasePanel> item in _DicShowPanel)
        {
            item.Value.OnExit();
        }
        _DicShowPanel.Clear();
        if (type != UIPanelType.Null)
        {
            BasePanel basePanel = GetPanel(type);
            basePanel.OnEnter();
            _DicShowPanel.Add(type, GetPanel(type));
        }
            
    }

    /// <summary>
    /// 将UI加入到已显示面板的字典中
    /// </summary>
    /// <param name="type"></param>
    private void EnterUIFormsCache(UIPanelType type)
    {
        //如果显示面板字典里面有当前面板则返回
        if (_DicShowPanel.ContainsKey(type)) return;
        BasePanel basePanel = GetPanel(type);
        _DicShowPanel.Add(type, basePanel);
        basePanel.OnEnter();
    }

    /// <summary>
    /// 将UI隐藏
    /// </summary>
    /// <param name="type"></param>
    private void HideUIFormsCache(UIPanelType type)
    {
        //如果显示面板字典里面没有当前面板则返回
        if (!_DicShowPanel.ContainsKey(type)) return;
        BasePanel basePanel = GetPanel(type);
        _DicShowPanel.Remove(type);
        basePanel.OnExit();
    }

    /// <summary>
    /// 根据面板类型得到实例化的面板
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private BasePanel GetPanel(UIPanelType type)
    {
        if (_DicPanel.ContainsKey(type))
        {
            return _DicPanel[type];
        }
        else
        {
            //如果找不到就找这个面板的prefab的路径,然后根据prefab去实例化面板
            BasePanel panel;
            string panelPath = _DicPanelPath.GetValue(type);
            GameObject insGo = GameObject.Instantiate(Resources.Load(panelPath),Canvas) as GameObject;
            insGo.name = type.ToString();
            panel = insGo.GetComponent<BasePanel>();
            switch (panel.uiType.uIFormParentType)
            {
                case UIFormParentType.FUllScreen:
                    insGo.transform.SetParent(_TraFullScreen);
                    break;
                case UIFormParentType.Fixed:
                    insGo.transform.SetParent(_TraFixed);
                    break;
                case UIFormParentType.PopUp:
                    insGo.transform.SetParent(_TraPopUp);
                    break;
            }
            _DicPanel.Add(type, panel);
            return panel;
        }
    }

    #region 解析地址json 并存储到字典中
    
    private void ParseUIPanelPathJson()
    {
        TextAsset text = Resources.Load<TextAsset>("UIPanelType");
        JSONObject jSONObject = new JSONObject(text.text);
        if (_DicPanelPath == null)
            _DicPanelPath = new Dictionary<UIPanelType, string>();
        foreach (JSONObject item in jSONObject.list)
        {
            string panelTypeString = item["panelTypeString"].str;
            string path = item["path"].str;
            UIPanelType uIPanelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
            _DicPanelPath.Add(uIPanelType, path);
        }
    }

    #endregion

}
using System.Collections;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


public class BasePanel : MonoBehaviour {


    protected CanvasGroup cg;
    public UIType uiType;


    private void Awake()
    {
        InitData();
    }


    public virtual void OnCloseBtnClick()
    {
        UIManager.Instance.HideUIForms(uiType.uiPanelType);
    }


    public virtual void InitData()
    {
        if (cg == null)
            cg = this.gameObject.GetAndAddComponent<CanvasGroup>();
        if (uiType == null)
            uiType = new UIType();
    }




    /// <summary>
    /// 界面被显示出来
    /// </summary>
    public virtual void OnEnter()
    {
        cg.alpha = 1;
        cg.blocksRaycasts = true;
        this.gameObject.transform.SetAsLastSibling();
    }


    /// <summary>
    /// 界面暂停 指的是当其他界面在此界面之上,此界面不是主界面的时候,此界面不接受鼠标检测,如果不需要此功能可以适当更改
    /// </summary>
    public virtual void OnPause()
    {
        cg.blocksRaycasts = false;
    }


    /// <summary>
    /// 界面继续
    /// </summary>
    public virtual void OnResume()
    {
        cg.blocksRaycasts = true;
    }


    /// <summary>
    /// 界面不显示,退出这个界面
    /// </summary>
    public virtual void OnExit()
    {
        cg.alpha = 0;
        cg.blocksRaycasts = false;
    }
	
}

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

public class UIType  {

    /// <summary>
    /// panel类别
    /// </summary>
    public UIPanelType uiPanelType;
    /// <summary>
    /// Panel的显示方式,有正常显示,隐藏其他和反向切换
    /// </summary>
    public UIFormShowMode uiFormShowMode = UIFormShowMode.Normal;
    /// <summary>
    /// 这个透明度暂时没用
    /// </summary>
    public UIFormLucencyType uIFormLucencyType = UIFormLucencyType.Lucency;
    /// <summary>
    /// Panel的父物体类型
    /// </summary>
    public UIFormParentType uIFormParentType = UIFormParentType.PopUp;

代码比较多,不过都有贴心的注释

-------------------------------加了一个代码自动生成的工具-----------------------------------

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
using System;
using UnityEditor;
using System.IO;
using System.Text;
using UnityEngine.UI;

public class UIElement
{
    public string Name;
    public string Path;
    public string ComponentName;
    public UIElement(string name,string path,string componentName)
    {
        Name = name;
        Path = path;
        ComponentName = componentName;
    }

    public override string ToString()
    {
        string str = string.Format("Name={0} || Path={1} || ComponentName={2}", Name, Path, ComponentName);
        return str;
    }
}

public class UICodeGenerator
{
    [MenuItem ( "Assets/CreateCodeDeleteComponent" )]
    public static void CreateCodeDeleteComponent ()
    {
        GetPath ( true );
    }
    
    [MenuItem ( "Assets/OnlyCreateCode" )]
    public static void OnlyCreateCode ()
    {
        GetPath ( false );
    }
    
    public static void GetPath (bool isDeleteComponent)
    {
        var objs =
            Selection.GetFiltered ( typeof ( GameObject ), SelectionMode.Assets | SelectionMode.TopLevel );
        GameObject obj = objs[ 0 ] as GameObject;
        elements = new List<UIElement> ();
        GetPathAs ( obj.transform, isDeleteComponent );

        foreach ( var item in elements )
        {
            Debug.Log ( item );
        }

        GeneratePane ( "Assets/" + obj.name + ".cs", obj.name, elements );
        GenerateCtrl ( "Assets/" + obj.name + "Ctrl.cs", obj.name, elements );

    }

    public static List<UIElement> elements;

    static void GetPathAs ( Transform transform ,bool isDeleteComponent)
    {
        foreach ( Transform child in transform )
        {
            if ( child.gameObject.GetComponent<UIMark> () )
            {
                elements.Add ( new UIElement ( child.name, GetPath ( child ),
                                               child.gameObject.GetComponent<UIMark> ().ComponentName ) );
                if ( isDeleteComponent )
                    GameObject.DestroyImmediate ( child.gameObject.GetComponent<UIMark> (), true );
            }

            if ( child.childCount != 0 )
            {
                GetPathAs ( child, isDeleteComponent );
            }
        }
    }


    public static void GeneratePane ( string generateFilePath, string behaviourName, List<UIElement> elements )
    {
        var sw         = new StreamWriter ( generateFilePath, false, Encoding.UTF8 );
        var strBuilder = new StringBuilder ();

        strBuilder.AppendLine ( "using UnityEngine;" );
        strBuilder.AppendLine ( "using UnityEngine.UI;" );
        strBuilder.AppendLine ();
        strBuilder.AppendFormat ( "public class {0} : BasePanel ", behaviourName );
        strBuilder.AppendLine ();
        strBuilder.AppendLine ( "{" );
        foreach ( var item in elements )
        {
            strBuilder.AppendLine ( "\tpublic " + item.ComponentName + " " + item.Name + " { get; private set; }" );
        }

        strBuilder.AppendLine ();
        strBuilder.AppendLine ( "\tpublic override void InitData()" );
        strBuilder.AppendLine ( "\t{" );
        foreach ( var item in elements )
        {
            strBuilder.AppendFormat ( "\t\t{0} = transform.Find(\"{1}\").GetComponent<{2}>();", item.Name,
                                      item.Path.Replace ( behaviourName + "/", "" ), item.ComponentName );
            strBuilder.AppendLine ();
        }

        strBuilder.AppendLine ();
        strBuilder.AppendLine ( "\t\tuiType.uIFormParentType = UIFormParentType.PopUp;" );
        strBuilder.AppendLine ( "\t\tuiType.uiFormShowMode = UIFormShowMode.Normal;" );
        strBuilder.AppendLine ( "\t\tuiType.uiPanelType = UIPanelType.BoxPanel;" );
        strBuilder.AppendLine ( "\t}" );
        strBuilder.AppendLine ( "}" );
        sw.Write ( strBuilder );
        sw.Flush ();
        sw.Close ();

        AssetDatabase.SaveAssets ();
        AssetDatabase.Refresh ();
    }

    public static void GenerateCtrl ( string generateFilePath, string behaviourName , List<UIElement> elements)
    {
        var sw         = new StreamWriter ( generateFilePath, false, Encoding.UTF8 );
        var strBuilder = new StringBuilder ();

        List<UIElement> temp = new List<UIElement> ();
        
        foreach ( UIElement element in elements )
        {
            if(element.ComponentName.Equals("Button"))
                temp.Add(element);
        }
        
        strBuilder.AppendLine ( "using UnityEngine;" );
        strBuilder.AppendLine ( "using UnityEngine.UI;" );
        strBuilder.AppendLine ();
        strBuilder.AppendFormat ( "public class {0}Ctrl : BaseCtrl ", behaviourName );
        strBuilder.AppendLine ( "{" );
        strBuilder.AppendLine ();
        strBuilder.AppendFormat ( "\tprivate {0} panel;", behaviourName );
        strBuilder.AppendLine ();
        strBuilder.AppendLine ();
        strBuilder.AppendLine ( "\tpublic override void InitPanel()" );
        strBuilder.AppendLine ( "\t{" );
        strBuilder.AppendFormat ( "\t\tpanel = GetComponent<{0}>();", behaviourName );
        strBuilder.AppendLine ();
        foreach ( UIElement element in temp )
        {
            strBuilder.AppendFormat ( "\t\tpanel.{0}.AddListenerGracefully( {1}Click );", element.Name, element.Name );
            strBuilder.AppendLine ();
        }
        strBuilder.AppendLine ( "\t}" );
        strBuilder.AppendLine ();
        foreach ( UIElement element in temp )
        {
            strBuilder.AppendFormat ("\tvoid {0}Click()",element.Name);
            strBuilder.AppendLine ();
            strBuilder.AppendLine ( "\t{" );
            strBuilder.AppendLine ();
            strBuilder.AppendLine ( "\t}" );
            strBuilder.AppendLine ();
        }

        strBuilder.AppendLine ( "}" );
        sw.Write ( strBuilder );
        sw.Flush ();
        sw.Close ();

        AssetDatabase.SaveAssets ();
        AssetDatabase.Refresh ();
    }

    public static string GetPath ( Transform transform )
    {
        var sb = new System.Text.StringBuilder ();
        var t  = transform;
        while ( true )
        {
            sb.Insert ( 0, t.name );
            t = t.parent;
            if ( t )
            {
                sb.Insert ( 0, "/" );
            }
            else
            {
                return sb.ToString ();
            }
        }
    }
}

using UnityEngine.UI;
using UnityEngine;

/// <inheritdoc />
/// <summary>
/// UI的标记
/// </summary>
public class UIMark : MonoBehaviour
{
    //public UIMarkType MarkType = UIMarkType.DefaultUnityElement;

    public Transform Transform
    {
        get { return transform; }
    }

    public string CustomComponentName = "";
    
    public virtual string ComponentName
    {
        get
        {
            if ( !string.IsNullOrEmpty ( CustomComponentName ) )
                return CustomComponentName;
            if (null != GetComponent("SkeletonAnimation"))
                return "SkeletonAnimation";
            if (null != GetComponent<ScrollRect>())
                return "ScrollRect";
            if (null != GetComponent<InputField>())
                return "InputField";
            if (null != GetComponent<Text>())
                return "Text";
            if (null != GetComponent("TMP.TextMeshProUGUI"))
                return "TextMeshProUGUI";
            if (null != GetComponent<Button>())
                return "Button";
            if (null != GetComponent<RawImage>())
                return "RawImage";
            if (null != GetComponent<Toggle>())
                return "Toggle";
            if (null != GetComponent<Slider>())
                return "Slider";
            if (null != GetComponent<Scrollbar>())
                return "Scrollbar";
            if (null != GetComponent<Image>())
                return "Image";
            if (null != GetComponent<ToggleGroup>())
                return "ToggleGroup";
            if (null != GetComponent<Animator>())
                return "Animator";
            if (null != GetComponent<Canvas>())
                return "Canvas";
            if (null != GetComponent("Empty4Raycast"))
                return "Empty4Raycast";
            if (null != GetComponent<RectTransform>())
                return "RectTransform";

            return "Transform";


        }
    }
}

使用方法,把需要使用的组件加上UIMark标记,拖成预设体,右键点击自动生成代码,就会自动生成视图和控制层

-------------------------------弃用-------------------------------------

思想可以借鉴,但是使用起来不是很方便

最新版框架借鉴QFramework,喜欢的可以去看看

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值