Unity UI框架(一 窗口层级管理)

image.png

image.png

无框架容易出现的问题

多个场景会反复出现相同的UI窗体,多个场景反复加载(复用)
各个UI脚本之间的传值,容易交错,耦合度高(UI之间不相互联系,通过消息传递)
要手工控制窗体中间的层级关系(使用栈结构保持控制当前所有需要显示的UI窗体)
多个UI窗体之间相互叠加,容易出现误操作(对当前窗体遮挡处理)
窗体自动加载管理
语言的国际化

UI框架核心原则

尽量让框架本身完成与具体业务无关的事务性工作,让开发人员只需要专注游戏的业务逻辑

开发最简版
1.窗体自动加载管理
2.缓存UI窗体
3.窗体生命周期管理

UI框架的核心类设计
1.BaseUIForms基础UI窗体
2.UIManager UI窗体管理
3.UIType窗体类型
4.SysDefine 系统定义类

链接:https://pan.baidu.com/s/1-PomcmeBh2Up1S3iHWTHqQ
提取码:41cf
创建一些文件夹把贴图素材导入MyAtlas
把脚本素材分别放到

image.png

然后把Log类先注释掉
定义一个窗口参数类

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     SysDefine.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:   UI框架核心参数
 * 1.系统常量
 * 2.全局性方法
 * 3.系统枚举类型
 * 4.委托定义
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#region 系统枚举类型
/// <summary>
/// UI窗体(位置)类型
/// </summary>
public enum UIFormType
{
    /// <summary>
    /// 普通窗体
    /// </summary>
    Normal,
    /// <summary>
    /// 固定窗体
    /// </summary>
    Fixed,
    /// <summary>
    /// 弹出窗体
    /// </summary>
    PopUp,
}

/// <summary>
/// UI窗体的显示类型
/// </summary>
public enum UIFormShowMode
{
    /// <summary>
    /// 普通
    /// </summary>
    Normal,
    /// <summary>
    /// 反向切换 
    /// </summary>//按照相反方向切换过去 原路弹回来
    ReverseChange,
    /// <summary>
    /// 隐藏其他 
    /// </summary>
    HideOther
}

/// <summary>
/// UI窗体透明度类型
/// </summary>
public enum UIFormLucenyType
{
    /// <summary>
    /// 完全透明,不能穿透
    /// </summary>
    Lucency,
    /// <summary>
    /// 半透明,不能穿透
    /// </summary>
    Translucence,
    /// <summary>
    /// 低透明度,不能穿透
    /// </summary>
    ImPenetrable,
    /// <summary>
    /// 可以穿透
    /// </summary>
    Pentrate
}
#endregion
public class SysDefine
{



}

然后定义一个 UI基类

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     BaseUI.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:  UI窗体父类
 *定义UI窗体的父类
 * 有四个生命周期
 * 1.Display显示状态
 * 2.Hiding隐藏状态
 * 3.ReDisplay再显示状态
 * 4.Freeze冻结状态 就是弹出窗体后面的窗体冻结
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//窗体类型
public class UIType
{
    /// <summary>
    /// 是否清空"栈集合"
    /// </summary>
    public bool isClearStack = false;
    /// <summary>
    /// UI窗体(位置)类型
    /// </summary>
    public UIFormType type = UIFormType.Normal;
    /// <summary>
    ///UI窗体显示类型
    /// </summary>
    public UIFormShowMode mode = UIFormShowMode.Normal;
    /// <summary>
    /// UI窗体透明度类型
    /// </summary>
    public UIFormLucenyType lucenyType = UIFormLucenyType.Lucency;

}

public class BaseUI : MonoBehaviour
{
    UIType currentUIType { get; set; } = new UIType();

    #region 窗体的四种状态
    /// <summary>
    /// 显示状态
    /// </summary>
    public virtual void ActiveTrue()
    {
        gameObject.SetActive(true);
    }
    /// <summary>
    /// 隐藏状态
    /// </summary>
    public virtual void ActiveFalse()
    {

        gameObject.SetActive(false);
    }
    /// <summary>
    /// 重新显示状态
    /// </summary>
    public virtual void ReActiveTrue()
    {
        gameObject.SetActive(true);
    }

    /// <summary>
    /// 冻结状态
    /// </summary>
    public virtual void Freeze()
    {
        gameObject.SetActive(true);
    }
    #endregion

    #region 封装子类常用方法
    /// <summary>
    /// 注册按钮事件
    /// </summary>
    void RigisterBtnOnClick(string btnName,EventTriggerListener.VoidDelegate del)
    {
        Transform btn = UnityHelper.Find(gameObject.transform, btnName);
        EventTriggerListener.Get(btn?.gameObject).onClick = del;
    }

    /// <summary>
    /// 打开UI窗体
    /// </summary>
    /// <param name="UIName"></param>
    void Open(string UIName)
    {
        
    }

    /// <summary>
    /// 关闭UI窗体
    /// </summary>
    void Close()
    {

    }
    #endregion
}

还有一个UI管理类 先列举出字段

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     UIManager.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:   UI管理器
 * 是整个UI框架的核心,用户程序通过本脚本,来实现框架绝大多数功能
 * 原则
 * 低耦合,高聚合
 * 方法单一职责
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIManager : MonoBehaviour {

    /// <summary>
    /// UI窗体预设路径 <窗口名字,路径>
    /// </summary>
    Dictionary<string, string> UIPaths;
    /// <summary>
    /// 缓存所有UI窗体
    /// </summary>
    Dictionary<string, BaseUI> cacheUIs;
    /// <summary>
    /// 当前显示的UI窗体
    /// </summary>
    Dictionary<string, BaseUI> showUIs;
    /// <summary>
    /// UI根节点
    /// </summary>
    Transform traRoot;
    /// <summary>
    /// 全屏幕显示的节点
    /// </summary>
    Transform traNormal;
    /// <summary>
    /// 固定显示的节点
    /// </summary>
    Transform traFixed;
    /// <summary>
    /// 弹出节点
    /// </summary>
    Transform traPopUp;
    /// <summary>
    /// UI脚本管理节点
    /// </summary>
    Transform traUISprites;

    private void Awake()
    {
        
    }
}

然后搭设场景


image.png

几个空节点还有UICamera
Canvans设置 为按相机照射 UIScaleMode调整为宽高比例缩放match一般是0.5或者按高度适配


image.png

相机处理设置为正交模式 照射层级只有UI 还有记得把相机往后拉一点点不然没有图片


image.png
image.png

然后再Nomal下创建这样的界面 记得定好锚点
在Resources保存为预制体 到时候动态加载


image.png

然后记得把UI相机的这个去掉


image.png

然后添加常量名字

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     SysDefine.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:   UI框架核心参数
 * 1.系统常量
 * 2.全局性方法
 * 3.系统枚举类型
 * 4.委托定义
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#region 系统枚举类型
/// <summary>
/// UI窗体(位置)类型
/// </summary>
public enum UIFormType
{
    /// <summary>
    /// 普通窗体
    /// </summary>
    Normal,
    /// <summary>
    /// 固定窗体
    /// </summary>
    Fixed,
    /// <summary>
    /// 弹出窗体
    /// </summary>
    PopUp,
}

/// <summary>
/// UI窗体的显示类型
/// </summary>
public enum UIFormShowMode
{
    /// <summary>
    /// 普通
    /// </summary>
    Normal,
    /// <summary>
    /// 反向切换 
    /// </summary>//按照相反方向切换过去 原路弹回来
    ReverseChange,
    /// <summary>
    /// 隐藏其他 
    /// </summary>
    HideOther
}

/// <summary>
/// UI窗体透明度类型
/// </summary>
public enum UIFormLucenyType
{
    /// <summary>
    /// 完全透明,不能穿透
    /// </summary>
    Lucency,
    /// <summary>
    /// 半透明,不能穿透
    /// </summary>
    Translucence,
    /// <summary>
    /// 低透明度,不能穿透
    /// </summary>
    ImPenetrable,
    /// <summary>
    /// 可以穿透
    /// </summary>
    Pentrate
}
#endregion
public class SysDefine
{
    /// <summary>
    /// 路径常量
    /// </summary>
    public const string canvasPath = "Canvas";
    /// <summary>
    /// 标签常量
    /// </summary>
    public const string canvasTag= "Canvas";
    
    //全局性方法

    //委托的定义
}

在UIManager添加UI创建方法

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     UIManager.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:   UI管理器
 * 是整个UI框架的核心,用户程序通过本脚本,来实现框架绝大多数功能
 * 原则
 * 低耦合,高聚合
 * 方法单一职责
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIManager : MonoBehaviour
{

    /// <summary>
    /// UI窗体预设路径 <窗口名字,路径>
    /// </summary>
    Dictionary<string, string> UIPaths = new Dictionary<string, string>();
    /// <summary>
    /// 缓存所有UI窗体
    /// </summary>
    Dictionary<string, BaseUI> cacheUIs = new Dictionary<string, BaseUI>();
    /// <summary>
    /// 当前显示的UI窗体
    /// </summary>
    Dictionary<string, BaseUI> showUIs = new Dictionary<string, BaseUI>();
    /// <summary>
    /// UI根节点
    /// </summary>
    Transform traRoot;
    /// <summary>
    /// 全屏幕显示的节点
    /// </summary>
    Transform traNormal;
    /// <summary>
    /// 固定显示的节点
    /// </summary>
    Transform traFixed;
    /// <summary>
    /// 弹出节点
    /// </summary>
    Transform traPopUp;
    /// <summary>
    /// UI脚本管理节点
    /// </summary>
    Transform traUISprites;

    /// <summary>
    /// 初始化核心数据,加载"UI窗体路径"到集合中
    /// </summary>
    private void Awake()
    {
        InitRootCanvasLoading();
        //得到UI根节点、全屏节点、固定节点、弹出节点
        traRoot = GameObject.FindGameObjectWithTag(SysDefine.canvasTag).transform;
        traNormal = traRoot.Find("Normal");
        traFixed = traRoot.Find("Fixed");
        traPopUp = traRoot.Find("PopUp");
        traUISprites = traRoot.Find("ScriptsMgr");
        //把本脚本作为"根UI窗体"的子节点 true世界坐标 false局部坐标
        this.gameObject.transform.SetParent(traUISprites, false);
        //"rootUI窗体"在场景转换时候,不允许被销毁
        DontDestroyOnLoad(traRoot);
        //初始化"UI窗体"路径信息
        UIPaths?.Add("LoginUI", "UIPrefabs/LoginUI");
    }
    /// <summary>
    /// 打开/显示UI窗体
    /// </summary>
    public void ShowUI(string UIName)
    {
        BaseUI baseUI;
        if (string.IsNullOrEmpty(UIName))
            return;

        baseUI = LoadUIToCacheUIs(UIName);

        if (baseUI == null)
            return;

        //根据不同的UI窗体的显示模式,分别作不同的加载处理
        switch (baseUI.currentUIType.mode)
        {
            case UIFormShowMode.Normal:
                break;
            case UIFormShowMode.ReverseChange:
                break;
            case UIFormShowMode.HideOther:
                break;
            default:
                break;
        }
    }

    /// <summary>
    /// 初始化加载(rootUI窗体)Canvas预制体
    /// </summary>
    private void InitRootCanvasLoading()
    {
        ResourcesMgr.GetInstance().LoadAsset(SysDefine.canvasPath, false);
    }

    #region 私有方法
    /// <summary>
    /// 加载与判断指定的UI窗体的名字,加载到"所有UI窗体"缓存里
    /// </summary>
    /// <param name="UIName"></param>
    /// <returns></returns>
    BaseUI LoadUIToCacheUIs(string UIName)
    {
        BaseUI baseUI;

        //得到就返回 得不到返回null
        cacheUIs.TryGetValue(UIName, out baseUI);
        if (baseUI == null)
        {
            LoadUI(UIName);
        }
        return baseUI;
    }

    /// <summary>
    /// 根据不同位置信息,加载到root下不同的节点
    /// </summary>
    BaseUI LoadUI(string UIName)
    {
        string uiPath;
        GameObject UIPrefabs = null;
        BaseUI baseUI = null;

        UIPaths.TryGetValue(UIName, out uiPath);
        if (!string.IsNullOrEmpty(uiPath))
        {
            UIPrefabs = ResourcesMgr.GetInstance().LoadAsset(uiPath, false);
        }

        if (traRoot != null && UIPrefabs != null)
        {
            baseUI = UIPrefabs.GetComponent<BaseUI>();
            if (baseUI == null)
            {
                Debug.Log($"baseUI==null,请确认窗口是否有BaseUI脚本,参数:{UIName}");
                return null;
            }

            switch (baseUI.currentUIType.type)
            {
                case UIFormType.Normal:
                    UIPrefabs.transform.SetParent(traNormal, false);
                    break;
                case UIFormType.Fixed:
                    UIPrefabs.transform.SetParent(traFixed, false);
                    break;
                case UIFormType.PopUp:
                    UIPrefabs.transform.SetParent(traPopUp, false);
                    break;
            }

            UIPrefabs.SetActive(false);
            cacheUIs.Add(UIName, baseUI);
            return baseUI;
        }
        else
        {
            Debug.Log($"traRoot!=null Or UIPrefabs!=null Please Check  参数{UIName}");
        }

        Debug.Log($"出现不可预估的错误 参数{UIName}");
        return null;
    }

    /// <summary>
    /// 把当前窗体加载到当前显示窗体集合中
    /// </summary>
    void LoadUIToShowUIs(string UIName)
    {
        BaseUI baseUI;
        BaseUI baseUIFormCache;

        showUIs.TryGetValue(UIName, out baseUI);

        if (baseUI == null)
            return;

        cacheUIs.TryGetValue(UIName, out baseUIFormCache);

        if (baseUIFormCache != null)
        {
            showUIs.Add(UIName, baseUIFormCache);
            baseUIFormCache.ActiveTrue();
        }



    }

    #endregion
}

创建一个


image.png

挂给我们的LoginUI预制体
创一个空节点 挂上脚本


image.png

Text只有这个一句话 然后加载出来
image.png

因为没设置窗口激活所以是隐藏的


image.png

然后UI层级管理的话要用到栈 “先进后出”
Stack<T>


image.png

类似于收盘子洗盘子 羽毛球筒放进去拿出来
还有类似于枪的弹夹

然后UIManager添加方法 有些判断写反了 又改了改

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     UIManager.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:   UI管理器
 * 是整个UI框架的核心,用户程序通过本脚本,来实现框架绝大多数功能
 * 原则
 * 低耦合,高聚合
 * 方法单一职责
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace UIFrameWork
{
    public class UIManager : MonoBehaviour
    {

        public static UIManager instance;
        /// <summary>
        /// UI窗体预设路径 <窗口名字,路径>
        /// </summary>
        Dictionary<string, string> UIPaths = new Dictionary<string, string>();
        /// <summary>
        /// 缓存所有UI窗体
        /// </summary>
        Dictionary<string, BaseUI> cacheUIs = new Dictionary<string, BaseUI>();
        /// <summary>
        /// 当前显示的UI窗体
        /// </summary>
        Dictionary<string, BaseUI> showUIs = new Dictionary<string, BaseUI>();
        /// <summary>
        /// 具备 "反向切换"窗体的管理
        /// </summary>
        Stack<BaseUI> staUIs = new Stack<BaseUI>();
        /// <summary>
        /// UI根节点
        /// </summary>
        Transform traRoot;
        /// <summary>
        /// 全屏幕显示的节点
        /// </summary>
        Transform traNormal;
        /// <summary>
        /// 固定显示的节点
        /// </summary>
        Transform traFixed;
        /// <summary>
        /// 弹出节点
        /// </summary>
        Transform traPopUp;
        /// <summary>
        /// UI脚本管理节点
        /// </summary>
        Transform traUISprites;

        /// <summary>
        /// 初始化核心数据,加载"UI窗体路径"到集合中
        /// </summary>
        private void Awake()
        {
            instance = this;
            InitRootCanvasLoading();
            //得到UI根节点、全屏节点、固定节点、弹出节点
            traRoot = GameObject.FindGameObjectWithTag(SysDefine.canvasTag).transform;
            traNormal = UnityHelper.Find(traRoot, SysDefine.normalNode);
            traFixed = UnityHelper.Find(traRoot, SysDefine.fixedNode);
            traPopUp = UnityHelper.Find(traRoot, SysDefine.popUpNode);
            traUISprites = UnityHelper.Find(traRoot, SysDefine.ScriptsMgrNode);
            //把本脚本作为"根UI窗体"的子节点 true世界坐标 false局部坐标
            this.gameObject.transform.SetParent(traUISprites, false);
            //"rootUI窗体"在场景转换时候,不允许被销毁
            DontDestroyOnLoad(traRoot);
            //初始化"UI窗体"路径信息
            UIPaths?.Add("LoginUI", "UIPrefabs/LoginUI");
            UIPaths?.Add("SelectUI", "UIPrefabs/SelectUI");
        }
        /// <summary>
        /// 打开/显示UI窗体
        /// </summary>
        public void ShowUI(string UIName)
        {
            BaseUI baseUI = null;
            if (string.IsNullOrEmpty(UIName))
                return;


            baseUI = LoadUIToCacheUIs(UIName);

            if (baseUI == null)
                return;

            //是否清空"栈集合"中得数据
            if (baseUI.currentUIType.isClearStack)
            {
                if (!ClearStack())
                    Debug.Log($"栈中数据没有成功清空请检查参数{UIName}");
            }

            //根据不同的UI窗体的显示模式,分别作不同的加载处理
            switch (baseUI.currentUIType.mode)
            {
                case UIFormShowMode.Normal:
                    LoadUIToShowUIs(UIName);
                    break;
                case UIFormShowMode.ReverseChange:
                    PushUIToStack(UIName);
                    break;
                case UIFormShowMode.HideOther:
                    EnterUIAndHideOther(UIName);
                    break;
            }
        }

        /// <summary>
        /// 关闭(返回上一个)窗体
        /// </summary>
        public void CloseUI(string UIName)
        {
            BaseUI baseUI;
            //参数检查
            if (string.IsNullOrEmpty(UIName))
                return;
            //所有UI窗体 没有记录直接返回
            cacheUIs.TryGetValue(UIName, out baseUI);
            if (baseUI == null)
                return;

            switch (baseUI.currentUIType.mode)
            {
                case UIFormShowMode.Normal:
                    ExitUI(UIName);
                    break;
                case UIFormShowMode.ReverseChange:
                    PopUIToStack();
                    break;
                case UIFormShowMode.HideOther:
                    ExitUIAndDisPlayOther(UIName);
                    break;
            }

        }
        #region 显示"UI管理器"内部核心数据,测试使用

        /// <summary>
        /// 显示所有UI窗体的数量
        /// </summary>
        /// <returns></returns>
        public int ShowCacheUIsCount()
        {
            return cacheUIs?.Count ?? 0;
        }
        /// <summary>
        /// 显示当前窗体的数量
        /// </summary>
        /// <returns></returns>
        public int ShowShowUIsCount()
        {
            return showUIs?.Count ?? 0;
        }
        /// <summary>
        /// 显示栈窗体的数量
        /// </summary>
        /// <returns></returns>
        public int ShowStaUIsCount()
        {
            return staUIs?.Count ?? 0;
        }
        #endregion

        #region 私有方法 
        /// <summary>
        /// 初始化加载(rootUI窗体)Canvas预制体
        /// </summary>
        private void InitRootCanvasLoading()
        {
            ResourcesMgr.GetInstance().LoadAsset(SysDefine.canvasPath, false);
        }

        /// <summary>
        /// 加载与判断指定的UI窗体的名字,加载到"所有UI窗体"缓存里
        /// </summary>
        /// <param name="UIName"></param>
        /// <returns></returns>
        BaseUI LoadUIToCacheUIs(string UIName)
        {
            BaseUI baseUI;

            //得到就返回 得不到返回null
            cacheUIs.TryGetValue(UIName, out baseUI);
            if (baseUI == null)
            {
                baseUI = LoadUI(UIName);
            }
            return baseUI;
        }

        /// <summary>
        /// 根据不同位置信息,加载到root下不同的节点
        /// </summary>
        BaseUI LoadUI(string UIName)
        {
            string uiPath;
            GameObject UIPrefabs = null;
            BaseUI baseUI = null;

            UIPaths.TryGetValue(UIName, out uiPath);
            if (!string.IsNullOrEmpty(uiPath))
            {
                UIPrefabs = ResourcesMgr.GetInstance().LoadAsset(uiPath, false);
            }

            if (traRoot != null && UIPrefabs != null)
            {
                baseUI = UIPrefabs.GetComponent<BaseUI>();
                if (baseUI == null)
                {
                    Debug.Log($"baseUI==null,请确认窗口是否有BaseUI脚本,参数:{UIName}");
                    return null;
                }

                switch (baseUI.currentUIType.type)
                {
                    case UIFormType.Normal:
                        UIPrefabs.transform.SetParent(traNormal, false);
                        break;
                    case UIFormType.Fixed:
                        UIPrefabs.transform.SetParent(traFixed, false);
                        break;
                    case UIFormType.PopUp:
                        UIPrefabs.transform.SetParent(traPopUp, false);
                        break;
                }

                UIPrefabs.SetActive(false);
                cacheUIs.Add(UIName, baseUI);
                return baseUI;
            }
            else
            {
                Debug.Log($"traRoot!=null Or UIPrefabs!=null Please Check  参数{UIName}");
            }

            Debug.Log($"出现不可预估的错误 参数{UIName}");
            return null;
        }

        /// <summary>
        /// 把当前窗体加载到当前显示窗体集合中
        /// </summary>
        void LoadUIToShowUIs(string UIName)
        {
            BaseUI baseUI;
            BaseUI baseUIFormCache;

            showUIs.TryGetValue(UIName, out baseUI);

            if (baseUI != null)
                return;

            cacheUIs.TryGetValue(UIName, out baseUIFormCache);

            if (baseUIFormCache != null)
            {
                showUIs.Add(UIName, baseUIFormCache);
                baseUIFormCache.ActiveTrue();
            }

        }

        /// <summary>
        /// UI窗体入栈
        /// </summary>
        void PushUIToStack(string UIName)
        {
            BaseUI baseUI;
            //判断栈中是否有其他的窗体,有则冻结
            if (staUIs.Count > 0)
            {
                BaseUI topUI = staUIs.Peek();
                topUI.Freeze();
            }
            //判断UI所有窗体 是否有指定的UI窗体 有就处理
            cacheUIs.TryGetValue(UIName, out baseUI);
            if (baseUI != null)
            {
                baseUI.ActiveTrue();
                //把指定UI窗体,入栈
                staUIs.Push(baseUI);
            }
            else
                Debug.Log($"baseUI==null,请确认窗口是否有BaseUI脚本,参数:{UIName}");
        }

        /// <summary>
        /// 退出指定UI窗体
        /// </summary>
        /// <param name="UIName"></param>
        void ExitUI(string UIName)
        {
            BaseUI baseUI;
            //"正在显示集合"如果没有记录 则直接返回
            showUIs.TryGetValue(UIName, out baseUI);
            if (baseUI == null)
                return;
            //指定窗体,标记为隐藏状态,从正在显示集合中移除
            baseUI.ActiveFalse();
            showUIs.Remove(UIName);
        }

        /// <summary>
        /// 反向切换 窗体的出栈处理
        /// </summary>
        void PopUIToStack()
        {
            BaseUI baseUI = staUIs.Pop();
            baseUI.ActiveFalse();
            if (staUIs.Count >= 2)
            {
                //下一个窗体重新显示
                staUIs.Peek().ReActiveTrue();
            }
        }

        /// <summary>
        /// 打开窗体 隐藏其他窗体
        /// </summary>
        /// <param name="UIName"></param>
        void EnterUIAndHideOther(string UIName)
        {
            BaseUI baseUI;
            BaseUI baseUIFromAll;
            if (string.IsNullOrEmpty(UIName))
                return;
            showUIs.TryGetValue(UIName, out baseUI);
            if (baseUI != null)
                return;
            //把正在显示集合 栈集合都隐藏
            foreach (var item in showUIs)
            {
                item.Value.ActiveFalse();
            }

            foreach (var item in staUIs)
            {
                item.ActiveFalse();
            }

            //把当前窗体加入到"正在显示窗体"集合中,且做显示处理
            cacheUIs.TryGetValue(UIName, out baseUIFromAll);

            if (baseUIFromAll != null)
            {
                showUIs.Add(UIName, baseUIFromAll);
                baseUIFromAll.ActiveTrue();
            }
        }

        /// <summary>
        /// 关闭窗体 显示其他窗体
        /// </summary>
        /// <param name="UIName"></param>
        void ExitUIAndDisPlayOther(string UIName)
        {
            BaseUI baseUI;

            if (string.IsNullOrEmpty(UIName))
                return;
            showUIs.TryGetValue(UIName, out baseUI);
            if (baseUI == null)
                return;
            //把当前窗口失活移除
            baseUI.ActiveFalse();
            showUIs.Remove(UIName);
            //把正在显示集合 栈集合都激活
            foreach (var item in showUIs)
            {
                item.Value.ReActiveTrue();
            }

            foreach (var item in staUIs)
            {
                item.ReActiveTrue();
            }
        }

        /// <summary>
        /// 是否清空栈集合中数据
        /// </summary>
        /// <returns></returns>
        bool ClearStack()
        {
            if (staUIs != null && staUIs.Count >= 1)
            {
                staUIs.Clear();
                return true;
            }
            return false;
        }
        #endregion
    }
}

然后是把常量提取出来定义

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     SysDefine.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:   UI框架核心参数
 * 1.系统常量
 * 2.全局性方法
 * 3.系统枚举类型
 * 4.委托定义
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace UIFrameWork
{
    #region 系统枚举类型
    /// <summary>
    /// UI窗体(位置)类型
    /// </summary>
    public enum UIFormType
    {
        /// <summary>
        /// 普通窗体
        /// </summary>//一般是全屏窗体
        Normal,
        /// <summary>
        /// 固定窗体
        /// </summary>//一般是固定不动的 某一方向的一组图标
        Fixed,
        /// <summary>
        /// 弹出窗体
        /// </summary>
        PopUp,
    }

    /// <summary>
    /// UI窗体的显示类型
    /// </summary>
    public enum UIFormShowMode
    {
        /// <summary>
        /// 普通  
        /// </summary>//同一层级叠加
        Normal,
        /// <summary>
        /// 反向切换 
        /// </summary>//按照相反方向切换过去 原路弹回来
        ReverseChange,
        /// <summary>
        /// 隐藏其他 
        /// </summary>//覆盖掉其他窗口
        HideOther
    }

    /// <summary>
    /// UI窗体透明度类型
    /// </summary>
    public enum UIFormLucenyType
    {
        /// <summary>
        /// 完全透明,不能穿透
        /// </summary>
        Lucency,
        /// <summary>
        /// 半透明,不能穿透
        /// </summary>
        Translucence,
        /// <summary>
        /// 低透明度,不能穿透
        /// </summary>
        ImPenetrable,
        /// <summary>
        /// 可以穿透
        /// </summary>
        Pentrate
    }
    #endregion
    public class SysDefine
    {
        // 路径常量
        public const string canvasPath = "Canvas";

        //标签常量
        public const string canvasTag = "Canvas";

        // 节点常量
        public const string normalNode = "Normal";
        public const string fixedNode = "Fixed";
        public const string popUpNode = "PopUp";
        public const string ScriptsMgrNode = "ScriptsMgr";

        //摄像机层深的常量

        //全局性方法

        //委托的定义
    }
}

我又把他的监听类 添加Transform也可以传递 语法糖改为C#6

/***
 * 
 *    Title: "SUIFW"UI框架项目
 *           主题: 事件触发监听      
 *    Description: 
 *           功能: 实现对于任何对象的监听处理。
 *    Date: 2017
 *    Version: 0.1版本
 *    Modify Recoder: 
 *    
 *   
 */
using UnityEngine;
using UnityEngine.EventSystems;

namespace UIFrameWork
{
    public class EventTriggerListener : UnityEngine.EventSystems.EventTrigger
    {
        public delegate void VoidDelegate(GameObject go);
        public VoidDelegate onClick;
        public VoidDelegate onDown;
        public VoidDelegate onEnter;
        public VoidDelegate onExit;
        public VoidDelegate onUp;
        public VoidDelegate onSelect;
        public VoidDelegate onUpdateSelect;

        /// <summary>
        /// 得到“监听器”组件
        /// </summary>
        /// <param name="go">监听的游戏对象</param>
        /// <returns>
        /// 监听器
        /// </returns>
        public static EventTriggerListener Get(GameObject go)
        {
            EventTriggerListener lister = go?.GetComponent<EventTriggerListener>();
            if (lister == null)
            {
                lister = go.AddComponent<EventTriggerListener>();
            }
            return lister;
        }
        public static EventTriggerListener Get(Transform go)
        {
            EventTriggerListener lister = go?.GetComponent<EventTriggerListener>();
            if (lister == null)
            {
                lister = go.gameObject.AddComponent<EventTriggerListener>();
            }
            return lister;
        }

        public override void OnPointerClick(PointerEventData eventData)
        {
            onClick?.Invoke(gameObject);
        }

        public override void OnPointerDown(PointerEventData eventData)
        {
            onDown?.Invoke(gameObject);
        }

        public override void OnPointerEnter(PointerEventData eventData)
        {
            onEnter?.Invoke(gameObject);
        }

        public override void OnPointerExit(PointerEventData eventData)
        {
            onExit?.Invoke(gameObject);
        }

        public override void OnPointerUp(PointerEventData eventData)
        {
            onUp?.Invoke(gameObject);
        }

        public override void OnSelect(BaseEventData eventBaseData)
        {
            onSelect?.Invoke(gameObject);
        }

        public override void OnUpdateSelected(BaseEventData eventBaseData)
        {
            onUpdateSelect?.Invoke(gameObject);
        }

    }//Class_end
}

UI基类封装常用方法

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     BaseUI.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:  UI窗体父类
 *定义UI窗体的父类
 * 有四个生命周期
 * 1.Display显示状态
 * 2.Hiding隐藏状态
 * 3.ReDisplay再显示状态
 * 4.Freeze冻结状态 就是弹出窗体后面的窗体冻结
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UIFrameWork
{
    //窗体类型
    public class UIType
    {
        /// <summary>
        /// 是否清空"栈集合" 反向切换
        /// </summary>
        public bool isClearStack = false;
        /// <summary>
        /// UI窗体(位置)类型
        /// </summary>
        public UIFormType type = UIFormType.Normal;
        /// <summary>
        ///UI窗体显示类型
        /// </summary>
        public UIFormShowMode mode = UIFormShowMode.Normal;
        /// <summary>
        /// UI窗体透明度类型
        /// </summary>
        public UIFormLucenyType lucenyType = UIFormLucenyType.Lucency;

    }

    public class BaseUI : MonoBehaviour
    {
        public UIType currentUIType { get; set; } = new UIType();

        #region 窗体的四种状态
        /// <summary>
        /// 显示状态
        /// </summary>
        public virtual void ActiveTrue()
        {
            gameObject.SetActive(true);
        }
        /// <summary>
        /// 隐藏状态
        /// </summary>
        public virtual void ActiveFalse()
        {

            gameObject.SetActive(false);
        }
        /// <summary>
        /// 重新显示状态
        /// </summary>
        public virtual void ReActiveTrue()
        {
            gameObject.SetActive(true);
        }

        /// <summary>
        /// 冻结状态
        /// </summary>
        public virtual void Freeze()
        {
            gameObject.SetActive(true);
        }
        #endregion

        #region 封装子类常用方法
        /// <summary>
        /// 注册按钮事件
        /// </summary>
        protected void RigisterBtnOnClick(string btnName, EventTriggerListener.VoidDelegate del)
        {
            Transform btn = UnityHelper.Find(gameObject.transform, btnName);
            EventTriggerListener.Get(btn?.gameObject).onClick = del;
        }

        /// <summary>
        /// 打开UI窗体
        /// </summary>
        /// <param name="UIName"></param>
        protected void OpenUI(string UIName)
        {
            UIManager.instance.ShowUI(UIName);
        }

        /// <summary>
        /// 关闭UI窗体
        /// </summary>
        protected void CloseUI()
        {
            string UIName;
            //int intPos = -1;
            //命名空间+类名
            UIName = GetType().ToString();
            //查询第一次出现在这在第几位
            //intPos = UIName.IndexOf('.');

            //if (intPos != -1)
            //{
            //    UIName = UIName.Substring(intPos + 1);
            //}

            UIManager.instance.CloseUI(UIName);

        }
        #endregion
    }
}

提供一个方法类Find Add的封装

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     UnityHelper.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2017.2.2f1
 *Date:         2019-05-10
 *Description:  Unity帮助脚本
 * 提供程序用户一些常用功能方法实现,方便程序员快速开发
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UIFrameWork
{
    public class UnityHelper
    {
        /// <summary>
        /// 递归查找子结点
        /// </summary>
        /// <returns></returns>
        public static Transform Find(Transform parent, string childName)
        {
            Transform child = null;

            child = parent.transform.Find(childName);
            if (child == null)
            {
                //直接是所有子物体对象
                foreach (Transform item in parent)
                {
                    child = Find(item, childName);
                    if (child != null)
                        return child;
                }
            }
            return child;
        }

        /// <summary>
        /// 获取子节点对象的脚本 限定范围游戏对象的组件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parent"></param>
        /// <param name=""></param>
        /// <returns></returns>
        public static T GetComponent<T>(Transform parent, string childName) where T : Component
        {
            Transform child = null;
            child = Find(parent, childName);

            return child?.gameObject.GetComponent<T>();
        }

        /// <summary>
        /// 给子节点添加脚本
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parent"></param>
        /// <param name="childName"></param>
        /// <returns></returns>
        public static T AddComponent<T>(Transform parent, string childName) where T : Component
        {
            Transform child = null;
            child = Find(parent, childName);

            //如果有相同的脚本,则先删除
            T[] componentScriptsArray = child.GetComponents<T>();
            for (int i = 0; i < componentScriptsArray.Length; i++)
            {
                GameObject.Destroy(componentScriptsArray?[i]);
            }
            return child?.gameObject.AddComponent<T>();
        }

        /// <summary>
        /// 给子节点设置父对象
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="child"></param>
        public static void SetParent(Transform parent, Transform child)
        {
            //如果为真,则修改父级相对位置、比例和旋转,以便
            //对象保持与以前相同的世界空间位置、旋转和缩放。
            child.SetParent(parent, false);
            child.localPosition = Vector3.zero;
            child.localScale = Vector3.one;
            child.localEulerAngles = Vector3.zero;
        }

    }
}

然后拿去测试
这里定义一些窗口的常量定义

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     ProConst.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-12
 *Description:   项目中常量定义
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ProConst 
{
    //常量定义 UI窗体名
    public const string LoginUI = "LoginUI";
    public const string SelectUI = "SelectUI";
    

}

登录逻辑

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     LoginUI.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-11
 *Description:   登录窗体
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UIFrameWork;
public class LoginUI : BaseUI
{
    public void Awake()
    {
        //定义窗口性质 (默认数值)
        currentUIType.type = UIFormType.Normal;
        currentUIType.mode = UIFormShowMode.Normal;
        currentUIType.lucenyType = UIFormLucenyType.Lucency;

        //注册按钮
        RigisterBtnOnClick("btnLogin", LogonSys);
    }

    public void LogonSys(GameObject go)
    {
        //前台或者后台检查用户名称与密码

        //如果成功,则登录下一个窗口
        OpenUI(ProConst.SelectUI);
    }
}

人物选择界面逻辑

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     SelectUI.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-11
 *Description:   选择英雄窗体
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UIFrameWork;
public class SelectUI :BaseUI
{
    private void Awake()
    {
        //定义窗口性质 (默认数值)
        currentUIType.mode = UIFormShowMode.HideOther;
        //进入主城
        RigisterBtnOnClick("BtnConfirm", go =>Debug.Log("进入主城"));
        //返回方法
        RigisterBtnOnClick("BtnClose", go => CloseUI());
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}
image.png

拖一个选择界面


image.png

主城还没做 只有个Deg提示
然后可以来回切换
脚本这么放


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335