import { SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc'; import { HttpUtil } from './HttpUtil'; import { ApiLevelData, ApiResponse, RuntimeLevelConfig } from '../types/LevelTypes'; /** * 进度回调类型 * @param progress 进度值 0-1 * @param message 进度消息 */ export type ProgressCallback = (progress: number, message: string) => void; /** * 关卡数据管理器 * 单例模式,负责从 API 获取关卡数据并预加载图片 */ export class LevelDataManager { private static _instance: LevelDataManager | null = null; /** API 地址 */ private readonly API_URL = 'https://ilookai.cn/api/v1/wechat-game/levels'; /** 请求超时时间(毫秒) */ private readonly REQUEST_TIMEOUT = 8000; /** 运行时关卡配置缓存 */ private _levelConfigs: RuntimeLevelConfig[] = []; /** 是否已成功从 API 获取数据 */ private _hasApiData: boolean = false; /** 图片缓存:URL -> SpriteFrame */ private _imageCache: Map = new Map(); /** * 获取单例实例 */ static get instance(): LevelDataManager { if (!this._instance) { this._instance = new LevelDataManager(); } return this._instance; } /** * 私有构造函数 */ private constructor() {} /** * 初始化:从 API 获取数据并预加载图片 * @param onProgress 进度回调 * @returns 是否初始化成功 */ async initialize(onProgress?: ProgressCallback): Promise { console.log('[LevelDataManager] 开始初始化'); try { // 阶段1: 获取 API 数据 (0-20%) onProgress?.(0, '正在获取关卡数据...'); const apiData = await this._fetchApiData(); if (!apiData || apiData.length === 0) { console.warn('[LevelDataManager] API 返回空数据'); onProgress?.(0.2, 'API 数据为空,使用本地配置'); return false; } console.log(`[LevelDataManager] 获取到 ${apiData.length} 个关卡数据`); onProgress?.(0.2, `获取到 ${apiData.length} 个关卡`); // 阶段2: 预加载所有图片 (20-80%) const configs = await this._preloadImages(apiData, (progress) => { onProgress?.(0.2 + progress * 0.6, '正在加载关卡资源...'); }); this._levelConfigs = configs; this._hasApiData = true; console.log('[LevelDataManager] 初始化完成'); onProgress?.(0.8, '关卡资源加载完成'); return true; } catch (error) { console.error('[LevelDataManager] 初始化失败:', error); onProgress?.(0.2, '获取数据失败,使用本地配置'); return false; } } /** * 获取指定关卡配置 * @param index 关卡索引 */ getLevelConfig(index: number): RuntimeLevelConfig | null { if (index < 0 || index >= this._levelConfigs.length) { return null; } return this._levelConfigs[index]; } /** * 获取关卡总数 */ getLevelCount(): number { return this._levelConfigs.length; } /** * 检查是否有 API 数据 */ hasApiData(): boolean { return this._hasApiData && this._levelConfigs.length > 0; } /** * 从 API 获取关卡数据 */ private async _fetchApiData(): Promise { try { const response = await HttpUtil.get(this.API_URL, this.REQUEST_TIMEOUT); if (response.code !== 0) { console.warn(`[LevelDataManager] API 返回错误码: ${response.code}, 消息: ${response.message}`); return null; } return response.data; } catch (error) { console.error('[LevelDataManager] API 请求失败:', error); return null; } } /** * 预加载所有图片 * @param apiData API 返回的关卡数据 * @param onProgress 进度回调 */ private async _preloadImages( apiData: ApiLevelData[], onProgress?: (progress: number) => void ): Promise { const configs: RuntimeLevelConfig[] = []; const total = apiData.length; for (let i = 0; i < total; i++) { const data = apiData[i]; const spriteFrame = await this._loadImage(data.imageUrl); configs.push({ id: data.id, name: data.name, spriteFrame: spriteFrame, clue1: data.hint1, clue2: data.hint2, clue3: data.hint3, answer: data.answer }); onProgress?.((i + 1) / total); } return configs; } /** * 加载远程图片为 SpriteFrame * @param url 图片 URL */ private async _loadImage(url: string): Promise { // 检查缓存 if (this._imageCache.has(url)) { return this._imageCache.get(url)!; } return new Promise((resolve) => { assetManager.loadRemote(url, (err, imageAsset) => { if (err) { console.error(`[LevelDataManager] 加载图片失败: ${url}`, err); resolve(null); return; } const texture = new Texture2D(); texture.image = imageAsset; const spriteFrame = new SpriteFrame(); spriteFrame.texture = texture; // 缓存 this._imageCache.set(url, spriteFrame); resolve(spriteFrame); }); }); } /** * 清除缓存 */ clearCache(): void { this._levelConfigs = []; this._hasApiData = false; this._imageCache.clear(); console.log('[LevelDataManager] 缓存已清除'); } }