// ============================================================================
// 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);
}
}
}