使用UI管理器的目的

  1. 使用单场景与zindex结合的方式管理UI。
  2. 能够隐藏底层UI达到优化效果。
  3. 很好的组织和管理UI。
  4. 跨引擎使用。

管理器分类

根据以往经验我开发了三种类型的管理器,队列管理器,栈式管理器,单UI管理器。

  1. 单UI管理器:SingleManager负责管理如登录,loading,大厅,游戏这样的一级UI,同一时刻只有一个UI实例存在。UI之间是替换关系。
  2. 栈式管理器:StackManager用于管理先进后出的UI,弹出功能UI使用。
  3. 队列管理器:QueueManager用于管理先进先出的UI,用于第一次进入大厅弹出各种活动UI时候使用,关闭一个弹出另一个。
  4. 类图

将UI分为五层

  1. 第一层:使用单UI管理器用于管理,大厅,游戏等一级界面。

  2. 第二层:使用栈式管理器 管理二级界面

  3. 第三层:使用队列管理器用于管理进入游戏时弹出的各种活动面板。

  4. 第四层:使用栈式管理器用于管理toast,tip等提示框。

  5. 第五层:为最上层,使用栈式管理器,用于管理教学,对话界面和网络屏蔽层等。

    特别说明:比如将一个战斗UI分为战斗层和按钮层,这个不属于管理器范畴。

  6. 结构图

代码

  1. 为了跨引擎使用,需要将各个引擎的组件抽象。
export default interface LayerInterface {

    exit(): void;
/**
* 设置组件是否可见
* @param f
*/
setVisible(f: boolean): void;
/**
* 设置组件节点的zroder
* @param order
*/
setOrder(order: number): void;
/**
*
* @param t 管理器层级
*/
setUIIndex(t: number): void; getUIIndex(): number;
/**
* 获得组件的node
*/
getNode(): any;
isLoad(): boolean; }
  1. 管理器的父类
import LayerInterface from "./LayerInterface";
export default abstract class LayerManager { //根节点
protected root: any; protected list: LayerInterface[] //管理器中的内容是否可以被删除
protected popFlag: boolean = false; protected zOrder: number = 1; constructor(zOrder: number = 1, canPop: boolean = true) {
this.list = []
this.zOrder = zOrder;
this.popFlag = canPop;
}
init(node: any) {
this.root = node;
}
setZOrder(order: number) {
this.zOrder = order;
}
getZOrder(): number {
return this.zOrder;
}
canPop() {
return this.popFlag;
}
//ui数量
count() {
return this.list.length;
}
setVisible(flag: boolean) {
for (let index = 0; index < this.list.length; index++) {
const element = this.list[index];
element.setVisible(flag)
}
}
//判断某个ui是否存在
has(layer: LayerInterface) {
for (let index = 0; index < this.list.length; index++) {
const element = this.list[index];
if (layer === element) {
return true;
}
}
return false;
}
//添加layer
abstract pushView(layer: LayerInterface): void;
// 移除layer
abstract popView(view: LayerInterface): boolean;
//删除指定ui
removeView(layer: LayerInterface): boolean {
// logInfo(' LayerManger removeView ')
for (let index = 0; index < this.list.length; index++) {
const element: LayerInterface = this.list[index];
if (layer === element) {
element.exit();
this.list.splice(index, 1);
return true;
}
}
// console.warn(' removeView is not have ', layer, ' list ', this.list)
return false;
}
//清空所有ui
clear() {
// logInfo(' LayerManger clear ')
for (let index = 0; index < this.list.length; index++) {
const element: LayerInterface = this.list[index];
element.exit();
}
this.list.length = 0;
} }
  1. 单UI管理器
import LayerManager from "./LayerManager";
import LayerInterface from "./LayerInterface"; export default class SingleManager extends LayerManager { pushView(view: LayerInterface) {
if (this.list.length > 0) {
this.removeView(this.list.shift())
}
this.list.push(view);
view.setOrder(this.zOrder);
this.root.addChild(view.getNode()) } //不支持主动移除
popView(view: LayerInterface) {
return false;
} }
  1. 栈结构管理器
import LayerManager from "./LayerManager"
import LayerInterface from "./LayerInterface"
export default class StackLayerManager extends LayerManager { //添加layer
pushView(view: LayerInterface) {
this.list.push(view);
view.setOrder(this.zOrder)
this.root.addChild(view.getNode())
} // 移除layer
popView(view: LayerInterface): boolean {
if (this.list.length > 0) {
let layerInfo = this.list.pop();
layerInfo.exit();
return true;
} else {
return false;
}
} }
  1. 队列管理器
import LayerManager from "./LayerManager"
import LayerInterface from "./LayerInterface";
export default class QueueLayerManager extends LayerManager { //添加layer
pushView(view: LayerInterface) {
this.list.push(view);
if (this.list.length == 1) {
this.show(view);
}
} show(view: LayerInterface) {
view.setOrder(this.zOrder);
this.root.addChild(view.getNode())
}
// 移除layer
popView(view: any): boolean {
if (this.list.length > 0) {
let layerInfo = this.list.shift();
layerInfo.exit();
if (this.list.length > 0) {
this.show(this.list[0]);
}
return true;
} else {
return false;
}
} }
  1. 所有管理器的整合
import LayerManager from "./LayerManager"
import EventDispatcher from "../event/EventDispatcher";
import GlobalEvent from "../event/GlobalEvent";
import LayerInterface from "./LayerInterface"; export default class UIManager extends EventDispatcher { private managers: LayerManager[] = []; private root: any; private static ins: UIManager; static instance(): UIManager {
if (!UIManager.ins) {
UIManager.ins = new UIManager();
}
return UIManager.ins;
} constructor() {
super();
this.managers = [];
} /**
*
* @param uiIndex
* @param flag
*/
setVisible(uiIndex: number, flag: boolean) {
// logInfo('setVisible ', uiIndex, flag)
this.managers[uiIndex].setVisible(flag)
} init(node: any) {
this.root = node
} setManager(index: number, manager: LayerManager) {
this.managers[index] = manager;
this.managers[index].init(this.root)
} /**
* 清除UI
*/
clear() {
for (let index = this.managers.length - 1; index >= 0; index--) {
const manager = this.managers[index];
if (manager.canPop() && manager.count() > 0) {
manager.clear()
}
}
} //添加UI
pushView(layer: LayerInterface) {
if (layer) {
let uiIndex = layer.getUIIndex()
let manager = this.managers[uiIndex];
if (!manager) {
console.log(' manager is null ', layer)
return;
}
layer.setUIIndex(uiIndex)
manager.pushView(layer);
this.getOpenUICount();
}
} /**
* 此方法用于关闭界面,为了兼容Android的back键 所以layer有为null的情况。
* 如果 返回false 表明已经没有界面可以弹出,此时就可以弹出是否退出游戏提示了。
* @param layer
*/
popView(layer?: LayerInterface) {
// console.log('popView layer is ', layer)
let flag = false;
if (layer) {
let index: number = layer.getUIIndex()
// console.log(' popView index is ', index, ' layer is ', layer)
let manger = this.managers[index];
if (!manger) {
// console.log(' popView layer is not found type is ', type)
flag = false;
} else {
flag = manger.popView(layer);
}
} else {
for (let index = this.managers.length - 1; index >= 0; index--) {
const manager = this.managers[index];
if (manager.canPop() && manager.count() > 0) {
if (manager.popView(null)) {
flag = true;
break;
}
}
}
}
return flag;
}
//获得当前存在的ui的数量
getOpenUICount() {
let count: number = 0;
for (let index = this.managers.length - 1; index >= 0; index--) {
const manager = this.managers[index];
if (manager.canPop()) {
count += manager.count()
}
}
return count;
}
}
  1. 所有组件的父类
import LayerInterface from "../cfw/ui/LayerInterface";
import { UIIndex } from "../cfw/tools/Define"; const { ccclass, property } = cc._decorator; @ccclass
export default class EngineView extends cc.Component implements LayerInterface { private uiIndex: number = 0; protected loadFlag: boolean = false; isLoad() {
return this.loadFlag
}
start() {
this.loadFlag = true;
} exit() {
this.node.destroy()
} setVisible(f: boolean): void {
this.node.active = f;
} setOrder(order: number): void {
this.node.zIndex = order;
}
setUIIndex(t: UIIndex): void {
this.uiIndex = t;
} getUIIndex(): UIIndex {
return this.uiIndex
} getNode() {
return this.node;
} show() {
UIManager.instance().pushView(this)
} hide(){
UIManager.instance().popView(this)
} }
  1. 使用方式

    定义层级枚举
//ui分层
export enum UIIndex {
ROOT,//最底层
STACK,//堆栈管理
QUEUE,//队列管理
TOAST,//
TOP,//永远不会清除的ui层
}

初始化:

封装函数:

  /**
* 将ui添加到管理器中。
* @param prefab 预制体麓景
* @param className 组件类型
* @param model 模型
* @param uiIndex ui管理器层级
* @param loader 加载器
* @param func 加载成功回调
*/
pushView(prefab: string, className: string, c: BaseController, model: any, uiIndex: UIIndex = UIIndex.STACK, loader: ResLoader, func?: Function) {
if (this.viewMap.has(prefab)) {
console.warn(' pushVIew ', this.viewMap.has(prefab))
return;
}
this.viewMap.set(prefab, 1)
this.pushToast(prefab, className, c, model, uiIndex, loader, (comp) => {
// console.log(' delete viewMap ', prefab)
this.viewMap.delete(prefab)
if (func) {
func(comp) }
}) } /**
*
* @param prefabName 预制体的名称
* @param callback 加载后的回调。
*/
getPrefab(prefabName: string, loader: ResLoader, callback: (err: string, node?: cc.Node) => void) {
let resName = "";
if (prefabName.indexOf("/") >= 0) {
resName = prefabName;
let list = prefabName.split("/");
prefabName = list[list.length - 1];
} else {
resName = "prefabs/" + prefabName;
} loader.loadRes(resName, ResType.Prefab,
(err, item: ResItem) => {
if (err) {
callback(" UIManager getComponent err " + err);
return;
}
//这里可以配合对象池使用。
let node = cc.instantiate(item.getRes())
if (node) {
callback(null, node);
} else {
callback("node is null");
}
});
}

这里边有个一小的处理,就是当一个UI成功加载后才会弹出另一个UI,避免的按钮被连续点击弹出多个UI的情况。

使用:

this.pushView('LoginView', 'LoginView', null, ModuleManager.getLoader(), UIIndex.ROOT)

结语

欢迎扫码关注公众号《微笑游戏》,浏览更多内容。

欢迎扫码关注公众号《微笑游戏》,浏览更多内容。

游戏开发之UI管理器(跨引擎)的更多相关文章

  1. Cocos2d-x 3.x游戏开发之旅

    Cocos2d-x 3.x游戏开发之旅 钟迪龙 著   ISBN 978-7-121-24276-2 2014年10月出版 定价:79.00元 516页 16开 内容提要 <Cocos2d-x ...

  2. [整理]Unity3D游戏开发之Lua

    原文1:[Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(上) 各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我地博客地址是blog.csdn.net/qinyuanpei.如果 ...

  3. iOS游戏开发之UIDynamic

    iOS游戏开发之UIDynamic 简介 什么是UIDynamic UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架 可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象 ...

  4. 【转载】浅谈游戏开发之2D手游工具

    浅谈游戏开发之2D手游工具 来源:http://www.gameres.com/459713.html 游戏程序 平台类型: iOS Android  程序设计: 其它  编程语言:   引擎/SDK ...

  5. [Unity3D]Unity3D游戏开发之从Unity3D到Eclipse

    ---------------------------------------------------------------------------------------------------- ...

  6. Cocos2d—X游戏开发之CCToggle(菜单标签切换)CCControlSwitch(开关切换)

    Cocos2d—X游戏开发之CCToggle(菜单标签切换) 首先继承子CCMenu,是菜单标签中的一种.‘ class CC_DLL CCMenuItemToggle : public CCMenu ...

  7. [Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘终结篇:UniLua热更新全然解读

    ---------------------------------------------------------------------------------------------------- ...

  8. LayaBox进阶之UI管理器

    自己动手写框架的话,UI管理器是最基础的一部分: 打开界底层是addChild打开的:     新建一个UIManager export class UIManager { private mainC ...

  9. [Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(下)

    ---------------------------------------------------------------------------------------------------- ...

随机推荐

  1. python基础学习笔记(纸质)

    大一的时候学的python做的笔记.

  2. 「雕爷学编程」Arduino动手做(41)---激光接收管模块

    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...

  3. 微信小程序-视频弹幕的项目

    1.视频播放器 2.选择弹幕颜色 3.弹幕来了... 一般微信小程序需要配置.wxml.wxss.js.json文件,所有接下来也是要配置这几个文件,请看下图: 第一:  index.wxml < ...

  4. MySQL事务隔离之MVCC版本控制

    MVCC简介 MVCC是一种多版本并发控制机制. MVCC是为了解决什么问题? 大多数的MYSQL事务型存储引擎,如,InnoDB,Falcon以及PBXT都不使用一种简单的行锁机制.事实上,他们都和 ...

  5. 2.2 Go变量类型

    内置类型 值类型: bool 布尔类型 int(32 or 64), int8, int16, int32, int64 整数类型 uint(32 or 64), uint8(byte), uint1 ...

  6. Docker的安装(Linux)

    官网下载安装说明 1.卸载旧版本 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ doc ...

  7. 王艳 201771010127《面向对象程序设计(java)》第九周学习总结

    实验九 异常.断言与日志 实验时间 2018-10-25 1.实验目的与要求 (1) 掌握java异常处理技术: (2) 了解断言的用法: (3) 了解日志的用途: (4) 掌握程序基础调试技巧: 一 ...

  8. 朱刘算法 有向图定根的最小生成树poj3164

    关于为什么不能用Prim求解此类问题,如下 Prim可以看成是维护两个顶点集或者看成维护一颗不断生成的树(感觉前一种说法好一点) 倘若是有向图有三个顶点1.2.3 边的情况如下 1->2:    ...

  9. HDU6040 Hints of sd0061

    题目链接:https://vjudge.net/problem/HDU-6040 题目大意: 给出 \(n\) 个数,有 \(m\) 次询问,每次询问这 \(n\) 个数中第 \(k+1\) 大的数是 ...

  10. java第十二周课后作业0523

    1.编写一个程序,实现字符串大小写的转换并倒序输出.要求如下(1)使用for循环将字符串“ Hello world”从最后一个字符开始遍历(2)遍历的当前字符如果是大写字符,就使用 toLower C ...