// ============================================================================
|
// ResourcePreloader.cs — 资源预加载管理器
|
// Feature: 001-async-resource-loading
|
// ============================================================================
|
|
using System;
|
using System.Collections.Generic;
|
using UnityEngine;
|
using Cysharp.Threading.Tasks;
|
|
namespace ProjSG.Resource
|
{
|
/// <summary>
|
/// 资源预加载管理器。
|
/// 按场景/流程组织预加载配置,通过 ResourceCacheManager 执行实际缓存。
|
/// </summary>
|
public class ResourcePreloader : Singleton<ResourcePreloader>, IResourcePreloader
|
{
|
private readonly Dictionary<string, PreloadConfig> _configs = new Dictionary<string, PreloadConfig>();
|
private readonly HashSet<string> _loadedConfigs = new HashSet<string>();
|
|
// ====================================================================
|
// 预设配置
|
// ====================================================================
|
|
/// <summary>
|
/// 注册默认预加载配置。应在 YooAsset 初始化后调用。
|
/// </summary>
|
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 实现
|
// ====================================================================
|
|
/// <inheritdoc/>
|
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;
|
}
|
|
/// <inheritdoc/>
|
public async UniTask PreloadAsync(string configName, IProgress<float> 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<float>(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);
|
}
|
|
/// <inheritdoc/>
|
public async UniTask PreloadByTagAsync(string tag, IProgress<float> 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);
|
}
|
}
|
|
/// <inheritdoc/>
|
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);
|
}
|
|
/// <inheritdoc/>
|
public bool IsConfigLoaded(string configName)
|
{
|
return _loadedConfigs.Contains(configName);
|
}
|
}
|
}
|