移动端Idle游戏启动时异步加载资源的最优管理方案
解决方案
方案1:使用Addressables.CombineAsync合并加载操作
Addressables原生提供了CombineAsync方法,可将多个异步加载操作的句柄合并为一个,等待合并操作完成时,就代表所有子加载任务都已结束。这种方式既能保持资源并行加载的效率,又能统一监听所有加载完成的时机。
代码示例:
IEnumerator LoadAllAssetsThenSavedGame() { // 同时启动三个并行加载任务,获取各自的操作句柄 var inventoryOp = Addressables.LoadAssetsAsync<InventorySO>("InventoryLabel", null); var characterOp = Addressables.LoadAssetsAsync<CharacterSO>("CharacterLabel", null); var soundOp = Addressables.LoadAssetsAsync<SoundSO>("SoundLabel", null); // 合并所有加载操作 var combinedOp = Addressables.CombineAsync(inventoryOp, characterOp, soundOp); yield return combinedOp; if (combinedOp.Status == AsyncOperationStatus.Succeeded) { // 分别提取每个加载任务的结果 _inventoryResults = inventoryOp.Result; _characterResults = characterOp.Result; _soundResults = soundOp.Result; // 所有资源加载完成,执行存档加载流程 yield return StartCoroutine(LoadSavedGame()); } else { Debug.LogError("部分资源加载失败,请检查资源标签或Addressables配置"); } }
调用时只需启动这个合并协程:StartCoroutine(LoadAllAssetsThenSavedGame());
方案2:协程中手动跟踪所有加载操作状态
如果不想依赖CombineAsync,可以手动收集所有加载句柄,在一个协程中等待所有操作完成。这里的等待不会影响并行加载,因为所有任务已经同时启动,只是逐个确认完成状态。
代码示例:
IEnumerator LoadAllAssetsThenSavedGame() { List<AsyncOperationHandle> allLoadHandles = new List<AsyncOperationHandle>(); // 启动并行加载并收集句柄,同时用子协程存储加载结果 var inventoryOp = Addressables.LoadAssetsAsync<InventorySO>("InventoryLabel", null); allLoadHandles.Add(inventoryOp); StartCoroutine(StoreLoadedResult(inventoryOp, ref _inventoryResults)); var characterOp = Addressables.LoadAssetsAsync<CharacterSO>("CharacterLabel", null); allLoadHandles.Add(characterOp); StartCoroutine(StoreLoadedResult(characterOp, ref _characterResults)); var soundOp = Addressables.LoadAssetsAsync<SoundSO>("SoundLabel", null); allLoadHandles.Add(soundOp); StartCoroutine(StoreLoadedResult(soundOp, ref _soundResults)); // 等待所有加载操作完成 foreach (var handle in allLoadHandles) { yield return handle; } // 所有资源就绪,执行存档加载 yield return StartCoroutine(LoadSavedGame()); } // 通用的结果存储协程 IEnumerator StoreLoadedResult<T>(AsyncOperationHandle<IList<T>> loadOp, ref IList<T> targetResult) { yield return loadOp; if (loadOp.Status == AsyncOperationStatus.Succeeded) { targetResult = loadOp.Result; } }
方案3:结合C# Task.WhenAll(Unity 2020+适用)
Unity 2020及以上版本支持将AsyncOperationHandle转换为Task,可利用Task.WhenAll批量等待所有任务完成,之后切回主线程执行Unity相关操作。
代码示例:
async void LoadAllAssetsThenSavedGame() { // 启动并行加载并转为Task var inventoryTask = Addressables.LoadAssetsAsync<InventorySO>("InventoryLabel", null).Task; var characterTask = Addressables.LoadAssetsAsync<CharacterSO>("CharacterLabel", null).Task; var soundTask = Addressables.LoadAssetsAsync<SoundSO>("SoundLabel", null).Task; // 等待所有Task完成 await Task.WhenAll(inventoryTask, characterTask, soundTask); // 提取结果(注意需确保在主线程操作Unity API) _inventoryResults = inventoryTask.Result; _characterResults = characterTask.Result; _soundResults = soundTask.Result; // 切回主线程执行存档加载 UnityMainThreadDispatcher.Instance.Enqueue(() => { StartCoroutine(LoadSavedGame()); }); }
注:如果项目没有现成的主线程调度器,可自行实现一个简单版本(比如用一个MonoBehaviour维护队列,在Update中执行队列任务),避免在后台线程调用Unity API导致异常。
通用注意事项
- 加载完成后(或场景卸载时)记得释放Addressables资源句柄:
Addressables.Release(handle);,避免内存泄漏。 - 需添加加载失败的容错逻辑,比如部分资源加载出错时,决定是否重试或终止存档加载流程。
内容的提问来源于stack exchange,提问作者Dror
相关产品推荐
相关产品推荐

