import { Node, Prefab, instantiate } from 'cc'; import { UIBase } from './UIBase'; import { ResMgr } from '../ResMgr/ResMgr'; /** * UI管理器 * 职责: * - 管理所有UI的加载、显示、隐藏和销毁 * - 维护UI缓存,避免重复加载 * - 管理UI的生命周期 * - 提供UI更新机制 */ export class UIMgr { private static _instance: UIMgr; /** 已加载的UI实例缓存 */ private _uiCache: Map = new Map(); /** UI父节点(Canvas) */ private _uiRoot: Node | null = null; /** 需要更新的UI列表 */ private _updateList: UIBase[] = []; /** * 获取单例 */ static getInstance(): UIMgr { if (!this._instance) { this._instance = new UIMgr(); } return this._instance; } /** * 设置UI根节点 * @param root Canvas节点或UI容器节点 * @example * const canvas = find('Canvas'); * if (canvas) { * UIMgr.getInstance().setUIRoot(canvas); * } */ setUIRoot(root: Node): void { this._uiRoot = root; console.log('[UIMgr] 设置UI根节点'); } /** * 加载并显示UI * @param uiClass UI类 * @param params 传递给onStart的参数 * @returns UI实例 * @example * const loginUI = await UIMgr.getInstance().load(UILogin, { username: 'test' }); */ async load( uiClass: new () => T, params?: any ): Promise { const className = uiClass.name; // 检查缓存 let ui = this._uiCache.get(className) as T; if (ui) { console.log(`[UIMgr] 从缓存加载UI: ${className}`); ui.show(); if (ui.onStart) { await ui.onStart(params); } return ui; } // 创建UI实例 ui = new uiClass(); // 获取UI资源路径 const url = ui.onGetUrl(); if (!url) { console.error(`[UIMgr] UI未定义资源路径: ${className}`); throw new Error(`UI未定义资源路径: ${className}`); } console.log(`[UIMgr] 开始加载UI: ${className} (${url})`); // 通过ResMgr加载预制体 try { const prefab = await ResMgr.getInstance().load( 'resources', url, Prefab ); // 实例化预制体 const node = instantiate(prefab); // 添加到UI根节点 if (this._uiRoot) { node.setParent(this._uiRoot); } else { console.warn(`[UIMgr] UI根节点未设置,UI将无父节点: ${className}`); } // 设置UI节点 ui.setNode(node); // 缓存UI this._uiCache.set(className, ui); // 如果UI有onUpdate方法,添加到更新列表 if (ui.onUpdate) { this._updateList.push(ui); } // 调用onStart生命周期 if (ui.onStart) { await ui.onStart(params); } // 显示UI ui.show(); console.log(`[UIMgr] UI加载完成: ${className}`); return ui; } catch (error) { console.error(`[UIMgr] 加载UI失败: ${className}`, error); throw error; } } /** * 卸载UI * @param uiClass UI类或类名 * @example * UIMgr.getInstance().unload(UILogin); * // 或 * UIMgr.getInstance().unload('UILogin'); */ unload(uiClass: (new () => UIBase) | string): void { const className = typeof uiClass === 'string' ? uiClass : uiClass.name; const ui = this._uiCache.get(className); if (!ui) { console.warn(`[UIMgr] UI未加载,无法卸载: ${className}`); return; } console.log(`[UIMgr] 卸载UI: ${className}`); // 调用onEnd生命周期 if (ui.onEnd) { ui.onEnd(); } // 从更新列表移除 const index = this._updateList.indexOf(ui); if (index !== -1) { this._updateList.splice(index, 1); } // 销毁节点 const node = ui.getNode(); if (node && node.isValid) { node.destroy(); } // 从缓存移除 this._uiCache.delete(className); // 释放资源 const url = ui.onGetUrl(); if (url) { ResMgr.getInstance().release('resources', url); } } /** * 获取已加载的UI实例 * @param uiClass UI类或类名 * @returns UI实例,如果未加载则返回null * @example * const loginUI = UIMgr.getInstance().get(UILogin); * if (loginUI) { * loginUI.show(); * } */ get(uiClass: (new () => T) | string): T | null { const className = typeof uiClass === 'string' ? uiClass : uiClass.name; return (this._uiCache.get(className) as T) || null; } /** * 检查UI是否已加载 * @param uiClass UI类或类名 * @returns true表示已加载,false表示未加载 * @example * if (UIMgr.getInstance().has(UILogin)) { * console.log('登录UI已加载'); * } */ has(uiClass: (new () => UIBase) | string): boolean { const className = typeof uiClass === 'string' ? uiClass : uiClass.name; return this._uiCache.has(className); } /** * 更新所有需要更新的UI * 应在主循环中每帧调用 * @param dt 距离上一帧的时间(秒) * @example * // 在主场景的update方法中调用 * update(dt: number) { * UIMgr.getInstance().update(dt); * } */ update(dt: number): void { for (const ui of this._updateList) { if (ui.isShowing() && ui.onUpdate) { ui.onUpdate(dt); } } } /** * 卸载所有UI * @example * // 在场景切换或退出游戏时调用 * UIMgr.getInstance().unloadAll(); */ unloadAll(): void { console.log(`[UIMgr] 卸载所有UI`); const classNames = Array.from(this._uiCache.keys()); for (const className of classNames) { this.unload(className); } } /** * 获取当前已加载UI的数量 * @returns UI数量 */ getUICount(): number { return this._uiCache.size; } /** * 显示指定UI(不重新加载) * @param uiClass UI类或类名 * @example * UIMgr.getInstance().show(UILogin); */ show(uiClass: (new () => UIBase) | string): void { const className = typeof uiClass === 'string' ? uiClass : uiClass.name; const ui = this._uiCache.get(className); if (ui) { ui.show(); } else { console.warn(`[UIMgr] UI未加载,无法显示: ${className}`); } } /** * 隐藏指定UI(不卸载) * @param uiClass UI类或类名 * @example * UIMgr.getInstance().hide(UILogin); */ hide(uiClass: (new () => UIBase) | string): void { const className = typeof uiClass === 'string' ? uiClass : uiClass.name; const ui = this._uiCache.get(className); if (ui) { ui.hide(); } else { console.warn(`[UIMgr] UI未加载,无法隐藏: ${className}`); } } }