云计算百科
云计算领域专业知识百科平台

10-06-YooAsset实战-Unity资源加载与使用

实战-资源加载与使用

篇章: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 实践建议

  • 启动用同步:避免 Loading 闪烁
  • 运行时用异步:避免卡顿
  • 引用计数必管:避免内存泄漏
  • 失败必处理:每个加载都要有 fallback
  • 性能要监控:加载慢的资源要优化

  • 上一篇:资源配置与打包 下一篇:热更新流程实现

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 10-06-YooAsset实战-Unity资源加载与使用
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!