当前位置:   article > 正文

Unity3D问题之简单UI框架设计和实现_unity ui界面跳转和层级

unity ui界面跳转和层级

目标:编写一个简单通用UI框架用于管理页面和完成导航跳转

框架具体实现的功能和需求

加载,显示,隐藏,关闭页面,根据标示获得相应界面实例
提供界面显示隐藏动画接口
单独界面层级,Collider,背景管理
根据存储的导航信息完成界面导航
界面通用对话框管理(多类型Message Box)
便于进行需求和功能扩展(比如,在跳出页面之前添加逻辑处理等)


编写UI框架意义

打开,关闭,层级,页面跳转等管理问题集中化,将外部切换等逻辑交给UIManager处理
功能逻辑分散化,每个页面维护自身逻辑,依托于框架便于多人协同开发,不用关心跳转和显示关闭细节
通用性框架能够做到简单的代码复用和"项目经验"沉淀


步入正题,如何实现

窗口类设计:基本窗口对象,维护自身逻辑维护
窗口管理类:控制被管理窗口的打开和关闭等逻辑(具体设计请看下文)
动画接口:提供打开和关闭动画接口,提供动画完成回调函数等
层级,Collider和通用背景管理


窗口基类设计

框架中设计的窗口类型和框架所需定义如下,对于UIBaseWindow 基类,只给出一个界面基类需要提供的接口
  1. public enum UIWindowType
  2. {
  3. Normal, // 可推出界面(UIMainMenu,UIRank等)
  4. Fixed, // 固定窗口(UITopBar等)
  5. PopUp, // 模式窗口
  6. }
  7. public enum UIWindowShowMode
  8. {
  9. DoNothing,
  10. HideOther, // 闭其他界面
  11. NeedBack, // 点击返回按钮关闭当前,不关闭其他界面(需要调整好层级关系)
  12. NoNeedBack, // 关闭TopBar,关闭其他界面,不加入backSequence队列
  13. }
  14. public enum UIWindowColliderMode
  15. {
  16. None, // 显示该界面不包含碰撞背景
  17. Normal, // 碰撞透明背景
  18. WithBg, // 碰撞非透明背景
  19. }

  1. public class UIBaseWindow : MonoBehaviour
  2. {
  3. protected UIPanel originPanel;
  4. // 如果需要可以添加一个BoxCollider屏蔽事件
  5. private bool isLock = false;
  6. protected bool isShown = false;
  7. // 当前界面ID
  8. protected WindowID windowID = WindowID.WindowID_Invaild;
  9. // 指向上一级界面ID(BackSequence无内容,返回上一级)
  10. protected WindowID preWindowID = WindowID.WindowID_Invaild;
  11. public WindowData windowData = new WindowData();
  12. // Return处理逻辑
  13. private event BoolDelegate returnPreLogic = null;
  14. protected Transform mTrs;
  15. protected virtual void Awake()
  16. {
  17. this.gameObject.SetActive(true);
  18. mTrs = this.gameObject.transform;
  19. InitWindowOnAwake();
  20. }
  21. private int minDepth = 1;
  22. public int MinDepth
  23. {
  24. get { return minDepth; }
  25. set { minDepth = value; }
  26. }
  27. ///
  28. /// 能否添加到导航数据中
  29. ///
  30. public bool CanAddedToBackSeq
  31. {
  32. get
  33. {
  34. if (this.windowData.windowType == UIWindowType.PopUp)
  35. return false;
  36. if (this.windowData.windowType == UIWindowType.Fixed)
  37. return false;
  38. if (this.windowData.showMode == UIWindowShowMode.NoNeedBack)
  39. return false;
  40. return true;
  41. }
  42. }
  43. ///
  44. /// 界面是否要刷新BackSequence数据
  45. /// 1.显示NoNeedBack或者从NoNeedBack显示新界面 不更新BackSequenceData(隐藏自身即可)
  46. /// 2.HideOther
  47. /// 3.NeedBack
  48. ///
  49. public bool RefreshBackSeqData
  50. {
  51. get
  52. {
  53. if (this.windowData.showMode == UIWindowShowMode.HideOther
  54. || this.windowData.showMode == UIWindowShowMode.NeedBack)
  55. return true;
  56. return false;
  57. }
  58. }
  59. ///
  60. /// 在Awake中调用,初始化界面(给界面元素赋值操作)
  61. ///
  62. public virtual void InitWindowOnAwake()
  63. {
  64. }
  65. ///
  66. /// 获得该窗口管理类
  67. ///
  68. public UIManagerBase GetWindowManager
  69. {
  70. get
  71. {
  72. UIManagerBase baseManager = this.gameObject.GetComponent();
  73. return baseManager;
  74. }
  75. private set { }
  76. }
  77. ///
  78. /// 重置窗口
  79. ///
  80. public virtual void ResetWindow()
  81. {
  82. }
  83. ///
  84. /// 初始化窗口数据
  85. ///
  86. public virtual void InitWindowData()
  87. {
  88. if (windowData == null)
  89. windowData = new WindowData();
  90. }
  91. public virtual void ShowWindow()
  92. {
  93. }
  94. public virtual void HideWindow(Action action = null)
  95. {
  96. }
  97. public void HideWindowDirectly()
  98. {
  99. }
  100. public virtual void DestroyWindow()
  101. {
  102. }
  103. protected virtual void BeforeDestroyWindow()
  104. {
  105. }
  106. ///
  107. /// 界面在退出或者用户点击返回之前都可以注册执行逻辑
  108. ///
  109. protected void RegisterReturnLogic(BoolDelegate newLogic)
  110. {
  111. returnPreLogic = newLogic;
  112. }
  113. public bool ExecuteReturnLogic()
  114. {
  115. if (returnPreLogic == null)
  116. return false;
  117. else
  118. return returnPreLogic();
  119. }
  120. }

动画接口设计

界面可以继承该接口进行实现打开和关闭动画,设计成接口的目的是为了让动画功能更好的扩展,一个界面可以根据需求设置成多个编写的动画interface
  1. ///
  2. /// 窗口动画
  3. ///
  4. interface IWindowAnimation
  5. {
  6. ///
  7. /// 显示动画
  8. ///
  9. void EnterAnimation(EventDelegate.Callback onComplete);
  10. ///
  11. /// 隐藏动画
  12. ///
  13. void QuitAnimation(EventDelegate.Callback onComplete);
  14. ///
  15. /// 重置动画
  16. ///
  17. void ResetAnimation();
  18. }


窗口管理和导航设计实现

导航功能实现通过一个显示窗口堆栈实现,每次打开和关闭窗口通过判断窗口属性和类型更新处理BackSequence数据

·打开界面:将当前界面状态压入堆栈中更新BackSequence数据
·返回操作(主动关闭当前界面或者点击返回按钮):从堆栈中Pop出一个界面状态,将相应的界面重新打开
·怎么衔接:比如从一个界面没有回到上一个状态而是直接的跳转到其他的界面,这个时候需要将BackSequence清空因为当前的导航链已经被破坏,当BackSequence为空需要根据当前窗口指定的PreWindowId告知系统当从该界面返回,需要到达的指定页面,这样就能解决怎么衔接的问题,如果没断,继续执行导航,否则清空数据,根据PreWindowId进行导航


导航系统中关键性设计:

游戏中可以存在多个的Manager进行管理(一般在很少需求下才会使用),每个管理对象需要维护自己的导航信息BackSequence,每次退出一个界面需要检测当前退出的界面是否存在相应的Manager管理,如果存在则需要先执行Manager退出操作(退出过程分步进行)保证界面一层接着一层正确退出

窗口层级,Collider,统一背景添加如何实现?

有很多方式进行层级管理,该框架选择的方法如下

·设置三个常用层级Root,根据窗口类型在加载到游戏中时添加到对应的层级Root下面即可,每次添加重新计算设置层级(通过UIPanel的depth实现)保证每次打开一个新窗口层级显示正确,每次窗口内通过depth的大小区分层级关系
·根据窗口Collider和背景类型,在窗口的最小Panel上面添加Collider或者带有碰撞体的BackGround即可
Scene结构


分层Depth设置

存在相应的Manager管理,如果存在则需要先执行Manager退出操作(退出过程分步进行)保证界面一层接着一层正确退出
  1. private void AdjustBaseWindowDepth(UIBaseWindow baseWindow)
  2. {
  3. // 不同类型窗口添加不同的父节点下面
  4. // 根据当前父节点下面合法窗口的depth设置新打开窗口depth
  5. // 根据当前窗口背景和Collider模式,自动添加背景和碰撞
  6. UIWindowType windowType = baseWindow.windowData.windowType;
  7. int needDepth = 1;
  8. if (windowType == UIWindowType.Normal)
  9. {
  10. needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UINormalWindowRoot.gameObject, false) + 1, normalWindowDepth, int.MaxValue);
  11. Debug.Log("[UIWindowType.Normal] maxDepth is " + needDepth + baseWindow.GetID);
  12. }
  13. else if (windowType == UIWindowType.PopUp)
  14. {
  15. needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UIPopUpWindowRoot.gameObject) + 1, popUpWindowDepth, int.MaxValue);
  16. Debug.Log("[UIWindowType.PopUp] maxDepth is " + needDepth);
  17. }
  18. else if (windowType == UIWindowType.Fixed)
  19. {
  20. needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UIFixedWidowRoot.gameObject) + 1, fixedWindowDepth, int.MaxValue);
  21. Debug.Log("[UIWindowType.Fixed] max depth is " + needDepth);
  22. }
  23. if(baseWindow.MinDepth != needDepth)
  24. GameUtility.SetTargetMinPanel(baseWindow.gameObject, needDepth);
  25. baseWindow.MinDepth = needDepth;
  26. }
  27. ///
  28. /// 窗口背景碰撞体处理
  29. ///
  30. private void AddColliderBgForWindow(UIBaseWindow baseWindow)
  31. {
  32. UIWindowColliderMode colliderMode = baseWindow.windowData.colliderMode;
  33. if (colliderMode == UIWindowColliderMode.None)
  34. return;
  35. if (colliderMode == UIWindowColliderMode.Normal)
  36. GameUtility.AddColliderBgToTarget(baseWindow.gameObject, "Mask02", maskAtlas, true);
  37. if (colliderMode == UIWindowColliderMode.WithBg)
  38. GameUtility.AddColliderBgToTarget(baseWindow.gameObject, "Mask02", maskAtlas, false);
  39. }

多形态功能MessageBox实现

这个应该是项目中一定会用到的功能,说下该框架简单的实现
·三个按钮三种回调逻辑:左中右三个按钮,提供设置内容,设置回调函数的接口即可
·提供接口设置核心Content
·不同作用下不同的按钮不会隐藏和显示
  1. // 设置中间按钮信息和回调函数
  2. public void SetCenterBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
  3. {
  4. lbCenter.text = msg;
  5. NGUITools.SetActive(btnCenter, true);
  6. UIEventListener.Get(btnCenter).onClick = callBack;
  7. }
  8. // 设置左侧按钮信息和回调函数
  9. public void SetLeftBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
  10. {
  11. lbLeft.text = msg;
  12. NGUITools.SetActive(btnLeft, true);
  13. UIEventListener.Get(btnLeft).onClick = callBack;
  14. }
  15. // 设置右侧按钮信息和回调函数
  16. public void SetRightBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
  17. {
  18. lbRight.text = msg;
  19. NGUITools.SetActive(btnRight, true);
  20. UIEventListener.Get(btnRight).onClick = callBack;
  21. }

后续需要改进和增强计划

1、图集管理,针对大中型游戏对游戏内存要求苛刻的项目,一般都会对UI图集贴图资源进行动态管理,加载和卸载图集,保证UI贴图占用较少内存
2、增加一些通用处理:变灰操作,Mask遮罩(一般用于新手教程中)等
3、在进行切换的过程可以需要Load新场景需求,虽然这个也可以在UI框架外实现
4、对话系统也算是UI框架的功能,新手引导系统也可以加入到UI框架中,统一管理和处理新手引导逻辑

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/534307
推荐阅读
相关标签
  

闽ICP备14008679号