honmono

游戏和计算机图形学

0%

Cocos UI框架

git 链接

1
https://github.com/kirikayakazuto/CocosCreator_UIFrameWork

0, 简单介绍

基于Cocos Creator的UI框架, 采用单场景+多预制体模式. 界面中的窗体都按需加载, 复用性高. 使用者只需要关心界面内的逻辑, 其他的
场景切换, 弹窗管理, 资源管理都可以交给框架.

对于游戏中的窗体, 可以大致分为5类.

  • Screen
  • Fixed
  • Window
  • Toast
  • Tips

以下图为例

Screen可以理解为场景, 一般是会铺满屏幕. 如上图黄色框中的地图.

Fixed则是一下固定在场景屏幕边缘的功能性UI, 如上图红色框中的两个按钮.

Window则是游戏中的各种弹出窗体, 一般会有一个弹出动画. 如上图蓝色框中的面板.

Toast 一个的窗体同时出现多个.

Tips则是一些提示性窗体, 比如断线提示窗体. 这种窗体的特点是不受其他窗体的影响, 只管自己显示和隐藏.

tips: Screen窗体切换时会隐藏当前显示的Fixed, Window窗体, 以到达切换场景的效果.

1. 项目结构

  • AutoScripts: 是插件AutoBinder生成的代码. (待实现: 自动清理未被使用的文件)
  • Common: 一些公共的脚本文件, 有工具类, 和一些封装好的组件.
  • Logic: 用户自定义的管理类, 如ConfigMgr
  • types: 提供wx小游戏的api提示
  • UIFrame: 框架核心脚本
  • UIScript: 用户自定义的界面控制脚本
  • Main.ts: 类似入口脚本

2. 启动流程

打开Main场景, 在结点树上可以看到有一个Scene结点



Scene结点很关键, 它作为框架的UI层级的父节点, 所以如果你有其他独立与UI框架的层级的需求时,可以根据Scene结点的层级做调整.

Scene结点上挂载了Scene脚本, 打开这个脚本可以看到onGameInit方法, 这个方法控制游戏的初始化流程. 流程如下

1
2
3
4
5
6
7
8
9
// 第一步 展示loading页面,当然有些默认就是loading页面, 可以把loading界面做在场景中, 遮挡住Scene结点.

// 第二步 初始化游戏(Managers,Configs,SDKs)
await Game.init(this.node);
// 第三步 构建初始场景(加载必要的prefab,音频,texture)
await ResMgr.loadRes() 或者 await UIManager.getInstance().loadForm() 等等
// 第四步 加载主界面UI,关掉loading页面,正式进入游戏
await SceneMgr.open("");
this.loading.destory();// 假设你已经获取到了loading ui

Game.ts脚本则是作为游戏的逻辑层控制器. 打开可以开到init方法, 这个方法控制游戏内控制器的初始化.

1
2
3
4
5
6
7
// 初始化Manager, 例如new ConfigMgr();
this.configMgr = new ConfigMgr();
this.playerMgr = new PlayerMgr();
// 初始化平台sdk

// 加载配置信息, 因为在这里就已经加装完毕配置信息了, 所以后面加装主界面UI时, 就可以读取相关配置了
await this.configMgr.loadConfigs();

Game.ts脚本还可以控制逻辑控制器的时间更新, 打开update方法.

1
2
if(!this.inited) return ;
Task.update(dt); // 更新任务进度

3. 框架结构

采用UIBase + UIManager两个核心类管理整个框架, 其他的Manager辅助UIManager进行管理.

UIBase篇

UIBase中定义窗体的属性和一系列的生命周期方法

1
2
3
formType: FormType;           // 窗体类型
modalType: ModalType; // 模糊层类型
closeType: ECloseType; // 关闭类型, CloseAndDestroy, CloseAndHide, LRU(使用LRU cache控制窗体销毁时机)

实际项目中不要直接继承UIBase, 请继承它的子类 UIScreen, UIFixed, UIWindow, UITips. 子类中预实现了一些功能.

生命周期方法

1
2
3
4
5
6
7
8
9
async load(): string;                         // 只调用一次, 异步方法, 返回一个错误信息.
onInit(): void; // 只调用一次, 初始化.
onShow(): void; // 每次显示时调用.
onload(): void; // cocos提供
start(): void; // cocos提供
onAfterShow(): void; // 显示动画结束后调用
onHide(): void; // 隐藏时调用
onAfterHide(): void; // 隐藏动画结束后调用
onDestory(): void; // cocos提供

显示和隐藏动画, 使用者可以重写下面两个方法, 实现自定义的显示隐藏动画.

1
2
async showEffect(): void;                      // 窗体显示动画
async hideEffect(): void; // 窗体隐藏动画

UIManager篇

UIManager在getInstance()时, 会自动的创建UIROOT和四个子节点. 结构如下图



这四个子节点对应的UIBase中的四种类型, 对应类型的窗体会被挂载到对应结点下.

UIManager只暴露的四个接口.

1
2
3
4
loadForm(path: string): UIBase;                                       // 预加载窗体
openForm(path: string, param: any, formData: IFormData): UIBase; // 打开窗体
closeForm(path: string): UIBase; // 隐藏窗体
getForm(path: string): UIBase; // 获得窗体

UIManager在打开窗体时, 会获取窗体上的UIBase组件, 然后根据类型, 将窗体挂载到不同的node上. 然后触发UIBase中的
生命周期方法.

UIManager内部控制流程如下图..

loadForm和openForm复用同一个加装回调, 所以不用担心预加载窗体后, 还没等预加载结束就调用openForm导致的加装浪费.

其他Manager

WindowMgr 窗体控制器

UIManager中其实已经通过栈结构实现了简单的窗体控制, 但是对于一些复杂的控制效果, 则交给WindowMgr实现.
比如优先栈和等待栈, 优先栈指的 每一个窗体有一个优先级, 根据优先级的不同在优先栈会有不同的排序效果, 从而影响窗体弹出顺序.
等待栈指的

1
2
WindowMgr.open(prefabPath: string, params?: any, formData: IFormData = {showWait: false, priority: EPriority.FIVE});														 // 打开, formData中 showWait表示是否进入等待队列, priority表示优先级.
WindowMgr.close(prefabPath: string); // 关闭

SceneMgr 场景控制器

1
2
3
4
// 打开场景
SceneMgr.open(scenePath: string, params?: any, formData?: IFormData);
// 退回上一个场景
SceneMgr.back(params?: any, formData?: IFormData);

ResMgr 资源控制器

ResMgr管理窗体的资源, 恪守 我释放的是我加载的资源, 我加载的资源会被释放.

不建议用户直接使用这个Manager. 里面主要是加载窗体,和动态资源.

1
2
ResMgr.inst.loadDynamicRes(url: string, type: typeof cc.Asset, tag: string);		// 加载动态资源, 内部使用
// 上面的方法请通过这个方法调用 UIBase.loadRes(url: string, type?: typeof cc.Asset);

AdapterMgr 适配控制器

对窗体进行位置适配, 比如对于screen类型的窗体, 框架默认为它添加了全屏的适配, 如下

1
AdapterMgr.inst.adapteByType(AdapterType.StretchHeight | AdapterType.StretchWidth, this.node);