| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- import { _decorator, Component, SpriteFrame, sp } from 'cc';
- import { BundleLoader } from './BundleLoader';
- import { LevelConfigManager } from '../LevelSystem/LevelConfigManager';
- import { SaveDataManager } from '../LevelSystem/SaveDataManager';
- import { ConfigManager } from './ConfigManager';
- import EventBus, { GameEvents } from './EventBus';
- const { ccclass } = _decorator;
- /**
- * 资源预加载管理器
- * 解决微信开发者平台动态加载资源不及时的问题
- * 在进入战斗前预加载当前关卡的背景图片和敌人资源
- */
- @ccclass('ResourcePreloader')
- export class ResourcePreloader {
- private static instance: ResourcePreloader = null;
- private bundleLoader: BundleLoader;
- private levelConfigManager: LevelConfigManager;
- private saveDataManager: SaveDataManager;
- private configManager: ConfigManager;
-
- // 预加载缓存
- private preloadedBackgrounds: Map<string, SpriteFrame> = new Map();
- private preloadedEnemyAnimations: Map<string, sp.SkeletonData> = new Map();
-
- // 预加载状态
- private isPreloading: boolean = false;
- private preloadProgress: number = 0;
- private preloadErrors: string[] = [];
-
- public static getInstance(): ResourcePreloader {
- if (!ResourcePreloader.instance) {
- ResourcePreloader.instance = new ResourcePreloader();
- }
- return ResourcePreloader.instance;
- }
-
- constructor() {
- this.bundleLoader = BundleLoader.getInstance();
- this.levelConfigManager = LevelConfigManager.getInstance();
- this.saveDataManager = SaveDataManager.getInstance();
- this.configManager = ConfigManager.getInstance();
- }
-
- /**
- * 预加载指定关卡的资源
- * @param levelId 关卡ID
- */
- public async preloadLevelResources(levelId: number): Promise<boolean> {
- try {
- console.log(`[ResourcePreloader] 开始预加载关卡 ${levelId} 的资源`);
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_START);
-
- const levelConfig = await this.levelConfigManager.getLevelConfig(levelId);
- if (!levelConfig) {
- console.warn(`[ResourcePreloader] 找不到关卡 ${levelId} 的配置`);
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_ERROR, { error: `找不到关卡 ${levelId} 的配置` });
- return false;
- }
-
- let totalResources = 0;
- let loadedResources = 0;
-
- // 计算总资源数
- if (levelConfig.backgroundImage) {
- totalResources++;
- }
-
- const enemyTypes = new Set<string>();
- if (levelConfig.waves) {
- levelConfig.waves.forEach(wave => {
- if (wave.enemies && Array.isArray(wave.enemies)) {
- wave.enemies.forEach(enemy => {
- if (enemy.enemyType) {
- enemyTypes.add(enemy.enemyType);
- }
- });
- }
- });
- }
- totalResources += enemyTypes.size;
-
- console.log(`[ResourcePreloader] 关卡 ${levelId} 需要预加载 ${totalResources} 个资源`);
-
- // 预加载背景图片
- if (levelConfig.backgroundImage) {
- try {
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_PROGRESS, {
- progress: (loadedResources / totalResources) * 100,
- message: `正在加载背景图片: ${levelConfig.backgroundImage}`
- });
-
- await this.preloadBackground(levelConfig.backgroundImage);
- loadedResources++;
-
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_PROGRESS, {
- progress: (loadedResources / totalResources) * 100,
- message: `背景图片加载完成: ${levelConfig.backgroundImage}`
- });
-
- console.log(`[ResourcePreloader] 背景图片预加载完成: ${levelConfig.backgroundImage}`);
- } catch (error) {
- console.warn(`[ResourcePreloader] 背景图片预加载失败: ${levelConfig.backgroundImage}`, error);
- }
- }
-
- // 预加载敌人动画
- for (const enemyType of enemyTypes) {
- try {
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_PROGRESS, {
- progress: (loadedResources / totalResources) * 100,
- message: `正在加载敌人动画: ${enemyType}`
- });
-
- await this.preloadEnemyAnimation(enemyType);
- loadedResources++;
-
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_PROGRESS, {
- progress: (loadedResources / totalResources) * 100,
- message: `敌人动画加载完成: ${enemyType}`
- });
-
- console.log(`[ResourcePreloader] 敌人动画预加载完成: ${enemyType}`);
- } catch (error) {
- console.warn(`[ResourcePreloader] 敌人动画预加载失败: ${enemyType}`, error);
- }
- }
-
- console.log(`[ResourcePreloader] 关卡 ${levelId} 资源预加载完成,成功加载 ${loadedResources}/${totalResources} 个资源`);
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_COMPLETE);
- return true;
-
- } catch (error) {
- console.error(`[ResourcePreloader] 关卡 ${levelId} 资源预加载失败:`, error);
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_ERROR, { error: error.toString() });
- return false;
- }
- }
-
- /**
- * 预加载当前关卡的所有资源
- */
- public async preloadCurrentLevelResources(): Promise<void> {
- const currentLevel = this.saveDataManager.getCurrentLevel();
- const success = await this.preloadLevelResources(currentLevel);
-
- if (!success) {
- throw new Error('资源预加载失败');
- }
- }
-
- /**
- * 预加载背景图片
- * @param backgroundPath 背景图片路径
- */
- private async preloadBackground(backgroundPath: string): Promise<void> {
- // 如果已经预加载过,直接返回
- if (this.preloadedBackgrounds.has(backgroundPath)) {
- return;
- }
-
- // 转换路径格式:从 "images/LevelBackground/BG1" 转换为 "LevelBackground/BG1"
- const bundlePath = backgroundPath.replace('images/', '');
- const spriteFramePath = `${bundlePath}/spriteFrame`;
-
- try {
- const spriteFrame = await this.bundleLoader.loadSpriteFrame(spriteFramePath);
- this.preloadedBackgrounds.set(backgroundPath, spriteFrame);
- console.log(`[ResourcePreloader] 背景图片缓存成功: ${backgroundPath}`);
- } catch (error) {
- console.error(`[ResourcePreloader] 背景图片预加载失败: ${backgroundPath}`, error);
- throw error;
- }
- }
-
- /**
- * 预加载敌人动画资源
- * @param enemyType 敌人类型
- */
- private async preloadEnemyAnimation(enemyType: string): Promise<void> {
- // 如果已经预加载过,直接返回
- if (this.preloadedEnemyAnimations.has(enemyType)) {
- return;
- }
-
- try {
- // 获取敌人配置
- const enemiesConfig = this.configManager.getAllEnemies();
- if (!enemiesConfig || enemiesConfig.length === 0) {
- throw new Error('敌人配置未加载');
- }
-
- const enemyConfig = enemiesConfig.find((enemy: any) => enemy.id === enemyType);
- if (!enemyConfig) {
- throw new Error(`未找到敌人类型: ${enemyType}`);
- }
-
- // 获取敌人动画路径
- let spinePath = enemyConfig.visualConfig?.spritePath || enemyConfig.visual?.sprite_path;
- if (!spinePath) {
- throw new Error(`敌人 ${enemyType} 没有配置动画路径`);
- }
-
- // 路径转换逻辑(与EnemyController保持一致)
- if (spinePath.startsWith('@EnemyAni')) {
- spinePath = spinePath.replace('@EnemyAni', 'Animation/EnemyAni');
- }
- if (spinePath.startsWith('@')) {
- spinePath = spinePath.substring(1);
- }
-
- // 对于Animation/EnemyAni路径,需要添加子目录和文件名
- if (spinePath.startsWith('Animation/EnemyAni/')) {
- const parts = spinePath.split('/');
- if (parts.length === 3) {
- const animId = parts[2];
- spinePath = `${spinePath}/${animId}`;
- }
- }
-
- // 移除Animation/前缀,因为BundleLoader会从Bundle中加载
- if (spinePath.startsWith('Animation/')) {
- spinePath = spinePath.replace('Animation/', '');
- }
-
- // 预加载骨骼动画数据
- const skeletonData = await BundleLoader.loadSkeletonData(spinePath);
- this.preloadedEnemyAnimations.set(enemyType, skeletonData);
- console.log(`[ResourcePreloader] 敌人动画缓存成功: ${enemyType} -> ${spinePath}`);
-
- } catch (error) {
- console.error(`[ResourcePreloader] 敌人动画预加载失败: ${enemyType}`, error);
- throw error;
- }
- }
-
- /**
- * 计算需要预加载的资源总数
- * @param levelConfig 关卡配置
- * @returns 资源总数
- */
- private calculateTotalResources(levelConfig: any): number {
- let total = 0;
-
- // 背景图片
- if (levelConfig.backgroundImage) {
- total += 1;
- }
-
- // 敌人动画
- const enemyTypes = this.extractEnemyTypes(levelConfig);
- total += enemyTypes.length;
-
- return total;
- }
-
- /**
- * 从关卡配置中提取所有敌人类型
- * @param levelConfig 关卡配置
- * @returns 敌人类型数组
- */
- private extractEnemyTypes(levelConfig: any): string[] {
- const enemyTypes = new Set<string>();
-
- if (levelConfig.waves && Array.isArray(levelConfig.waves)) {
- for (const wave of levelConfig.waves) {
- if (wave.enemies && Array.isArray(wave.enemies)) {
- for (const enemy of wave.enemies) {
- if (enemy.enemyType) {
- enemyTypes.add(enemy.enemyType);
- }
- }
- }
- }
- }
-
- return Array.from(enemyTypes);
- }
-
- /**
- * 更新预加载进度
- * @param loaded 已加载数量
- * @param total 总数量
- */
- private updateProgress(loaded: number, total: number): void {
- this.preloadProgress = total > 0 ? (loaded / total) * 100 : 100;
-
- // 发送进度更新事件
- EventBus.getInstance().emit(GameEvents.RESOURCE_PRELOAD_PROGRESS, {
- progress: this.preloadProgress,
- loaded: loaded,
- total: total
- });
- }
-
- /**
- * 获取预加载的背景图片
- * @param backgroundPath 背景图片路径
- * @returns SpriteFrame或null
- */
- public getPreloadedBackground(backgroundPath: string): SpriteFrame | null {
- return this.preloadedBackgrounds.get(backgroundPath) || null;
- }
-
- /**
- * 获取预加载的敌人动画
- * @param enemyType 敌人类型
- * @returns SkeletonData或null
- */
- public getPreloadedEnemyAnimation(enemyType: string): sp.SkeletonData | null {
- return this.preloadedEnemyAnimations.get(enemyType) || null;
- }
-
- /**
- * 清理预加载缓存
- */
- public clearCache(): void {
- this.preloadedBackgrounds.clear();
- this.preloadedEnemyAnimations.clear();
- console.log('[ResourcePreloader] 预加载缓存已清理');
- }
-
- /**
- * 获取预加载状态
- */
- public getPreloadStatus(): {
- isPreloading: boolean;
- progress: number;
- errors: string[];
- } {
- return {
- isPreloading: this.isPreloading,
- progress: this.preloadProgress,
- errors: [...this.preloadErrors]
- };
- }
- }
|