实战-资源加载与使用
篇章:10-实战篇-从零搭建 状态:已完成 阅读时间:约 25 分钟
一、引言
1.1 本章在系列中的定位
资源加载是 YooAsset 在运行时最常用的 API。本章系统讲解 YooAsset 的各种加载方式:
- 同步/异步加载 Prefab、Texture、Audio 等
- 加载场景
- 实例化与销毁
- 加载完成回调
- 资源释放与回收
1.2 本章要解决的核心问题
- 如何加载各种类型的资源?
- 同步和异步加载如何选择?
- 如何正确管理引用计数?
- 加载失败的降级策略?
1.3 阅读前置要求
- 完成 10-01 到 10-05
- 了解 C# async/await
- 了解 Unity 协程
二、基础加载 API
2.1 同步加载
using YooAsset;
public class SyncLoader
{
public T LoadSync<T>(string location) where T : Object
{
var op = YooAssets.LoadAssetSync<T>(location);
return op.Result;
}
public Sprite LoadSprite(string location)
{
return LoadSync<Sprite>(location);
}
public GameObject LoadPrefab(string location)
{
return LoadSync<GameObject>(location);
}
public AudioClip LoadAudio(string location)
{
return LoadSync<AudioClip>(location);
}
}
同步加载的适用场景:
- 启动时必须的资源
- Loading 界面的资源
- 调试时快速验证
注意事项:
- 会卡主线程
- 不支持进度回调
- 失败时无法取消
2.2 异步加载
public class AsyncLoader
{
public async Task<T> LoadAsync<T>(string location) where T : Object
{
var op = YooAssets.LoadAssetAsync<T>(location);
await op.Task;
if (op.Status != EOperationStatus.Succeed)
{
Debug.LogError($"加载失败: {location}, 错误: {op.Error}");
return null;
}
return op.Result;
}
public async Task<Sprite> LoadSpriteAsync(string location)
{
return await LoadAsync<Sprite>(location);
}
}
2.3 加载配置选项
public class LoadOptions
{
/// <summary>
/// 加载优先级
/// </summary>
public uint Priority = 0;
/// <summary>
/// 是否缓存
/// </summary>
public bool IsCache = true;
/// <summary>
/// 加载失败时是否重试
/// </summary>
public bool RetryOnError = true;
/// <summary>
/// 加载超时(秒)
/// </summary>
public int Timeout = 30;
}
使用:
var op = YooAssets.LoadAssetAsync<Sprite>(
"ui_icon_home",
priority: 100,
// 其他选项
);
2.4 加载回调
public class CallbackLoader
{
public void LoadWithCallback<T>(string location, Action<T> onSuccess, Action<string> onError) where T : Object
{
var op = YooAssets.LoadAssetAsync<T>(location);
op.Completed += handle =>
{
if (handle.Status == EOperationStatus.Succeed)
onSuccess?.Invoke(handle.Result as T);
else
onError?.Invoke(handle.Error);
};
}
}
三、加载各种资源类型
3.1 Prefab 加载与实例化
public class PrefabLoader
{
public async Task<GameObject> InstantiateAsync(string location, Transform parent = null)
{
// 1. 加载 Prefab
var op = YooAssets.LoadAssetAsync<GameObject>(location);
await op.Task;
if (op.Status != EOperationStatus.Succeed)
return null;
// 2. 实例化
var go = Object.Instantiate(op.Result, parent);
return go;
}
public void DestroyInstance(GameObject go, string location)
{
if (go == null) return;
// 1. 销毁 GameObject
Object.Destroy(go);
// 2. 释放资源(注意:会影响其他实例)
// 一般不需要立即释放
}
}
3.2 Sprite 加载
public class SpriteLoader
{
public async Task<Sprite> LoadSpriteAsync(string location)
{
return await LoadAsyncInternal<Sprite>(location);
}
public void SetSprite(Image image, string location)
{
// 同步加载(用于 UI 频繁刷新的场景)
var sprite = YooAssets.LoadAssetSync<Sprite>(location);
if (sprite != null)
{
image.sprite = sprite;
}
}
}
3.3 Texture 加载
public class TextureLoader
{
public async Task<Texture2D> LoadTextureAsync(string location)
{
return await LoadAsyncInternal<Texture2D>(location);
}
}
3.4 Audio 加载
public class AudioLoader
{
public async Task<AudioClip> LoadAudioAsync(string location)
{
return await LoadAsyncInternal<AudioClip>(location);
}
}
3.5 场景加载
public class SceneLoader
{
public async Task LoadSceneAsync(string sceneName, LoadSceneMode mode = LoadSceneMode.Single)
{
var op = YooAssets.LoadSceneAsync(sceneName, mode);
await op.Task;
if (op.Status != EOperationStatus.Succeed)
{
Debug.LogError($"场景加载失败: {sceneName}, 错误: {op.Error}");
}
}
public async Task LoadSceneAdditive(string sceneName)
{
await LoadSceneAsync(sceneName, LoadSceneMode.Additive);
}
public void UnloadScene(SceneInstance scene)
{
YooAssets.UnloadSceneAsync(scene);
}
}
3.6 文本/二进制加载
public class TextLoader
{
/// <summary>
/// 加载文本(Json、Xml、Txt)
/// </summary>
public async Task<string> LoadTextAsync(string location)
{
var op = YooAssets.GetRawFileAsync(location);
await op.Task;
if (op.Status != EOperationStatus.Succeed)
return null;
return System.Text.Encoding.UTF8.GetString(op.Result);
}
/// <summary>
/// 加载二进制
/// </summary>
public async Task<byte[]> LoadBytesAsync(string location)
{
var op = YooAssets.GetRawFileAsync(location);
await op.Task;
return op.Status == EOperationStatus.Succeed ? op.Result : null;
}
}
3.7 ScriptableObject 加载
public class ScriptableObjectLoader
{
public async Task<T> LoadSOAsync<T>(string location) where T : ScriptableObject
{
return await LoadAsyncInternal<T>(location);
}
public T LoadSOSync<T>(string location) where T : ScriptableObject
{
return YooAssets.LoadAssetSync<T>(location);
}
}
四、批量加载
4.1 并行加载
public class BatchLoader
{
public async Task<List<T>> LoadBatchAsync<T>(List<string> locations) where T : Object
{
// 1. 创建所有加载任务
var tasks = locations.Select(loc => LoadInternalAsync<T>(loc)).ToList();
// 2. 等待所有完成
var results = await Task.WhenAll(tasks);
// 3. 过滤失败
return results.Where(r => r != null).ToList();
}
async Task<T> LoadInternalAsync<T>(string location) where T : Object
{
var op = YooAssets.LoadAssetAsync<T>(location);
await op.Task;
return op.Status == EOperationStatus.Succeed ? op.Result : null;
}
}
4.2 并发控制
public class ControlledBatchLoader
{
public async Task<List<T>> LoadBatchAsync<T>(
List<string> locations,
int maxConcurrent = 10) where T : Object
{
var semaphore = new SemaphoreSlim(maxConcurrent);
var results = new ConcurrentBag<T>();
var failed = new ConcurrentBag<string>();
var tasks = locations.Select(async loc =>
{
await semaphore.WaitAsync();
try
{
var op = YooAssets.LoadAssetAsync<T>(loc);
await op.Task;
if (op.Status == EOperationStatus.Succeed)
results.Add(op.Result);
else
failed.Add(loc);
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks);
if (failed.Any())
{
Debug.LogWarning($"加载失败: {string.Join(", ", failed)}");
}
return results.ToList();
}
}
4.3 加载进度跟踪
public class ProgressTracker
{
public async Task<List<T>> LoadWithProgress<T>(
List<string> locations,
IProgress<float> progress,
int maxConcurrent = 10) where T : Object
{
int total = locations.Count;
int completed = 0;
var lockObj = new object();
var semaphore = new SemaphoreSlim(maxConcurrent);
var tasks = locations.Select(async loc =>
{
await semaphore.WaitAsync();
try
{
var op = YooAssets.LoadAssetAsync<T>(loc);
// 进度回调
while (!op.IsDone)
{
await Task.Yield();
}
lock (lockObj)
{
completed++;
progress.Report((float)completed / total);
}
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks);
return null; // 简化返回
}
}
// 使用:
var progress = new Progress<float>(p =>
{
progressBar.value = p;
progressText.text = $"{p * 100:F1}%";
});
await loader.LoadWithProgress<Sprite>(locations, progress);
五、资源释放
5.1 引用计数
YooAsset 自动管理引用计数:
public class RefCountExample
{
public async Task OnSceneEnter()
{
// 加载资源
var op1 = YooAssets.LoadAssetAsync<Sprite>("ui_icon_1");
var op2 = YooAssets.LoadAssetAsync<Sprite>("ui_icon_2");
await Task.WhenAll(op1.Task, op2.Task);
// 引用计数 +1, +1
}
public void OnSceneExit()
{
// 释放资源
// 引用计数 -1
// 计数为 0 时真正释放
}
}
5.2 主动释放
public class ResourceReleaser
{
/// <summary>
/// 释放单个加载操作
/// </summary>
public void Release(AsyncOperationBase op)
{
op.Release();
}
/// <summary>
/// 批量释放
/// </summary>
public void ReleaseAll(List<AsyncOperationBase> ops)
{
foreach (var op in ops)
{
op.Release();
}
}
}
5.3 自动释放
public class AutoReleaser : MonoBehaviour
{
private readonly List<AsyncOperationBase> _trackedOps = new();
public async Task<T> LoadAndTrack<T>(string location) where T : Object
{
var op = YooAssets.LoadAssetAsync<T>(location);
_trackedOps.Add(op);
await op.Task;
return op.Status == EOperationStatus.Succeed ? op.Result : null;
}
void OnDestroy()
{
// GameObject 销毁时自动释放关联资源
foreach (var op in _trackedOps)
{
if (op.IsValid())
op.Release();
}
}
}
5.4 内存压力下的释放
public class MemoryPressureHandler
{
public void OnLowMemory()
{
// 1. 释放缓存
YooAssets.ClearCache();
// 2. 卸载未使用资源
Resources.UnloadUnusedAssets();
// 3. 强制 GC
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Debug.Log($"[Memory] 释放后: {Profiler.GetTotalAllocatedMemoryLong() / 1024 / 1024}MB");
}
}
六、加载失败处理
6.1 失败原因
| 资源不存在 | Address 配置错误 |
| Bundle 不存在 | 资源未被打包 |
| 网络失败 | CDN 不可达 |
| 校验失败 | Hash 不匹配 |
| 超时 | 网络慢或资源大 |
6.2 失败检测
public class LoadFailureDetector
{
public async Task<T> LoadWithDetection<T>(string location) where T : Object
{
var op = YooAssets.LoadAssetAsync<T>(location);
await op.Task;
if (op.Status == EOperationStatus.Failed)
{
// 上报失败
ReportFailure(location, op.Error);
// 重试(可选)
if (ShouldRetry(op.Error))
{
return await RetryLoad<T>(location);
}
// 降级
return GetDefaultResource<T>();
}
return op.Result;
}
}
6.3 重试策略
public class RetryLoad
{
public async Task<T> LoadWithRetry<T>(
string location,
int maxRetry = 3) where T : Object
{
for (int i = 0; i < maxRetry; i++)
{
try
{
var op = YooAssets.LoadAssetAsync<T>(location);
await op.Task;
if (op.Status == EOperationStatus.Succeed)
return op.Result;
}
catch (Exception ex)
{
Debug.LogWarning($"第 {i + 1} 次加载失败: {ex.Message}");
}
// 指数退避
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, i)));
}
return GetDefaultResource<T>();
}
}
6.4 降级策略
public class FallbackStrategy
{
public async Task<T> LoadWithFallback<T>(string location) where T : Object
{
// 1. 尝试主资源
var op = YooAssets.LoadAssetAsync<T>(location);
await op.Task;
if (op.Status == EOperationStatus.Succeed)
return op.Result;
// 2. 尝试备用资源
var fallbackLoc = GetFallbackLocation(location);
if (fallbackLoc != null)
{
var fallbackOp = YooAssets.LoadAssetAsync<T>(fallbackLoc);
await fallbackOp.Task;
if (fallbackOp.Status == EOperationStatus.Succeed)
return fallbackOp.Result;
}
// 3. 使用默认资源
return GetDefaultResource<T>();
}
}
七、加载性能优化
7.1 预加载
public class Preloader
{
public async Task Preload(List<string> locations)
{
var tasks = locations.Select(loc =>
YooAssets.LoadAssetAsync<Object>(loc).Task
);
await Task.WhenAll(tasks);
// 资源已在缓存中
}
}
7.2 懒加载
public class LazyLoader
{
private readonly Dictionary<string, AsyncOperationBase> _cache = new();
public T Get<T>(string location) where T : Object
{
if (!_cache.ContainsKey(location))
{
_cache[location] = YooAssets.LoadAssetAsync<T>(location);
}
return (_cache[location] as AssetOperation)?.Result as T;
}
}
7.3 加载策略选择
| 启动资源 | ✅ | ❌ | – |
| 主城资源 | ❌ | ✅ | 5-10 |
| 战斗资源 | ❌ | ✅ | 10-20 |
| 活动资源 | ❌ | ✅ | 3-5 |
| DLC 资源 | ❌ | ✅ | 1-3 |
八、调试与监控
8.1 加载监控
public class LoadMonitor
{
private readonly Dictionary<string, float> _loadTimes = new();
public async Task<T> MonitoredLoad<T>(string location) where T : Object
{
var sw = Stopwatch.StartNew();
var op = YooAssets.LoadAssetAsync<T>(location);
await op.Task;
sw.Stop();
if (op.Status == EOperationStatus.Succeed)
{
// 记录加载时间
_loadTimes[location] = sw.ElapsedMilliseconds;
// 上报
if (sw.ElapsedMilliseconds > 1000)
{
Debug.LogWarning($"[SlowLoad] {location}: {sw.ElapsedMilliseconds}ms");
ReportSlowLoad(location, sw.ElapsedMilliseconds);
}
}
return op.Status == EOperationStatus.Succeed ? op.Result : null;
}
}
8.2 内存监控
public class MemoryMonitor
{
public void PrintStatus()
{
long totalMemory = Profiler.GetTotalAllocatedMemoryLong() / 1024 / 1024;
long textureMemory = Profiler.GetAllocatedMemoryForGraphicsDriver() / 1024 / 1024;
Debug.Log($"[Memory] Total: {totalMemory}MB, Texture: {textureMemory}MB");
}
}
九、实战案例
9.1 角色加载
public class CharacterLoader : MonoBehaviour
{
private GameObject _currentCharacter;
public async Task LoadCharacter(string characterId)
{
// 1. 销毁旧角色
if (_currentCharacter != null)
{
Destroy(_currentCharacter);
}
// 2. 加载新角色
var location = $"characters/{characterId}";
var op = YooAssets.LoadAssetAsync<GameObject>(location);
await op.Task;
if (op.Status != EOperationStatus.Succeed)
{
Debug.LogError($"角色加载失败: {location}");
return;
}
// 3. 实例化
_currentCharacter = Instantiate(op.Result, transform);
_currentCharacter.name = $"Character_{characterId}";
}
}
9.2 场景切换
public class SceneTransition : MonoBehaviour
{
public async Task TransitionToScene(string sceneName)
{
// 1. 显示 Loading 界面
ShowLoadingUI();
// 2. 异步加载新场景
var op = YooAssets.LoadSceneAsync(sceneName, LoadSceneMode.Single);
// 3. 等待加载完成
while (!op.IsDone)
{
UpdateProgress(op.Progress);
await Task.Yield();
}
if (op.Status != EOperationStatus.Succeed)
{
Debug.LogError($"场景加载失败: {sceneName}");
return;
}
// 4. 隐藏 Loading
HideLoadingUI();
}
}
十、总结
10.1 本章要点回顾
- 基础 API:LoadAssetSync、LoadAssetAsync、LoadSceneAsync
- 批量加载:并行加载、并发控制、进度跟踪
- 资源释放:引用计数、主动释放、自动释放
- 失败处理:检测、重试、降级
- 性能优化:预加载、懒加载、并发策略
10.2 与前后章节的关联
- 前章:10-05 介绍了资源配置与打包
- 本章:聚焦资源加载与使用
- 后章:10-07 将介绍热更新流程实现
10.3 实践建议
上一篇:资源配置与打包 下一篇:热更新流程实现
网硕互联帮助中心





评论前必须登录!
注册