【GameFramework框架内置模块】18、界面(UI)

推荐阅读

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

【GameFramework框架】系列教程目录:
https://blog.csdn.net/q764424567/article/details/135831551

二、正文

2-1、介绍

界面(UI)提供管理UI界面和界面组的功能,比如显示界面、激活界面、改变界面层级等。

界面(UI)为Unity的GUI系统提供了灵活和强大的功能,帮助开发者快速构建游戏UI。

还可以与现有的Unity 的UI插件(如NGUI、FGUI)及GUI系统(UGUI)无缝集成,只要派生自UIFormLogic类并实现自己的界面类即可使用。

界面使用结束后可以不立即销毁,等待下一次使用。

2-2、使用说明

(1)UIComponent组件的编辑器面板

在这里插入图片描述
UIComponent组件的编辑器面板,配置UI的回调事件、UI界面组、界面对象池参数,以及辅助器的设置。

UIGroups
UI界面的管理,层级的管理是不可少的,而UIGroups就是处理不同页面的层级。

在同一个Group中可以打开不同的页面。

不同的Group组中的UI的层级不一样的,比如UI我们可以这么分:

  • Scene层:最底层,场景UI,一般置于场景之上,界面UI之下,如:点击建筑查看建筑信息—
  • Background层:背景UI,如:主界面,一般情况下用户不能主动关闭,永远处于其它Ul的最底层
  • Normal层:普通UI,一级、二级、三级等窗口,一般由用户点击打开的多级窗口
  • lnfo层:信息UI,—如:跑马灯、广播等,—般永远置于用户打开窗口顶层
  • Tip层:提示UI,如:错误弹窗,网络连接弹窗等
  • Top层:顶层UI,如:场景加载
(2)配置UI界面配置表

配置表
在这里插入图片描述
界面配置表中配置了UI界面Id、资源名AssetName、UI组名UIGroupName、是否允许多个实例allow multiple instances,以及是否覆盖UIForm。

预制体
在这里插入图片描述

参数及枚举

这些参数的设置,可以保证框架在打开UI界面时可以找到UI界面的相关参数。

在UIFormId.cs中,添加枚举,注意枚举的Id要和配置表的id一致:

namespace StarForce
{
    /// <summary>
    /// 界面编号。
    /// </summary>
    public enum UIFormId : byte
    {
        Undefined = 0,

        /// <summary>
        /// 弹出框。
        /// </summary>
        DialogForm = 1,

        /// <summary>
        /// 主菜单。
        /// </summary>
        MenuForm = 100,

        /// <summary>
        /// 设置。
        /// </summary>
        SettingForm = 101,

        /// <summary>
        /// 关于。
        /// </summary>
        AboutForm = 102,
    }
}
(3)打开、获取、关闭UI界面

UI的相关操作,代码示例如下:

//打开
GameEntry.UI.OpenUIForm(UIFormId.MenuForm);
//获取
GameEntry.UI.GetUIForm(UIFormId.MenuForm);
//关闭
GameEntry.UI.CloseUIForm(uiForm);

这里只说明如何打开UI,具体UI的加载过程将在下一节详细分析。

(4)传递参数

打开UI传参数数组,或者自己指定实体类。

传参:

GameEntry.UI.OpenUIForm(UIFormId.MainForm, new object[]{xxx,xxx});

UI界面接收参数:

public class MenuForm : UGuiForm
{
    protected override void OnOpen(object userData)
    {
        base.OnOpen(userData);
		var args = (object[])userData;
		//......
    }
}
(5)实现UI界面的事件

MenuForm.cs为例,继承UGuiForm后,实现UI事件即可:

using UnityEngine;
using UnityGameFramework.Runtime;

namespace StarForce
{
    public class MenuForm : UGuiForm
    {
        [SerializeField]
        private GameObject m_QuitButton = null;

        private ProcedureMenu m_ProcedureMenu = null;

        public void OnStartButtonClick()
        {
            m_ProcedureMenu.StartGame();
        }

        public void OnSettingButtonClick()
        {
            GameEntry.UI.OpenUIForm(UIFormId.SettingForm);
        }

        public void OnAboutButtonClick()
        {
            GameEntry.UI.OpenUIForm(UIFormId.AboutForm);
        }

        public void OnQuitButtonClick()
        {
            GameEntry.UI.OpenDialog(new DialogParams()
            {
                Mode = 2,
                Title = GameEntry.Localization.GetString("AskQuitGame.Title"),
                Message = GameEntry.Localization.GetString("AskQuitGame.Message"),
                OnClickConfirm = delegate (object userData) { UnityGameFramework.Runtime.GameEntry.Shutdown(ShutdownType.Quit); },
            });
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnOpen(object userData)
#else
        protected internal override void OnOpen(object userData)
#endif
        {
            base.OnOpen(userData);

            m_ProcedureMenu = (ProcedureMenu)userData;
            if (m_ProcedureMenu == null)
            {
                Log.Warning("ProcedureMenu is invalid when open MenuForm.");
                return;
            }

            m_QuitButton.SetActive(Application.platform != RuntimePlatform.IPhonePlayer);
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnClose(bool isShutdown, object userData)
#else
        protected internal override void OnClose(bool isShutdown, object userData)
#endif
        {
            m_ProcedureMenu = null;

            base.OnClose(isShutdown, userData);
        }
    }
}

绑定事件:
在这里插入图片描述

2-3、实现及代码分析

接下来,就来分析一下GameFramework框架,在GameEntry.UI.OpenUIForm(UIFormId.MenuForm)中做了那些事情吧。

(1)打开UI界面过程及代码分析

打开UI界面:

GameEntry.UI.OpenUIForm(UIFormId.MenuForm, this);

获取数据表中的数据传递给uiComponent.OpenUIForm()函数:

public static int? OpenUIForm(this UIComponent uiComponent, UIFormId uiFormId, object userData = null)
{
    return uiComponent.OpenUIForm((int)uiFormId, userData);
}

public static int? OpenUIForm(this UIComponent uiComponent, int uiFormId, object userData = null)
{
    IDataTable<DRUIForm> dtUIForm = GameEntry.DataTable.GetDataTable<DRUIForm>();
    DRUIForm drUIForm = dtUIForm.GetDataRow(uiFormId);
    if (drUIForm == null)
    {
        Log.Warning("Can not load UI form '{0}' from data table.", uiFormId.ToString());
        return null;
    }

    string assetName = AssetUtility.GetUIFormAsset(drUIForm.AssetName);
    if (!drUIForm.AllowMultiInstance)
    {
        if (uiComponent.IsLoadingUIForm(assetName))
        {
            return null;
        }

        if (uiComponent.HasUIForm(assetName))
        {
            return null;
        }
    }

    return uiComponent.OpenUIForm(assetName, drUIForm.UIGroupName, Constant.AssetPriority.UIFormAsset, drUIForm.PauseCoveredUIForm, userData);
}

资源路径的获取:

string assetName = AssetUtility.GetUIFormAsset(drUIForm.AssetName);
// 获取到预制体的路径
public static string GetUIFormAsset(string assetName)
{
    return Utility.Text.Format("Assets/GameMain/UI/UIForms/{0}.prefab", assetName);
}

调用UIComponent组件封装的m_UIManager的API:

/// <summary>
/// 打开界面。
/// </summary>
/// <param name="uiFormAssetName">界面资源名称。</param>
/// <param name="uiGroupName">界面组名称。</param>
/// <param name="priority">加载界面资源的优先级。</param>
/// <param name="pauseCoveredUIForm">是否暂停被覆盖的界面。</param>
/// <param name="userData">用户自定义数据。</param>
/// <returns>界面的序列编号。</returns>
public int OpenUIForm(string uiFormAssetName, string uiGroupName, int priority, bool pauseCoveredUIForm, object userData)
{
    return m_UIManager.OpenUIForm(uiFormAssetName, uiGroupName, priority, pauseCoveredUIForm, userData);
}

OK,最后一步了属于是,我们找到UIManager.cs脚本来看一下:

/// <summary>
/// 打开界面。
/// </summary>
/// <param name="uiFormAssetName">界面资源名称。</param>
/// <param name="uiGroupName">界面组名称。</param>
/// <param name="priority">加载界面资源的优先级。</param>
/// <param name="pauseCoveredUIForm">是否暂停被覆盖的界面。</param>
/// <param name="userData">用户自定义数据。</param>
/// <returns>界面的序列编号。</returns>
public int OpenUIForm(string uiFormAssetName, string uiGroupName, int priority, bool pauseCoveredUIForm, object userData)
{
    if (m_ResourceManager == null)
    {
        throw new GameFrameworkException("You must set resource manager first.");
    }

    if (m_UIFormHelper == null)
    {
        throw new GameFrameworkException("You must set UI form helper first.");
    }

    if (string.IsNullOrEmpty(uiFormAssetName))
    {
        throw new GameFrameworkException("UI form asset name is invalid.");
    }

    if (string.IsNullOrEmpty(uiGroupName))
    {
        throw new GameFrameworkException("UI group name is invalid.");
    }

    UIGroup uiGroup = (UIGroup)GetUIGroup(uiGroupName);
    if (uiGroup == null)
    {
        throw new GameFrameworkException(Utility.Text.Format("UI group '{0}' is not exist.", uiGroupName));
    }

    int serialId = ++m_Serial;
    UIFormInstanceObject uiFormInstanceObject = m_InstancePool.Spawn(uiFormAssetName);
    if (uiFormInstanceObject == null)
    {
        m_UIFormsBeingLoaded.Add(serialId, uiFormAssetName);
        m_ResourceManager.LoadAsset(uiFormAssetName, priority, m_LoadAssetCallbacks, OpenUIFormInfo.Create(serialId, uiGroup, pauseCoveredUIForm, userData));
    }
    else
    {
        InternalOpenUIForm(serialId, uiFormAssetName, uiGroup, uiFormInstanceObject.Target, pauseCoveredUIForm, false, 0f, userData);
    }

    return serialId;
}

最后,判断是否存在这个UI界面的实例,有的话加载,没有的话通过Resource模块进行加载。

害!最后还得是Resource模块进行加载。

(2)UIManager.cs

UlManager类是整个UI框架的总管理器,负责UI的所有调度,如打开,关闭,激活等。

  • UIManager使用了ResourceManager来加载界面预制体资源等,所以UI框架只支持异步加载,不能同步打开UI界面或传参。
  • UIManager使用了ObjectPool来处理UI界面的复用和回收。让我们完全不用担心UI界面的垃圾处理问题,大胆放心的使用UI框架。
  • UIManager使用了Group组的方式类存储和维护UI界面,非常方便我们管理和扩展界面功能。
  • UIFormHelper是处理界面实例化,初始化和释放的辅助器,方便我们在实际使用时扩展UlManager
namespace GameFramework.UI
{
	/// 界面管理器。
	internal sealed partial class UIManager : GameFrameworkModule, IUIManager
	{
        private readonly Dictionary<string, UIGroup> m_UIGroups;//UI界面组
        private IResourceManager m_ResourceManager;//资源管理器
        private IObjectPool<UIFormInstanceObject> m_InstancePool;//界面实例对象池
        private IUIFormHelper m_UIFormHelper;//界面辅助器Helper
        
	    /// 增加界面组。
	    public bool AddUIGroup(string uiGroupName, int uiGroupDepth, IUIGroupHelper uiGroupHelper)
	    /// 打开界面。
	    public int OpenUIForm(string uiFormAssetName, string uiGroupName, object userData)
	    /// 关闭界面
	    public void CloseUIForm(IUIForm uiForm)
	 	/// 初始化打开UIForm
	    private void InternalOpenUIForm(int serialId, string uiFormAssetName, UIGroup uiGroup, object uiFormInstance, bool pauseCoveredUIForm, bool isNewInstance, float duration, object userData)
		/// 异步加载UI预制体回调
	    private void LoadAssetSuccessCallback(string uiFormAssetName, object uiFormAsset, float duration, object userData)
	}
}
(4)UIForm、UIFormLogic、UGuiForm

每个单独的页面是一个UIForm,它继承自IUIForm:

/// <summary>
/// 界面。
/// </summary>
public sealed class UIForm : MonoBehaviour, IUIForm
{
    private UIFormLogic m_UIFormLogic;/// 获取界面逻辑。

    /// 界面回收。
    public void OnRecycle(){m_UIFormLogic.OnRecycle();}
    /// 界面打开。
    public void OnOpen(object userData){m_UIFormLogic.OnOpen(userData);}
    /// 界面关闭。
    public void OnClose(bool isShutdown, object userData){m_UIFormLogic.OnClose(isShutdown, userData);}
    /// 界面暂停。
    public void OnPause(){m_UIFormLogic.OnPause();}
    /// 界面暂停恢复。
    public void OnResume(){m_UIFormLogic.OnResume();}
    /// 界面遮挡。
    public void OnCover(){m_UIFormLogic.OnCover();}
    /// 界面遮挡恢复。
    public void OnReveal(){m_UIFormLogic.OnReveal();}
    /// 界面激活。
    public void OnRefocus(object userData){m_UIFormLogic.OnRefocus(userData);}
}

UIForm只是一个UI窗口,,所有的界面逻辑都写在UlFormLogic中。

不管是UGUI,NGUI还是FGUI等等,只要继承自UIFormLogic就可以了。

比如Starforce中的UGuiForm:

namespace StarForce
{
    public abstract class UGuiForm : UIFormLogic
    {
        protected Canvas m_CachedCanvas = null;
        protected CanvasGroup m_CanvasGroup = null;

        //自定义函数
        public void Close(bool ignoreFade)
        public void PlayUISound(int uiSoundId)
        public static void SetMainFont(Font mainFont)

        //生命周期函数
        protected override void OnInit(object userData)
        protected override void OnRecycle()
        protected override void OnOpen(object userData)
        protected override void OnClose(bool isShutdown, object userData)
        protected override void OnPause()
        protected override void OnResume()
        protected override void OnCover()
        protected override void OnReveal()
        protected override void OnRefocus(object userData)
        protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
        protected override void OnDepthChanged(int uiGroupDepth, int depthInUIGroup)
    }
}

我们可以根据UGuiFormLogic的结构来实现我们自己的NGuiFormLogic或者FGuiFormLogic等等。

三、后记

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏 方向 简介
Unity3D开发小游戏 小游戏开发教程 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶 入门 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUI UGUI Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据 文件读取 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合 数据集合 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发 虚拟仿真 总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件 插件 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发 日常记录 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG 日常记录 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-27 00:02:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-27 00:02:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-27 00:02:04       82 阅读
  4. Python语言-面向对象

    2024-03-27 00:02:04       91 阅读

热门阅读

  1. 2024.3.26

    2024.3.26

    2024-03-27 00:02:04      38 阅读
  2. 计算机的内存不等于存储器

    2024-03-27 00:02:04       42 阅读
  3. 3.26C++

    3.26C++

    2024-03-27 00:02:04      41 阅读
  4. 2024.3.25力扣(1200-1400)刷题记录

    2024-03-27 00:02:04       34 阅读
  5. 力扣438---找到字符串中所有字母异位词

    2024-03-27 00:02:04       40 阅读
  6. 使用Spring ORM和MyBatis简化数据库访问

    2024-03-27 00:02:04       37 阅读
  7. 13、Spring CLI中的特殊命令

    2024-03-27 00:02:04       44 阅读
  8. LeetCode1047:删除字符串中的所有相邻重复项

    2024-03-27 00:02:04       44 阅读