GameFramework框架 (五)UI组件

前言

在前面的代码中,我们有看到 GameEntry.UI.OpenUIForm(UIFormId.MenuForm, this);

这篇文章就从这行代码开始追踪,探索GF框架的UI组件

  protected override void OnEnter(ProcedureOwner procedureOwner)
        {
            base.OnEnter(procedureOwner);

            GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);

            m_StartGame = false;
            GameEntry.UI.OpenUIForm(UIFormId.MenuForm, this);
        }

代码追踪

//在UIExtension.cs中的静态扩展方法
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)
        {
            //从配置表中获取UI的配置信息
            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;
            }

            //从配置信息中读取AssetName, 这个接口获取的是Ui的预设体路径
            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);
        }


//UIComponent.cs中调用UIManager.OpenUIForm
  public int OpenUIForm(string uiFormAssetName, string uiGroupName, int priority, bool pauseCoveredUIForm, object userData)
        {
            return m_UIManager.OpenUIForm(uiFormAssetName, uiGroupName, priority, pauseCoveredUIForm, userData);
        }

//UIManager.cs, 下面的代码中,我删除了一些判空操作,方便阅读
  public int OpenUIForm(string uiFormAssetName, string uiGroupName, int priority, bool pauseCoveredUIForm, object userData)
        {
           
            UIGroup uiGroup = (UIGroup)GetUIGroup(uiGroupName);   
            // 对于UIManager来说,Serial一直是递增的,所有每一个UI实例的serialId都是不一样的      
            int serialId = ++m_Serial;
            //首先从池中获取,如果没获取到,使用资源管理器加载UI资源
            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;
        }

资源加载模块以后另开文章讨论,假设现在获取了实例,走到 InternalOpenUIForm中

 private void InternalOpenUIForm(int serialId, string uiFormAssetName, UIGroup uiGroup, object uiFormInstance, bool pauseCoveredUIForm, bool isNewInstance, float duration, object userData)
        {
            try
            {
        //实例化UI
                IUIForm uiForm = m_UIFormHelper.CreateUIForm(uiFormInstance, uiGroup, userData);
                if (uiForm == null)
                {
                    throw new GameFrameworkException("Can not create UI form in UI form helper.");
                }
        //调用OnInit, OnOpen等方法
                uiForm.OnInit(serialId, uiFormAssetName, uiGroup, pauseCoveredUIForm, isNewInstance, userData);
                uiGroup.AddUIForm(uiForm);
                uiForm.OnOpen(userData);
                uiGroup.Refresh();
                //打开成功了,触发事件,还记得上一节Event组件吧
                if (m_OpenUIFormSuccessEventHandler != null)
                {
                    OpenUIFormSuccessEventArgs openUIFormSuccessEventArgs = OpenUIFormSuccessEventArgs.Create(uiForm, duration, userData);
                    m_OpenUIFormSuccessEventHandler(this, openUIFormSuccessEventArgs);
                    ReferencePool.Release(openUIFormSuccessEventArgs);
                }
            }
            catch (Exception exception)
            {
                if (m_OpenUIFormFailureEventHandler != null)
                {
                    OpenUIFormFailureEventArgs openUIFormFailureEventArgs = OpenUIFormFailureEventArgs.Create(serialId, uiFormAssetName, uiGroup.Name, pauseCoveredUIForm, exception.ToString(), userData);
                    m_OpenUIFormFailureEventHandler(this, openUIFormFailureEventArgs);
                    ReferencePool.Release(openUIFormFailureEventArgs);
                    return;
                }

                throw;
            }
        }

GameEntry.UI.OpenUIForm这个接口的执行总结,

首先读取配置,获取UIForm的相关信息,如资源名称,分组

首先到对象池重查找UI实例,

如果没有查找到使用资源加载模块,加载UI实例,

在获取UI实例后,创建UIForm组件,执行UIForm的 OnInit ,添加分组, OnOpen方法,并且触发OpenUIFormSuccessEventArgs 事件

IUIForm

 IUIForm是封装的UI生命周期的接口类,主要的接口有 SerialId, UIFormAssetName,Handle,

UIGroup, DepthInUIGroup,OnInit,OnOpen,OnClose等,请自行查看

UIForm是对IUIForm的实现,其中 //生命周期的实现封装到了UIFormLogic中

 public sealed class UIForm : MonoBehaviour, IUIForm
    {
        private int m_SerialId;
        private string m_UIFormAssetName;
        private IUIGroup m_UIGroup;
        private int m_DepthInUIGroup;
        private bool m_PauseCoveredUIForm;

        //生命周期的实现封装到了UIFormLogic中
        private UIFormLogic m_UIFormLogic;

        /// <summary>
        /// 获取界面序列编号。
        /// </summary>
        public int SerialId
        {
            get
            {
                return m_SerialId;
            }
        }

  public UIFormLogic Logic
        {
            get
            {
                return m_UIFormLogic;
            }
        }

  public void OnInit(int serialId, string uiFormAssetName, IUIGroup uiGroup, bool pauseCoveredUIForm, bool isNewInstance, object userData)
        {
            m_SerialId = serialId;
            m_UIFormAssetName = uiFormAssetName;
            m_UIGroup = uiGroup;
            m_DepthInUIGroup = 0;
            m_PauseCoveredUIForm = pauseCoveredUIForm;

            if (!isNewInstance)
            {
                return;
            }

            m_UIFormLogic = GetComponent<UIFormLogic>();
            if (m_UIFormLogic == null)
            {
                Log.Error("UI form '{0}' can not get UI form logic.", uiFormAssetName);
                return;
            }

            try
            {
                m_UIFormLogic.OnInit(userData);
            }
            catch (Exception exception)
            {
                Log.Error("UI form '[{0}]{1}' OnInit with exception '{2}'.", m_SerialId, m_UIFormAssetName, exception);
            }
        }

}

UGUIForm继承子UIFormLogic,实现了生命周期中通用的一些功能


    public abstract class UGuiForm : UIFormLogic
    {
        public const int DepthFactor = 100;
        private const float FadeTime = 0.3f;

        private static Font s_MainFont = null;
        private Canvas m_CachedCanvas = null;
        private CanvasGroup m_CanvasGroup = null;
        private List<Canvas> m_CachedCanvasContainer = new List<Canvas>();

        public int OriginalDepth
        {
            get;
            private set;
        }

      ......
#if UNITY_2017_3_OR_NEWER
        protected override void OnInit(object userData)
#else
        protected internal override void OnInit(object userData)
#endif
        {
            base.OnInit(userData);
            //添加Canvas,
            m_CachedCanvas = gameObject.GetOrAddComponent<Canvas>();
            m_CachedCanvas.overrideSorting = true;
            OriginalDepth = m_CachedCanvas.sortingOrder;

            m_CanvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();

            RectTransform transform = GetComponent<RectTransform>();
            //设置Transform
            transform.anchorMin = Vector2.zero;
            transform.anchorMax = Vector2.one;
            transform.anchoredPosition = Vector2.zero;
            transform.sizeDelta = Vector2.zero;

            gameObject.GetOrAddComponent<GraphicRaycaster>();
            //获取组件
            Text[] texts = GetComponentsInChildren<Text>(true);
            for (int i = 0; i < texts.Length; i++)
            {
                texts[i].font = s_MainFont;
                if (!string.IsNullOrEmpty(texts[i].text))
                {
                    texts[i].text = GameEntry.Localization.GetString(texts[i].text);
                }
            }
        }

}

平时开发UI界面,只需要继承UGUIForm,实现我们自定义的功能即可,参考MenuForm

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);
        }

UI代码分析到此结束,下面看一下日常开发

UI面板开发流程:

1. 制作UI面板预设,添加脚本,脚本需要继承自 UGUIForm,  在脚本中实现相应的功能,见上文

 2. 编写配置文件

 注意UI预设体的路径和配置的AssetName,必须正确映射, 默认的路径如下图:

 这里是映射关系,如果要改默认路径,记得改一下AssetUtility中的路径映射

//这是UIExtension.cs文件中,OPenUIForm中的代码

//通过配置的AssetName获取的其实是预设体的路径
string assetName = AssetUtility.GetUIFormAsset(drUIForm.AssetName);


//AssetUtility.cs
   public static string GetUIFormAsset(string assetName)
        {
            return Utility.Text.Format("Assets/GameMain/UI/UIForms/{0}.prefab", assetName);
        }

3. 调用  GameEntry.UI.OpenUIForm(UIFormId.MenuForm, this); 就可以加载UI了,

UIComponent

UIComponent封装了UI操作的一些常用接口,可以使用GameEntry.UI.xxx调用,举几个粒子

关闭自身

 点击按钮切换子页签

2022年11月18日,再读源码

问题1:OpenUI中都干了什么

这种写法其实是有楼栋的,当返回值为null时,可能会覆盖之前的有效值

正确的写法应该是:

配置表参数:
#	界面配置表					
#	Id		AssetName	UIGroupName	AllowMultiInstance	PauseCoveredUIForm
#	int		string	string	bool	bool
#	界面编号	策划备注	资源名称	界面组名称	是否允许多个界面实例	是否暂停被其覆盖的界面
	1	弹出框	DialogForm	Default	TRUE	TRUE
	100	主菜单	MenuForm	Default	FALSE	TRUE
	101	设置	SettingForm	Default	FALSE	TRUE
	102	关于	AboutForm	Default	FALSE	TRUE


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

  public static OpenUIFormInfo Create(int serialId, UIGroup uiGroup, bool pauseCoveredUIForm, object userData)
            {
                OpenUIFormInfo openUIFormInfo = ReferencePool.Acquire<OpenUIFormInfo>();
                openUIFormInfo.m_SerialId = serialId;
                openUIFormInfo.m_UIGroup = uiGroup;
                openUIFormInfo.m_PauseCoveredUIForm = pauseCoveredUIForm;
                openUIFormInfo.m_UserData = userData;
                return openUIFormInfo;
            }
//加载回调
 m_LoadAssetCallbacks = new LoadAssetCallbacks(LoadAssetSuccessCallback, LoadAssetFailureCallback, LoadAssetUpdateCallback, LoadAssetDependencyAssetCallback);

 private void LoadAssetSuccessCallback(string uiFormAssetName, object uiFormAsset, float duration, object userData)
        {
            OpenUIFormInfo openUIFormInfo = (OpenUIFormInfo)userData;
            if (openUIFormInfo == null)
            {
                throw new GameFrameworkException("Open UI form info is invalid.");
            }

            if (m_UIFormsToReleaseOnLoad.Contains(openUIFormInfo.SerialId))
            {
                m_UIFormsToReleaseOnLoad.Remove(openUIFormInfo.SerialId);
                ReferencePool.Release(openUIFormInfo);
                m_UIFormHelper.ReleaseUIForm(uiFormAsset, null);
                return;
            }

            m_UIFormsBeingLoaded.Remove(openUIFormInfo.SerialId);
            UIFormInstanceObject uiFormInstanceObject = UIFormInstanceObject.Create(uiFormAssetName, uiFormAsset, m_UIFormHelper.InstantiateUIForm(uiFormAsset), m_UIFormHelper);
            m_InstancePool.Register(uiFormInstanceObject, true);

            InternalOpenUIForm(openUIFormInfo.SerialId, uiFormAssetName, openUIFormInfo.UIGroup, uiFormInstanceObject.Target, openUIFormInfo.PauseCoveredUIForm, true, duration, openUIFormInfo.UserData);
            ReferencePool.Release(openUIFormInfo);
        }

在加载完成资源之后,

生成实例化对象UIFormInstanceObject ,

把实例化对象注册到对象池

 

 

public static UIFormInfo Create(IUIForm uiForm)
                {
                    if (uiForm == null)
                    {
                        throw new GameFrameworkException("UI form is invalid.");
                    }

                    UIFormInfo uiFormInfo = ReferencePool.Acquire<UIFormInfo>();
                    uiFormInfo.m_UIForm = uiForm;
                    uiFormInfo.m_Paused = true;
                    uiFormInfo.m_Covered = true;
                    return uiFormInfo;
                }


 public void AddUIForm(IUIForm uiForm)
            {
                m_UIFormInfos.AddFirst(UIFormInfo.Create(uiForm));
            }

   public void OnOpen(object userData)
        {
           
            try
            {
                //转发
                m_UIFormLogic.OnOpen(userData);
            }
            catch (Exception exception)
            {
                Log.Error("UI form '[{0}]{1}' OnOpen with exception '{2}'.", m_SerialId, m_UIFormAssetName, exception);
            }
        }


//实际调用
  protected internal virtual void OnOpen(object userData)
        {          
            m_Available = true;
            Visible = true;         
        }

层级逻辑刷新

  public void Refresh()
            {
                //新打开的 是 AddFirst
                LinkedListNode<UIFormInfo> current = m_UIFormInfos.First;
                bool pause = m_Pause;
                bool cover = false;
                int depth = UIFormCount;
                while (current != null && current.Value != null)
                {
                    LinkedListNode<UIFormInfo> next = current.Next;
                    current.Value.UIForm.OnDepthChanged(Depth, depth--);
                    if (current.Value == null)
                    {
                        return;
                    }

                    //当前组暂停,调用form的pause和cover
                    if (pause)
                    {
                        if (!current.Value.Covered)
                        {
                            current.Value.Covered = true;
                            current.Value.UIForm.OnCover();
                            if (current.Value == null)
                            {
                                return;
                            }
                        }

                        if (!current.Value.Paused)
                        {
                            current.Value.Paused = true;
                            current.Value.UIForm.OnPause();
                            if (current.Value == null)
                            {
                                return;
                            }
                        }
                    }
                    else   //当前组没有暂停
                    {

                        //当前Form暂停,则恢复
                        if (current.Value.Paused)
                        {
                            current.Value.Paused = false;
                            current.Value.UIForm.OnResume();
                            if (current.Value == null)
                            {
                                return;
                            }
                        }

                        if (current.Value.UIForm.PauseCoveredUIForm)
                        {
                            pause = true;
                        }

                        if (cover)  //这里默认是false
                        {
                            if (!current.Value.Covered)
                            {
                                current.Value.Covered = true;
                                current.Value.UIForm.OnCover();
                                if (current.Value == null)
                                {
                                    return;
                                }
                            }
                        }
                        else
                        {

                            //当前Form被覆盖,则恢复
                            if (current.Value.Covered)
                            {
                                current.Value.Covered = false;
                                current.Value.UIForm.OnReveal();
                                if (current.Value == null)
                                {
                                    return;
                                }
                            }

                            cover = true;
                        }
                    }

                    current = next;
                }
            }

问题2:如果需要在CloseUIForm中执行一些延迟操作该如何操作?

可以利用 userData参数,执行一起操作之后,比如说延迟1秒等,

再执行Close操作 

public void CloseUIForm(IUIForm uiForm, object userData)
        {
            if (uiForm == null)
            {
                throw new GameFrameworkException("UI form is invalid.");
            }

            UIGroup uiGroup = (UIGroup)uiForm.UIGroup;
            if (uiGroup == null)
            {
                throw new GameFrameworkException("UI group is invalid.");
            }

            //这里改变了 uiform的cover 和 pause属性
            uiGroup.RemoveUIForm(uiForm);
            uiForm.OnClose(m_IsShutdown, userData);   //这里可能有异步操作
            //这里改变了深度值 
            uiGroup.Refresh();
           
            if (m_CloseUIFormCompleteEventHandler != null)
            {
                CloseUIFormCompleteEventArgs closeUIFormCompleteEventArgs = CloseUIFormCompleteEventArgs.Create(uiForm.SerialId, uiForm.UIFormAssetName, uiGroup, userData);
                m_CloseUIFormCompleteEventHandler(this, closeUIFormCompleteEventArgs);
                ReferencePool.Release(closeUIFormCompleteEventArgs);
            }

            m_RecycleQueue.Enqueue(uiForm);
        }

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
GameFramework框架中,销毁UI对象需要通过以下步骤进行操作。 首先,我们需要通过UIComponent接口的GetUIForm方法获取到UI对象对应的UIForm组件。然后,调用Close方法关闭UIForm组件,将UI对象从当前场景中移除,并且释放其占用的资源。 在销毁UI对象之前,我们还需要进行一些清理工作。可以通过UIForm组件的OnClose方法来进行处理。在OnClose方法中,我们可以处理一些准备销毁UI对象前的逻辑,例如取消注册事件、清理数据等操作。 最后,通过GameEntry.UI的CloseUIForm方法将UIForm组件UI管理模块中移除,并触发UIForm组件的OnClose方法。此时,UI对象已经完成销毁操作。 以下是具体的伪代码示例: ``` using GameFramework; using UnityGameFramework.Runtime; using UnityEngine; public class MyUIForm : UIFormLogic { // ... protected override void OnClose(bool isShutdown, object userData) { base.OnClose(isShutdown, userData); // 在UI对象关闭之前进行清理工作 // 例如取消注册事件、清理数据等操作 } // ... public void CloseUIForm() { // 调用Close方法关闭UIForm组件 Close(isShutdown: false); } } ``` ``` using GameFramework.UI; public class UIManager : MonoBehaviour { // ... public void DestroyUIForm(MyUIForm uiForm) { // 获取UIForm组件 UIForm uiFormComponent = uiForm.GetUIForm(); // 关闭UIForm组件 uiFormComponent.Close(isShutdown: false); // 从UI管理模块中移除UIForm组件,并触发Close事件 GameEntry.UI.CloseUIForm(uiFormComponent); } } ``` 通过上述步骤,我们就可以在GameFramework框架中销毁UI对象了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值