基于 Unity3D 的自主UI框架开发的思考

发表于2017-02-06
评论2 7.6k浏览

以下列举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,1000B的实际深度为2200,2000

  • 解决相同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开发模板,不然很难推广
  • 收集玩家操作信息,分析玩家心理

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引