目录

  • 前言
  •       一、新建UIMgr.ts
  •       二、提问AI
  •       三、优化修改
  •       四、最终版本
  •       五、总结
  • 我们接之前【资源模块】,现在继续使用AI帮我们写一个【UI管理器】。

    前言

    UI管理器,主要提供UI界面的创建、显示、隐藏、销毁。
    UI管理器是我们游戏中很常用的功能,比如:登录UI、大厅UI、游戏中的人物背包UI、游戏设置UI、掉线时出现的警告UI等等。
    我把游戏中的UI简单分为2种:一种是场景UI、一种是窗口UI,我喜欢称呼场景UI为大UI,窗口UI为小UI。比如:我们把游戏看成一个 web 网站,其中【用户登录页面】就等于【登录UI】也就是场景UI,如果登录失败时出现的【错误提示框】就等于【提示UI】也就是窗口UI。
    UI示意图

    小白必读:UI管理器的用处?
    :UI管理器能方便的统一管理我们游戏中各种UI,哪些UI需要常驻保留,提高复用、哪些UI需要用后销毁,减少开销。也能让我们在显示UI时赋予动画效果等等。在我们的框架中UI的类型都是预制体,符合我们【单场景+多预制体】的开发模式。

    一、新建UIMgr.ts

    我们继续在Core分包目录下的Scripts\Managers目录新建UIMgr.ts
    新建UIMgr.ts

    二、提问AI

    注意:现在我们需要结合ResMgr.ts的资源加载读取,来写代码。
    1、点击提问区的 + 号
    2、选择ResMgr.ts
    3、输入问题

    结合ResMgr.ts,用ccc 3.8.x 内置 API 帮我们写一个UIMgr.tsUI管理器,实现:
    1UI界面的创建、显示、隐藏、销毁
    2、创建时可以根据参数设置UI是否缓存,提高复用
    3UI属于预制体类型
    4、在 constructor 中 使用 director.once的Director.EVENT_AFTER_SCENE_LAUNCH事件执行init
    5、在init中获取场景Canvas根节点作为UI父节点,但在编辑器模式下不执行
    5、参考ResMgr单例方式,并导出

    提问AI1

    三、优化修改

    从上图种我们看到有几个地方需要修改:
    1、我们的UI是预制体,并且存放于各个分包里,而不是单独的 ui 分包
    2、prefabPath 参数应该包含分包名称
    3、showUI里应该加一个如果没有获取到缓存就直接使用createUI新建
    4、注释说明不完整
    优化修改1

    四、最终版本

    import { Node, Prefab, instantiate, director, isValid, Director } from 'cc';
    import { resMgr } from './ResMgr';
    import { EDITOR } from 'cc/env';
    
    /**
     * UI管理器
     * 用于UI界面的创建、显示、隐藏、销毁
     */
    export class UIMgr {
        private uiRoot: Node | null = null;
        private uiCache: Map<string, Node> = new Map();
    
        /** 单例实例 */
        public static readonly instance: UIMgr = new UIMgr();
    
        // 私有构造函数,防止外部实例化
        private constructor() {
            director.once(Director.EVENT_AFTER_SCENE_LAUNCH, this.init, this);
        }
    
        /**
         * 初始化UI管理器
         * 在非编辑器模式下,获取场景中的Canvas节点作为UI的父节点
         */
        private init() {
            if (!EDITOR) {
                this.uiRoot = director.getScene().getChildByName('Canvas')!;
            }
        }
    
        /**
         * 创建UI界面
         * @param {string} prefabPath - 预制体路径,格式为 "bundleName/prefabPath"
         * @param {boolean} cache - 是否缓存UI节点
         * @param {Function} callback - 创建完成时的回调函数
         */
        public createUI(prefabPath: string, cache: boolean = true, callback: (err: Error | null, uiNode: Node | null) => void) {
            if (this.uiCache.has(prefabPath)) {
                callback(null, this.uiCache.get(prefabPath)!);
                return;
            }
    
            const [bundleName, path] = prefabPath.split('/', 2);
    
            resMgr.loadAsset(bundleName, path, Prefab, (err, prefab) => {
                if (err) {
                    callback(err, null);
                    return;
                }
    
                const uiNode = instantiate(prefab);
                if (this.uiRoot && isValid(this.uiRoot)) {
                    this.uiRoot.addChild(uiNode);
                }
    
                if (cache) {
                    this.uiCache.set(prefabPath, uiNode);
                }
    
                callback(null, uiNode);
            });
        }
    
        /**
         * 显示UI界面
         * 如果UI未缓存,则创建新的UI
         * @param {string} prefabPath - 预制体路径
         */
        public showUI(prefabPath: string) {
            const uiNode = this.uiCache.get(prefabPath);
            if (uiNode && isValid(uiNode)) {
                uiNode.active = true;
            } else {
                this.createUI(prefabPath, true, (err, newUiNode) => {
                    if (!err && newUiNode) {
                        newUiNode.active = true;
                    }
                });
            }
        }
    
        /**
         * 隐藏UI界面
         * @param {string} prefabPath - 预制体路径
         */
        public hideUI(prefabPath: string) {
            const uiNode = this.uiCache.get(prefabPath);
            if (uiNode && isValid(uiNode)) {
                uiNode.active = false;
            }
        }
    
        /**
         * 销毁UI界面
         * @param {string} prefabPath - 预制体路径
         */
        public destroyUI(prefabPath: string) {
            const uiNode = this.uiCache.get(prefabPath);
            if (uiNode && isValid(uiNode)) {
                uiNode.destroy();
                this.uiCache.delete(prefabPath);
            }
        }
    }
    
    /** 导出实例 */
    export const uiMgr = UIMgr.instance;

    五、总结

    这里呢我们没有区分场景UI和窗口UI,也没有设置UI的层级,只是简单的通过创建、显示、隐藏、销毁达到管理UI的目的,在以后的实际开发中我们会对UI管理器进行扩展。

    作者:ccs2d.com  创建时间:2024-09-30 04:44
    最后编辑:ccs2d.com  更新时间:2024-10-06 09:43
    上一篇:
    下一篇: