基于 Unity3D 的自主UI框架开发的思考
以下列举UI系统中的要点,脱离出引擎,保留设计思想。
结构
- UISystem 负责管理UIWnd
- UIWnd是所有UI的基类
- 拓展UI公用组件,模块间公用,例如公共弹窗,UIIcon等
接口设计
我觉得接口非常重要,即使不同项目之间,重用率也非常的高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public interface IBaseWindow { public void Show(); public void Hide(); public void Destroy(); public void BringTop(); public void SetDepth( int value); public System.Action OnClose; public System.Action OnFinshShow; } interface IWindowAnimation { void EnterAnimation(EventDelegate.Callback onComplete); void LeaveAnimation(EventDelegate.Callback onComplete); void ResetAnimation(); } public interface IStack { public void Clear(); public void Push(UIWnd wnd); public void Pop(); } |
UI层级管理
- 解决不同ENUM_LAYER_TYPE的层级问题
1 2 3 4 5 6 7 8 9 10 | enum ENUM_LAYER_TYPE { NONE, DEFAULT, //深度0 TOP, //深度1000 POPUP, //深度2000 GUIDE, //深度3000 LOADING, //深度4000 COUNT } |
假如:A是TOP类型(预设深度200),B是POPUP类型(预设深度200)
A的实际深度为1200,1000<2000
B的实际深度为2200,2000<3000< p="">
- 解决相同ENUM_LAYER_TYPE的层级问题
例如:同样是DEFAULT的A、B、C
首先创建A,自动分配深度为0
再创建B,自动分配深度为A的深度+1,B的深度为1
在创建C,自动分配深度为B的深度+1,C的深度为2
如果我希望A在最上层,因为他们被存入保存在同为DEFAULT的字典中,找出除了A以外最大深度N,把A的深度设为N+1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class UISystem : MonoBehaviour { Dictionary m_wndDic = new Dictionary(); public void BringTop( string value) { //找到m_wndDic 中除了A以外最大深度N,把A的深度设为N+1. } }
public class UIWnd : MonoBehaviour { enum ENUM_LAYER_TYPE LayerType = ENUM_LAYER_TYPE.NONE; int Depth =0; public void BringTop() { UISystem.Instance.BringTop(gameObject.name); } }
|
UI导航
例如:ABCDA顺序打开界面,然后一路返回,要求能回到A.
可以考虑用栈来管理,把UI分为2类:
入栈型:普通
非入栈型:常驻内存,例如UITop不会入栈
防错机制:对栈UI数量进行评估,超过N个,如何处理?例如只保留最上层,清除所有下层UI
分两种情况:
常规出入栈
清空栈,再入栈
1 2 3 4 5 6 7 8 9 | Stack m_uiStack = new Stack(); void Push(UIWnd wnd) { //在UI创建时会把非常驻型UI添加到这个栈中,不能重复添加同样的 }
public void Pop() { //当返回时会从栈中取对象并刷新UI } |
UI间的通讯
这两个使用起来比较顺手,另外可借用更多设计模式来解耦,例如外观模式。
- 消息分发,调用EventDispatch
各模块自己去监听消息
- P2P,UISystem.FindWindow()直接调用
从UISystem中查找目标UI,注意空引用
UI动画
主要指入场/出场动画,一般是根据UI的类型来确定动画效果,各模块不用自己去写。
我喜欢玩王者荣耀,它的UI动态效果让人感觉有活力、很舒服。像PVE界面还特意加强了动态UI。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | enum ENUM_WND_TYPE { NONE, POPUP, //弹出窗口 RETURN_FULL_SCREEN, //全屏有返回窗口 DONOT_RETURN_FULL_SCREEN, //全屏无返回窗口 GLOBAL, //全局窗口 COUNT }
public class UIWnd : MonoBehaviour { void onShow(ENUM_WND_TYPE type) { switch (type) { //具体的动画 //动画后的回调 } }
void onDestroy(ENUM_WND_TYPE type) { switch (type) { //具体的动画 //动画后的回调 } } } |
本地化方案
不考虑这些,等做海外版的时候就蛋碎了
涉及到图片、字符串、音频,必须考虑如何便捷的切换
字符串
所有用于UI显示的字符串不允许直接写在脚本里,也不要写在预设中,而是从XML或者EXCEL通过id读取
//例如我需要字符串"系统错误"
string str= TextSystem.Instance.GetText(101);
//TextSystem负责根据语言类型从xml中读取如果大量信息并且使用频率不高,可以考虑用html5的展示,给个外部链接。
参考王者荣耀的英雄攻略界面。IMG_4588.PNG
- 图片和音频
- 直接替换
- 尽量少用图片字体
特效
- 例如黑屏、高斯模糊、边缘模糊,一般非全屏窗口会带一个这样的底图效果
- 特效和UI系统结合时,会有一些裁剪、深度、缩放等问题,需要来解决
- 还需要测试特效对性能的影响,可以进行性能分档,低端机降低或屏蔽特效
搜集玩家操作信息
- UI框架最好能集成常规数据结构搜集的接口,这对运营非常的必要,例如大量玩家在XXX页面做了XXX操作后就流失了,而不是程序异常导致,基于这个行为来分析流失原因。
常规统计:
进入页面、退出页面、与服务器通讯操作、模块停留时长、切到后台和恢复的时间,以及角色数据相关的
我们经常谈DAU、MAU,却无法分析玩家流失原因、回归原因,基于这些数据来分析更靠谱一些。
总结
- 基于插件和需求去定制自己的UI框架,例如Unity的NGUI,itween,poolManager等
- 集成公用UI组件,提高开发效率
- 对性能的评估、资源的管理都需要提前文档规划和测试,对产品有整体把控
- UI热更新,需要提前定制自己的热更方案来应对版本迭代,产品运营期间非常重要
- 可视化编程,Unity编辑器扩展做到这点,对Debug和提高开发效率很有帮助,可看看UFrame
- 设计模式,例如常见的MVC、StrangeIoC、MVVM等,选定适合的提前做好UI开发模板,不然很难推广
- 收集玩家操作信息,分析玩家心理