5.7 Addressables


文档摘要

5.7 Addressables 5.7 Unity Addressables 详解:代码实践与资源管理利器 在现代游戏开发中,资源管理是一项至关重要的任务。随着游戏内容日益丰富,资源文件(如纹理、模型、音频、场景等)的数量和大小也急剧增长。传统的资源管理方式,例如直接将资源拖拽到场景中或使用 加载资源,在大型项目中会暴露出诸多弊端,例如: 包体过大: 所有资源都被打包到最终的游戏包中,即使某些资源在特定场景或流程中才会被使用,也会增加包体大小,影响用户下载和安装体验。 内存管理困难: 资源加载和卸载的时机难以精确控制,容易造成内存浪费,甚至导致内存溢出。 更新迭代复杂: 修改或更新资源需要重新构建整个游戏包,发布流程繁琐,难以快速迭代。

5.7 Addressables

5.7 Unity Addressables 详解:代码实践与资源管理利器

在现代游戏开发中,资源管理是一项至关重要的任务。随着游戏内容日益丰富,资源文件(如纹理、模型、音频、场景等)的数量和大小也急剧增长。传统的资源管理方式,例如直接将资源拖拽到场景中或使用 Resources.Load 加载资源,在大型项目中会暴露出诸多弊端,例如:

  • 包体过大: 所有资源都被打包到最终的游戏包中,即使某些资源在特定场景或流程中才会被使用,也会增加包体大小,影响用户下载和安装体验。

  • 内存管理困难: 资源加载和卸载的时机难以精确控制,容易造成内存浪费,甚至导致内存溢出。

  • 更新迭代复杂: 修改或更新资源需要重新构建整个游戏包,发布流程繁琐,难以快速迭代。

  • 资源依赖混乱: 项目规模扩大后,资源之间的依赖关系变得复杂,容易出现资源丢失或加载错误。

为了解决这些问题,Unity 引入了 Addressable Asset System (简称 Addressables)。Addressables 是一套强大的资源管理系统,它允许开发者通过 地址 来管理和加载资源,并将资源包 分离 打包,从而实现资源的动态加载、更新和高效管理。本章节将深入探讨 Unity Addressables 的核心概念、代码实践以及高级应用,帮助你掌握这一强大的工具,提升项目开发效率和游戏性能。

5.7.1 Addressables 核心概念

理解 Addressables 的核心概念是掌握其使用的基础。以下是几个关键概念:

  • Addressable Asset: 可以通过地址加载的资源。可以是 Unity 项目中的任何资源,例如 Prefab、Texture、Scene、AudioClip 等。

  • Address (地址): 用于唯一标识 Addressable Asset 的字符串。开发者可以使用资源名称、路径或其他自定义字符串作为地址。通过地址,可以在运行时动态加载资源,而无需关心资源的具体位置和打包方式。

  • Group (组): 用于组织和管理 Addressable Asset 的逻辑容器。可以将相关的资源放在同一个 Group 中,方便进行统一配置和管理。Group 可以设置不同的打包策略、加载方式、更新模式等。

  • Catalog (目录): Addressables 系统维护的一个索引文件,记录了所有 Addressable Asset 的地址、Group 信息、资源位置等元数据。运行时通过 Catalog 来查找和加载资源。

  • Build Layout (构建布局): 定义了 Addressable Asset 如何被打包成 AssetBundle 以及如何部署的规则。不同的 Build Layout 可以满足不同的打包和部署需求。

  • Profile (配置): 允许开发者为不同的环境(例如本地开发、测试、发布)配置不同的 Addressables 设置,例如 Catalog 的构建路径、AssetBundle 的服务器地址等。

为了更清晰地理解这些概念之间的关系,我们可以用 Mermaid 的 graph TD 图来表示 Addressables 的系统架构:

图 5.7.1 Addressables 系统架构

解释:

  • Addressable Asset 是核心,它是用户想要管理的资源。

  • 每个 Addressable Asset 都被赋予一个唯一的 Address,用于运行时加载。

  • Addressable Asset 被组织到 Group 中进行管理。

  • Group 的打包和部署方式由 Build LayoutProfile 定义。

  • Catalog 充当索引,将 Address 映射到实际的 AssetBundle 位置。

  • 运行时通过 AddressCatalog 加载 AssetBundle 中的资源。

5.7.2 Addressables 代码实践

接下来,我们将通过一系列代码示例,演示如何使用 Addressables 进行资源加载和管理。

5.7.2.1 安装 Addressables 包

首先,确保你的 Unity 项目中安装了 Addressables 包。可以通过 Unity Package Manager (Window -> Package Manager) 搜索 "Addressables" 并安装。

5.7.2.2 将资源标记为 Addressable

要将资源标记为 Addressable,可以按照以下步骤操作:

  1. 打开 Addressables 窗口: Window -> Asset Management -> Addressable Assets -> Groups。

  2. 创建 Group (可选): 如果需要,可以点击 "Create Group" 按钮创建新的 Group 来组织资源。默认情况下,资源会被添加到 "Default Local Group"。

  3. 将资源拖拽到 Group 中: 在 Project 窗口中选择要标记为 Addressable 的资源 (例如 Prefab),然后拖拽到 Addressables 窗口的 Group 中。

拖拽资源后,资源将被标记为 Addressable,并在 Addressables 窗口中显示。你可以修改资源的 Address,并将其分配到不同的 Group。

5.7.2.3 通过 Address 加载资源

Addressables 提供了多种加载资源的方式,包括异步加载和同步加载。推荐使用异步加载,以避免阻塞主线程,提升游戏性能。

1. 异步加载 Prefab 并实例化:

using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public class AddressablesExample : MonoBehaviour { public string prefabAddress = "MyPrefab"; // Prefab 的 Address async void Start() { // 异步加载 Prefab AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>(prefabAddress); await handle.Task; // 等待加载完成 if (handle.Status == AsyncOperationStatus.Succeeded) { // 加载成功,实例化 Prefab GameObject prefab = handle.Result; Instantiate(prefab, transform); Debug.Log("Prefab instantiated successfully!"); } else { // 加载失败,处理错误 Debug.LogError("Failed to load prefab: " + handle.OperationException.Message); } // 释放资源句柄 (非常重要!) Addressables.Release(handle); } }

代码解释:

  • Addressables.LoadAssetAsync<GameObject>(prefabAddress): 异步加载指定 Address 的 GameObject 资源。返回一个 AsyncOperationHandle<GameObject> 对象,用于跟踪加载进度和获取加载结果。

  • await handle.Task;: 使用 await 关键字等待异步操作完成。这是 C# 的异步编程特性,可以避免回调地狱,使代码更易读。

  • handle.Status == AsyncOperationStatus.Succeeded: 检查异步操作是否成功完成。

  • handle.Result: 获取异步操作的结果,即加载的 GameObject 资源。

  • Addressables.Release(handle);: 释放资源句柄。这是 Addressables 资源管理的关键步骤。必须在资源不再使用时手动释放资源句柄,否则会导致内存泄漏。

2. 异步加载 Sprite 并设置到 Image 组件:

using UnityEngine; using UnityEngine.UI; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public class LoadSpriteExample : MonoBehaviour { public string spriteAddress = "MySprite"; // Sprite 的 Address public Image targetImage; // UI Image 组件 async void Start() { // 异步加载 Sprite AsyncOperationHandle<Sprite> handle = Addressables.LoadAssetAsync<Sprite>(spriteAddress); await handle.Task; if (handle.Status == AsyncOperationStatus.Succeeded) { // 加载成功,设置 Sprite 到 Image 组件 Sprite loadedSprite = handle.Result; targetImage.sprite = loadedSprite; Debug.Log("Sprite loaded and set to Image successfully!"); } else { // 加载失败,处理错误 Debug.LogError("Failed to load sprite: " + handle.OperationException.Message); } // 释放资源句柄 Addressables.Release(handle); } }

3. 同步加载资源 (不推荐在运行时频繁使用):

using UnityEngine; using UnityEngine.AddressableAssets; public class SyncLoadExample : MonoBehaviour { public string textureAddress = "MyTexture"; // Texture 的 Address void Start() { // 同步加载 Texture (会阻塞主线程) Texture2D texture = Addressables.LoadAssetSync<Texture2D>(textureAddress); if (texture != null) { // 加载成功,使用 Texture GetComponent<Renderer>().material.mainTexture = texture; Debug.Log("Texture loaded synchronously!"); } else { // 加载失败,处理错误 Debug.LogError("Failed to load texture synchronously!"); } // 同步加载的资源需要手动释放 (使用 ReleaseInstance) Addressables.ReleaseInstance(texture); // 注意这里使用 ReleaseInstance } }

注意:

  • Addressables.LoadAssetSync<T>(address) 方法是同步加载,会阻塞主线程,不推荐在运行时频繁使用,尤其是在加载大型资源时。

  • 同步加载的资源需要使用 Addressables.ReleaseInstance(resource) 方法进行释放,而不是 Addressables.Release(handle)

5.7.2.4 加载场景

Addressables 也可以用于加载场景。

using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.SceneManagement; public class LoadSceneExample : MonoBehaviour { public string sceneAddress = "MyScene"; // Scene 的 Address async void Start() { // 异步加载场景 (Additive 模式) AsyncOperationHandle<SceneInstance> handle = Addressables.LoadSceneAsync(sceneAddress, LoadSceneMode.Additive); await handle.Task; if (handle.Status == AsyncOperationStatus.Succeeded) { // 加载成功,获取 SceneInstance SceneInstance sceneInstance = handle.Result; Debug.Log("Scene loaded successfully!"); } else { // 加载失败,处理错误 Debug.LogError("Failed to load scene: " + handle.OperationException.Message); } // 场景卸载 (在需要时手动卸载) // Addressables.UnloadSceneAsync(handle); } }

代码解释:

  • Addressables.LoadSceneAsync(sceneAddress, LoadSceneMode.Additive): 异步加载场景。LoadSceneMode.Additive 表示以附加模式加载场景,不会卸载当前场景。

  • Addressables.UnloadSceneAsync(handle): 卸载已加载的场景。场景卸载也需要使用 AsyncOperationHandle 进行管理。

5.7.2.5 加载多个资源 (批量加载)

Addressables 支持批量加载多个资源,可以提高加载效率。

using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using System.Collections.Generic; public class BatchLoadExample : MonoBehaviour { public List<string> addressList = new List<string>() { "MyTexture1", "MyTexture2", "MyAudioClip" }; // 资源 Address 列表 async void Start() { List<AsyncOperationHandle> handles = new List<AsyncOperationHandle>(); List<object> loadedAssets = new List<object>(); // 存储加载的资源 foreach (string address in addressList) { // 异步加载每个资源 AsyncOperationHandle handle = Addressables.LoadAssetAsync<object>(address); // 使用 object 类型加载不同类型的资源 handles.Add(handle); } // 等待所有资源加载完成 await AsyncOperationHandle.WhenAll(handles); bool allSuccess = true; for (int i = 0; i < handles.Count; i++) { if (handles[i].Status == AsyncOperationStatus.Succeeded) { loadedAssets.Add(handles[i].Result); Debug.Log($"Resource '{addressList[i]}' loaded successfully!"); } else { Debug.LogError($"Failed to load resource '{addressList[i]}': {handles[i].OperationException.Message}"); allSuccess = false; } } if (allSuccess) { // 所有资源加载成功,进行后续处理 Debug.Log("All resources loaded successfully!"); // ... 使用 loadedAssets 中的资源 ... } // 释放所有资源句柄 foreach (var handle in handles) { Addressables.Release(handle); } } }

代码解释:

  • AsyncOperationHandle.WhenAll(handles): 等待所有 AsyncOperationHandle 对象完成。

  • 使用 object 类型加载资源,可以处理不同类型的资源加载。

  • 遍历 handles 列表,检查每个资源的加载状态,并获取加载结果。

  • 循环释放所有资源句柄。

5.7.3 Addressables 内容更新

Addressables 的一个重要优势是支持内容更新。通过 Addressables,可以发布新的 AssetBundle 到服务器,玩家可以在不重新下载整个游戏包的情况下,更新游戏内容。

内容更新流程 (简要描述):

图 5.7.2 Addressables 内容更新流程

步骤简述:

  1. 开发阶段: 开发者修改或添加新的 Addressable Asset。

  2. 构建 Addressables 内容: 使用 Addressables Build Script 构建新的 AssetBundle 和 Catalog。

  3. 上传到服务器: 将构建好的 AssetBundle 和 Catalog 上传到服务器 (例如 CDN)。

  4. 游戏启动时检查更新: 游戏启动时,通过代码请求服务器上的 Catalog 文件,与本地 Catalog 进行比较,判断是否有更新。

  5. 下载更新: 如果有更新,下载新的 AssetBundle 和 Catalog 文件。

  6. 加载资源: 游戏运行时,优先加载最新的 AssetBundle 中的资源。

代码示例 (检查更新并下载):

using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public class ContentUpdateExample : MonoBehaviour { public string catalogUrl = "http://your-server/catalog.json"; // 服务器 Catalog 地址 async void Start() { // 检查更新 AsyncOperationHandle<ContentUpdateResult> updateHandle = Addressables.CheckForCatalogUpdates(false); // false 表示不自动更新 Catalog await updateHandle.Task; if (updateHandle.Status == AsyncOperationStatus.Succeeded) { ContentUpdateResult updateResult = updateHandle.Result; if (updateResult.UpdateNeeded) { Debug.Log("Content update available!"); // 下载更新 await DownloadContentUpdates(); } else { Debug.Log("No content update available."); } } else { Debug.LogError("Failed to check for catalog updates: " + updateHandle.OperationException.Message); } Addressables.Release(updateHandle); } async System.Threading.Tasks.Task DownloadContentUpdates() { AsyncOperationHandle<List<string>> downloadHandle = Addressables.UpdateCatalogs(null); // null 表示更新所有 Catalog await downloadHandle.Task; if (downloadHandle.Status == AsyncOperationStatus.Succeeded) { List<string> updatedCatalogs = downloadHandle.Result; Debug.Log("Content update downloaded successfully!"); // ... 重启游戏或加载新的资源 ... } else { Debug.LogError("Failed to download content updates: " + downloadHandle.OperationException.Message); } Addressables.Release(downloadHandle); } }

代码解释:

  • Addressables.CheckForCatalogUpdates(false): 检查服务器上是否有 Catalog 更新。false 表示不自动更新 Catalog,只检查。

  • updateResult.UpdateNeeded: 判断是否需要更新。

  • Addressables.UpdateCatalogs(null): 下载所有 Catalog 更新。

  • 更新完成后,通常需要重启游戏或重新加载资源才能生效。

内容更新的详细配置和流程比较复杂,涉及到 Build Layout、Profile、服务器配置等,这里只是一个简单的示例。实际应用中需要根据项目需求进行详细配置和测试。

5.7.4 Addressables 高级应用与最佳实践

除了基本的资源加载和内容更新,Addressables 还有许多高级应用和最佳实践,可以进一步提升资源管理效率和游戏性能。

  • Labels (标签): 可以使用标签来标记 Addressable Asset,然后通过标签批量加载资源。例如,可以为所有 "特效" 资源添加 "Effect" 标签,然后通过 Addressables.LoadAssetsAsync<GameObject>(labels, null) 加载所有 "Effect" 标签的资源。

  • Profiles (配置): 使用 Profile 可以为不同的环境配置不同的 Addressables 设置,例如本地开发使用本地 AssetBundle,发布版本使用服务器 AssetBundle。

  • Build Layout (构建布局): 根据项目需求选择合适的 Build Layout,例如 Packed Playmode Scriptable Build Layout 可以加速本地迭代开发,Packed Assets 可以优化 AssetBundle 打包大小。

  • 内存管理: 务必在资源不再使用时及时释放资源句柄。可以使用 Addressables.Release(handle)Addressables.ReleaseInstance(resource) 方法释放资源。

  • 性能优化:

    • 异步加载: 优先使用异步加载,避免阻塞主线程。

    • 批量加载: 尽量使用批量加载,减少加载请求次数。

    • AssetBundle 压缩: 开启 AssetBundle 压缩,减小包体大小和下载时间。

    • 资源复用: 合理规划资源 Group 和 AssetBundle 打包策略,提高资源复用率,减少 AssetBundle 数量。

  • 自定义加载流程: Addressables 提供了丰富的 API,可以自定义加载流程,例如自定义 AssetBundle 下载器、缓存策略等。

5.7.5 总结

Unity Addressables 是一套强大而灵活的资源管理系统,可以有效解决传统资源管理方式的诸多弊端。通过学习和实践 Addressables 的核心概念、代码实践和高级应用,开发者可以构建更加高效、可维护、易于更新的游戏项目。

本章节主要涵盖了以下内容:

  • Addressables 的核心概念 (Addressable Asset, Address, Group, Catalog, Build Layout, Profile)。

  • 通过代码示例演示了如何使用 Addressables 进行 Prefab、Sprite、Texture、Scene 等资源的异步和同步加载。

  • 简要介绍了 Addressables 内容更新的流程和代码示例。

  • 提及了 Addressables 的高级应用和最佳实践 (Labels, Profiles, Build Layout, 内存管理, 性能优化)。

希望本章节能够帮助你快速入门并掌握 Unity Addressables 的使用,并在实际项目开发中灵活运用,提升资源管理效率和游戏质量。 深入学习 Addressables 还需要不断实践和探索,查阅官方文档和社区资源,才能充分发挥其强大的功能。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U