• 游戏开发之UI管理器(跨引擎)


    使用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. 类图
      image.png

    将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层
    }
    

    初始化:
    image.png

    封装函数:

      /**
         * 将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)
    

    结语

    欢迎扫码关注公众号《微笑游戏》,浏览更多内容。
    微信图片_20190904220029.jpg

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

  • 相关阅读:
    几道算法题及学java心得
    css入门
    关于 移动端整屏切换专题 效果的思考
    css3实现卡牌旋转与物体发光效果
    九方格抽奖插件
    绑定弹窗事件最好的方法,原生JS和JQuery方法
    整屏滚动效果 jquery.fullPage.js插件+CSS3实现
    自定义 页面滚动条
    有趣的HTML5 CSS3效果
    CSS3 过渡与动画
  • 原文地址:https://www.cnblogs.com/cgw0827/p/13059866.html
Copyright © 2020-2023  润新知