// ============================================================================ // ResourcePreloader.cs — 资源预加载管理器 // Feature: 001-async-resource-loading // ============================================================================ using System; using System.Collections.Generic; using UnityEngine; using Cysharp.Threading.Tasks; namespace ProjSG.Resource { /// /// 资源预加载管理器。 /// 按场景/流程组织预加载配置,通过 ResourceCacheManager 执行实际缓存。 /// public class ResourcePreloader : Singleton, IResourcePreloader { private readonly Dictionary _configs = new Dictionary(); private readonly HashSet _loadedConfigs = new HashSet(); // ==================================================================== // 预设配置 // ==================================================================== /// /// 注册默认预加载配置。应在 YooAsset 初始化后调用。 /// public void RegisterDefaultConfigs() { // 启动必需资源(常驻) RegisterConfig(new PreloadConfig { ConfigName = "StartupEssential", Locations = new[] { "Assets/ResourcesOut/Shader", // Shader 全部 "Assets/ResourcesOut/Materials", // 通用 Material "Assets/ResourcesOut/BuiltIn/Font", // 常用字体 "Assets/ResourcesOut/BuiltIn/UIRoot", // UIRoot 预制体 "Assets/ResourcesOut/BuiltIn/SoundPlayer", // 音频播放器 }, Tags = null, IsPermanent = true, }); // 战斗场景资源(非常驻,战斗结束后可释放) RegisterConfig(new PreloadConfig { ConfigName = "BattleScene", Locations = null, Tags = new[] { "tag_battle_spine", "tag_battle_effect", "tag_battle_sound" }, IsPermanent = false, }); } // ==================================================================== // IResourcePreloader 实现 // ==================================================================== /// public void RegisterConfig(PreloadConfig config) { if (config == null || string.IsNullOrEmpty(config.ConfigName)) { Debug.LogError("ResourcePreloader.RegisterConfig: config or ConfigName is null"); return; } _configs[config.ConfigName] = config; } /// public async UniTask PreloadAsync(string configName, IProgress progress = null) { if (string.IsNullOrEmpty(configName)) { Debug.LogError("ResourcePreloader.PreloadAsync: configName is null or empty"); progress?.Report(1f); return; } if (!_configs.TryGetValue(configName, out var config)) { Debug.LogWarning($"ResourcePreloader.PreloadAsync: config '{configName}' not found"); progress?.Report(1f); return; } if (_loadedConfigs.Contains(configName)) { progress?.Report(1f); return; } var cacheManager = ResourceCacheManager.Instance; int totalSteps = 0; int completedSteps = 0; // 计算总步骤 if (config.Locations != null) totalSteps += config.Locations.Length; if (config.Tags != null) totalSteps += config.Tags.Length; if (totalSteps == 0) { progress?.Report(1f); _loadedConfigs.Add(configName); return; } // 加载 Locations if (config.Locations != null && config.Locations.Length > 0) { var locationProgress = new Progress(p => { float locationWeight = (float)config.Locations.Length / totalSteps; progress?.Report(p * locationWeight); }); await cacheManager.PreloadAsync(config.Locations, config.IsPermanent, locationProgress); completedSteps += config.Locations.Length; } // 按 Tag 加载 if (config.Tags != null) { foreach (var tag in config.Tags) { await PreloadByTagAsync(tag); completedSteps++; progress?.Report((float)completedSteps / totalSteps); } } _loadedConfigs.Add(configName); progress?.Report(1f); } /// public async UniTask PreloadByTagAsync(string tag, IProgress progress = null) { if (string.IsNullOrEmpty(tag)) { progress?.Report(1f); return; } try { // 使用 YooAssetService 按标签下载/缓存 await YooAssetService.Instance.DownloadByTagsAsync( new[] { tag }, progress: progress); progress?.Report(1f); } catch (Exception e) { Debug.LogError($"ResourcePreloader.PreloadByTagAsync failed for tag '{tag}': {e.Message}"); progress?.Report(1f); } } /// public void UnloadConfig(string configName) { if (string.IsNullOrEmpty(configName)) return; if (!_configs.TryGetValue(configName, out var config)) return; // 常驻配置不卸载 if (config.IsPermanent) { Debug.LogWarning($"ResourcePreloader.UnloadConfig: config '{configName}' is permanent, skipping unload"); return; } if (config.Locations != null) { var cacheManager = ResourceCacheManager.Instance; foreach (var loc in config.Locations) { cacheManager.Release(loc); } } _loadedConfigs.Remove(configName); } /// public bool IsConfigLoaded(string configName) { return _loadedConfigs.Contains(configName); } } }