使用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. 为了跨引擎使用,需要将各个引擎的组件抽象。
  1. export default interface LayerInterface {
  2. exit(): void;
  3. /**
  4. * 设置组件是否可见
  5. * @param f
  6. */
  7. setVisible(f: boolean): void;
  8. /**
  9. * 设置组件节点的zroder
  10. * @param order
  11. */
  12. setOrder(order: number): void;
  13. /**
  14. *
  15. * @param t 管理器层级
  16. */
  17. setUIIndex(t: number): void;
  18. getUIIndex(): number;
  19. /**
  20. * 获得组件的node
  21. */
  22. getNode(): any;
  23. isLoad(): boolean;
  24. }
  1. 管理器的父类
  1. import LayerInterface from "./LayerInterface";
  2. export default abstract class LayerManager {
  3. //根节点
  4. protected root: any;
  5. protected list: LayerInterface[]
  6. //管理器中的内容是否可以被删除
  7. protected popFlag: boolean = false;
  8. protected zOrder: number = 1;
  9. constructor(zOrder: number = 1, canPop: boolean = true) {
  10. this.list = []
  11. this.zOrder = zOrder;
  12. this.popFlag = canPop;
  13. }
  14. init(node: any) {
  15. this.root = node;
  16. }
  17. setZOrder(order: number) {
  18. this.zOrder = order;
  19. }
  20. getZOrder(): number {
  21. return this.zOrder;
  22. }
  23. canPop() {
  24. return this.popFlag;
  25. }
  26. //ui数量
  27. count() {
  28. return this.list.length;
  29. }
  30. setVisible(flag: boolean) {
  31. for (let index = 0; index < this.list.length; index++) {
  32. const element = this.list[index];
  33. element.setVisible(flag)
  34. }
  35. }
  36. //判断某个ui是否存在
  37. has(layer: LayerInterface) {
  38. for (let index = 0; index < this.list.length; index++) {
  39. const element = this.list[index];
  40. if (layer === element) {
  41. return true;
  42. }
  43. }
  44. return false;
  45. }
  46. //添加layer
  47. abstract pushView(layer: LayerInterface): void;
  48. // 移除layer
  49. abstract popView(view: LayerInterface): boolean;
  50. //删除指定ui
  51. removeView(layer: LayerInterface): boolean {
  52. // logInfo(' LayerManger removeView ')
  53. for (let index = 0; index < this.list.length; index++) {
  54. const element: LayerInterface = this.list[index];
  55. if (layer === element) {
  56. element.exit();
  57. this.list.splice(index, 1);
  58. return true;
  59. }
  60. }
  61. // console.warn(' removeView is not have ', layer, ' list ', this.list)
  62. return false;
  63. }
  64. //清空所有ui
  65. clear() {
  66. // logInfo(' LayerManger clear ')
  67. for (let index = 0; index < this.list.length; index++) {
  68. const element: LayerInterface = this.list[index];
  69. element.exit();
  70. }
  71. this.list.length = 0;
  72. }
  73. }
  1. 单UI管理器
  1. import LayerManager from "./LayerManager";
  2. import LayerInterface from "./LayerInterface";
  3. export default class SingleManager extends LayerManager {
  4. pushView(view: LayerInterface) {
  5. if (this.list.length > 0) {
  6. this.removeView(this.list.shift())
  7. }
  8. this.list.push(view);
  9. view.setOrder(this.zOrder);
  10. this.root.addChild(view.getNode())
  11. }
  12. //不支持主动移除
  13. popView(view: LayerInterface) {
  14. return false;
  15. }
  16. }
  1. 栈结构管理器
  1. import LayerManager from "./LayerManager"
  2. import LayerInterface from "./LayerInterface"
  3. export default class StackLayerManager extends LayerManager {
  4. //添加layer
  5. pushView(view: LayerInterface) {
  6. this.list.push(view);
  7. view.setOrder(this.zOrder)
  8. this.root.addChild(view.getNode())
  9. }
  10. // 移除layer
  11. popView(view: LayerInterface): boolean {
  12. if (this.list.length > 0) {
  13. let layerInfo = this.list.pop();
  14. layerInfo.exit();
  15. return true;
  16. } else {
  17. return false;
  18. }
  19. }
  20. }
  1. 队列管理器
  1. import LayerManager from "./LayerManager"
  2. import LayerInterface from "./LayerInterface";
  3. export default class QueueLayerManager extends LayerManager {
  4. //添加layer
  5. pushView(view: LayerInterface) {
  6. this.list.push(view);
  7. if (this.list.length == 1) {
  8. this.show(view);
  9. }
  10. }
  11. show(view: LayerInterface) {
  12. view.setOrder(this.zOrder);
  13. this.root.addChild(view.getNode())
  14. }
  15. // 移除layer
  16. popView(view: any): boolean {
  17. if (this.list.length > 0) {
  18. let layerInfo = this.list.shift();
  19. layerInfo.exit();
  20. if (this.list.length > 0) {
  21. this.show(this.list[0]);
  22. }
  23. return true;
  24. } else {
  25. return false;
  26. }
  27. }
  28. }
  1. 所有管理器的整合
  1. import LayerManager from "./LayerManager"
  2. import EventDispatcher from "../event/EventDispatcher";
  3. import GlobalEvent from "../event/GlobalEvent";
  4. import LayerInterface from "./LayerInterface";
  5. export default class UIManager extends EventDispatcher {
  6. private managers: LayerManager[] = [];
  7. private root: any;
  8. private static ins: UIManager;
  9. static instance(): UIManager {
  10. if (!UIManager.ins) {
  11. UIManager.ins = new UIManager();
  12. }
  13. return UIManager.ins;
  14. }
  15. constructor() {
  16. super();
  17. this.managers = [];
  18. }
  19. /**
  20. *
  21. * @param uiIndex
  22. * @param flag
  23. */
  24. setVisible(uiIndex: number, flag: boolean) {
  25. // logInfo('setVisible ', uiIndex, flag)
  26. this.managers[uiIndex].setVisible(flag)
  27. }
  28. init(node: any) {
  29. this.root = node
  30. }
  31. setManager(index: number, manager: LayerManager) {
  32. this.managers[index] = manager;
  33. this.managers[index].init(this.root)
  34. }
  35. /**
  36. * 清除UI
  37. */
  38. clear() {
  39. for (let index = this.managers.length - 1; index >= 0; index--) {
  40. const manager = this.managers[index];
  41. if (manager.canPop() && manager.count() > 0) {
  42. manager.clear()
  43. }
  44. }
  45. }
  46. //添加UI
  47. pushView(layer: LayerInterface) {
  48. if (layer) {
  49. let uiIndex = layer.getUIIndex()
  50. let manager = this.managers[uiIndex];
  51. if (!manager) {
  52. console.log(' manager is null ', layer)
  53. return;
  54. }
  55. layer.setUIIndex(uiIndex)
  56. manager.pushView(layer);
  57. this.getOpenUICount();
  58. }
  59. }
  60. /**
  61. * 此方法用于关闭界面,为了兼容Android的back键 所以layer有为null的情况。
  62. * 如果 返回false 表明已经没有界面可以弹出,此时就可以弹出是否退出游戏提示了。
  63. * @param layer
  64. */
  65. popView(layer?: LayerInterface) {
  66. // console.log('popView layer is ', layer)
  67. let flag = false;
  68. if (layer) {
  69. let index: number = layer.getUIIndex()
  70. // console.log(' popView index is ', index, ' layer is ', layer)
  71. let manger = this.managers[index];
  72. if (!manger) {
  73. // console.log(' popView layer is not found type is ', type)
  74. flag = false;
  75. } else {
  76. flag = manger.popView(layer);
  77. }
  78. } else {
  79. for (let index = this.managers.length - 1; index >= 0; index--) {
  80. const manager = this.managers[index];
  81. if (manager.canPop() && manager.count() > 0) {
  82. if (manager.popView(null)) {
  83. flag = true;
  84. break;
  85. }
  86. }
  87. }
  88. }
  89. return flag;
  90. }
  91. //获得当前存在的ui的数量
  92. getOpenUICount() {
  93. let count: number = 0;
  94. for (let index = this.managers.length - 1; index >= 0; index--) {
  95. const manager = this.managers[index];
  96. if (manager.canPop()) {
  97. count += manager.count()
  98. }
  99. }
  100. return count;
  101. }
  102. }
  1. 所有组件的父类
  1. import LayerInterface from "../cfw/ui/LayerInterface";
  2. import { UIIndex } from "../cfw/tools/Define";
  3. const { ccclass, property } = cc._decorator;
  4. @ccclass
  5. export default class EngineView extends cc.Component implements LayerInterface {
  6. private uiIndex: number = 0;
  7. protected loadFlag: boolean = false;
  8. isLoad() {
  9. return this.loadFlag
  10. }
  11. start() {
  12. this.loadFlag = true;
  13. }
  14. exit() {
  15. this.node.destroy()
  16. }
  17. setVisible(f: boolean): void {
  18. this.node.active = f;
  19. }
  20. setOrder(order: number): void {
  21. this.node.zIndex = order;
  22. }
  23. setUIIndex(t: UIIndex): void {
  24. this.uiIndex = t;
  25. }
  26. getUIIndex(): UIIndex {
  27. return this.uiIndex
  28. }
  29. getNode() {
  30. return this.node;
  31. }
  32. show() {
  33. UIManager.instance().pushView(this)
  34. }
  35. hide(){
  36. UIManager.instance().popView(this)
  37. }
  38. }
  1. 使用方式

    定义层级枚举
  1. //ui分层
  2. export enum UIIndex {
  3. ROOT,//最底层
  4. STACK,//堆栈管理
  5. QUEUE,//队列管理
  6. TOAST,//
  7. TOP,//永远不会清除的ui层
  8. }

初始化:

封装函数:

  1. /**
  2. * 将ui添加到管理器中。
  3. * @param prefab 预制体麓景
  4. * @param className 组件类型
  5. * @param model 模型
  6. * @param uiIndex ui管理器层级
  7. * @param loader 加载器
  8. * @param func 加载成功回调
  9. */
  10. pushView(prefab: string, className: string, c: BaseController, model: any, uiIndex: UIIndex = UIIndex.STACK, loader: ResLoader, func?: Function) {
  11. if (this.viewMap.has(prefab)) {
  12. console.warn(' pushVIew ', this.viewMap.has(prefab))
  13. return;
  14. }
  15. this.viewMap.set(prefab, 1)
  16. this.pushToast(prefab, className, c, model, uiIndex, loader, (comp) => {
  17. // console.log(' delete viewMap ', prefab)
  18. this.viewMap.delete(prefab)
  19. if (func) {
  20. func(comp)
  21. }
  22. })
  23. }
  24. /**
  25. *
  26. * @param prefabName 预制体的名称
  27. * @param callback 加载后的回调。
  28. */
  29. getPrefab(prefabName: string, loader: ResLoader, callback: (err: string, node?: cc.Node) => void) {
  30. let resName = "";
  31. if (prefabName.indexOf("/") >= 0) {
  32. resName = prefabName;
  33. let list = prefabName.split("/");
  34. prefabName = list[list.length - 1];
  35. } else {
  36. resName = "prefabs/" + prefabName;
  37. }
  38. loader.loadRes(resName, ResType.Prefab,
  39. (err, item: ResItem) => {
  40. if (err) {
  41. callback(" UIManager getComponent err " + err);
  42. return;
  43. }
  44. //这里可以配合对象池使用。
  45. let node = cc.instantiate(item.getRes())
  46. if (node) {
  47. callback(null, node);
  48. } else {
  49. callback("node is null");
  50. }
  51. });
  52. }

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

使用:

  1. 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. HttpRequestUtils post get请求

    package com.nextjoy.projects.usercenter.util.http; /** * Created by Administrator on 2016/10/20. */ ...

  2. .Net Core3.0 WebApi 项目框架搭建 四:JWT权限验证

    .Net Core3.0 WebApi 项目框架搭建:目录 什么是JWT 根据维基百科定义,JWT(读作 [/dʒɒt/]),即JSON Web Tokens,是一种基于JSON的.用于在网络上声明某 ...

  3. .net core 3.1 使用nlog记录日志 NLog.Web.AspNetCore

    背景 .net core 中已经集成了log的方法, 但是只能控制台输出不能写入文件等等. 常见第三方的的日志工具包括log4net, nlog等等, 本文介绍nlog 一. 引用程序集, nuget ...

  4. 利用logrotate将mysql log截断

    https://blog.pythian.com/mysql-log-rotation/ 1.授权用户 CREATE USER 'log_rotate'@'localhost' IDENTIFIED ...

  5. 教你避过安装TensorFlow的两个坑

    TensorFlow作为著名机器学习相关的框架,很多小伙伴们都可能要安装它.WIN+R,输入cmd运行后,通常可能就会pip install tensorflow直接安装了,但是由于这个库比较大,接近 ...

  6. Kattis - entertainmentbox

    题目链接:https://vjudge.net/problem/Kattis-entertainmentbox 题目大意: 一种叫做不知道什么的盒子可以同时录 k 个节目,现给出 n 个节目的开始和结 ...

  7. 纯python自研接口自动化脚本更新版本,让小白也能实现0到1万+的接口自动化用例

    查看完整文章点击原文链接:纯python自研接口自动化脚本更新版本,让小白也能实现0到1万+的接口自动化用例 你是否还在用postman\jmeter做接口自动化吗?用python的开源框架[unit ...

  8. thymeleaf抛出项目上下文ServletContext ,session,request等信息

    @RequestMapping("/alls") public String allsinfo(HttpSession session, HttpServletRequest re ...

  9. Spring笔记 - 组件注册

    @Bean:类注入容器 xml方式: <bean id="person" class="com.hrh.bean.Person"> <prop ...

  10. nginx: [error] invalid PID number "" in ...

    1.查看进程 ps -ef|grep nginx 2.进入nginx安装目录sbin下,执行命令: ./nginx -t 如下显示: syntax is ok test is successful 3 ...