# Unity Addressable Asset System 演化史、概念和使用流程

## 演化史：Unity 资源管理的发展历程

### 1. 传统资源管理 (Unity 早期)

在 Addressable 系统出现之前，Unity 的资源管理主要依赖以下方式：

1. **直接引用** - 将资源直接拖拽到 Inspector 面板中，由编辑器管理依赖关系
2. **Resources 文件夹** - 通过 `Resources.Load()` 动态加载资源
3. **StreamingAssets** - 不被 Unity 处理的原始文件，适合不需要处理的数据

这些方法存在严重缺点：

- Resources 文件夹中的所有内容都会被打包，无法进行精细控制
- 无法进行资源的热更新
- 内存管理困难，容易造成资源泄漏
- 随着项目增大，构建时间变长

### 2. Asset Bundle 系统 (Unity 5+)

为解决上述问题，Unity 引入了 Asset Bundle 系统：

- 允许将资源打包成独立的二进制文件
- 支持按需加载和卸载
- 支持热更新和网络下载
- 减小初始安装包体积

但 Asset Bundle 也有明显缺点：

- API 复杂，需要手动管理依赖关系
- 资源重复问题难以处理
- 需要手写很多样板代码
- 版本管理和更新困难

### 3. Addressable Asset System (Unity 2018.2+)

2018 年，Unity 引入了 Addressable Asset System，作为 Asset Bundle 的高级封装：

- 正式版本在 Unity 2019.3 中发布
- 提供简单、统一的资源加载接口
- 自动管理内存和依赖关系
- 集成了异步加载功能
- 提供内置的分析和调试工具

### 4. 持续优化和发展

Addressable 系统持续得到改进：

- **2020.1**: 内存管理优化，增加批量操作
- **2020.2**: 改进了预加载和缓存机制
- **2021.1**: 简化了构建流程，改进了编辑器工作流
- **2021.2+**: 改进与 Scriptable Build Pipeline 的集成，提高构建性能
- **2022+**: 进一步提升加载性能，增强与 Burst 编译器的兼容性

## 核心概念

### 1. 基础概念

**可寻址资源 (Addressable Asset)**: 通过唯一地址或标签可以动态加载的资源

**地址 (Address)**: 每个可寻址资产的唯一字符串标识符

**标签 (Label)**: 可以应用于多个资源的分类标记

**资源组 (Asset Group)**: 共享相同设置的资源集合，通常打包在一起

### 2. 技术架构

**内容目录 (Content Catalog)**: 包含所有可寻址资源的信息及其位置的数据库

**资源位置 (Asset Location)**: 每个资源的物理位置信息

**资源引用 (Asset Reference)**: 编辑时引用可寻址资产的序列化对象

**Operation Handle**: 异步加载操作的句柄，用于跟踪和管理资源加载

### 3. 构建和部署

**打包模式**:

- **打包到本地**: 将资源打包到应用程序内
- **打包并上传**: 将资源打包并准备上传到远程服务器
- **使用现有构建**: 利用已有构建的资源

**组打包策略**:

- **Pack Together**: 将组中所有资产打包到一个 bundle
- **Pack Separately**: 每个资产单独打包
- **Pack By Label**: 按标签分组打包
- **Pack Together By Label**: 有相同标签的资产打包到一起

**内容更新**:

- 增量内容构建
- 内容目录更新
- 远程加载路径设置

## 使用流程

### 1. 项目设置

1. **安装 Addressables 包**:
    
    - 打开 Package Manager
    - 搜索 "Addressables"
    - 安装最新版本
2. **初始化 Addressables 系统**:
    
    - 菜单: Window > Asset Management > Addressables > Groups
    - 点击 "Create Addressables Settings" 按钮
3. **配置基本设置**:
    
    - 远程加载路径
    - 本地加载路径
    - 构建和加载规则

### 2. 资源标记与组织

1. **将资源标记为 Addressable**:
    
    - 选择资源
    - 在 Inspector 中找到 "Addressable" 复选框并勾选
    - 设置唯一的地址或使用默认路径
2. **创建和管理资源组**:
    
    - 在 Addressables Groups 窗口中创建新组
    - 设置组的打包规则和加载方式
    - 将资源分配到不同的组
3. **添加标签**:
    
    - 选择资源
    - 在 Inspector 的 Addressables 部分添加标签
    - 使用标签对资源进行分类

### 3. 资源加载

1. **基本加载**:
    
    ```csharp
    // 通过地址加载
    var handle = Addressables.LoadAssetAsync<GameObject>("Assets/Prefabs/Character.prefab");
    handle.Completed += (operation) => 
    {
        if (operation.Status == AsyncOperationStatus.Succeeded)
        {
            GameObject character = operation.Result;
            Instantiate(character);
        }
    };
    ```
    
2. **通过标签加载多个资源**:
    
    ```csharp
    // 加载所有带有特定标签的资源
    var loadHandle = Addressables.LoadAssetsAsync<GameObject>(
        "enemy", // 标签
        (GameObject obj) => 
        {
            // 每加载一个资源就会调用一次
            Instantiate(obj);
        }
    );
    ```
    
3. **使用 AssetReference**:
    
    ```csharp
    // 在 Inspector 中可赋值的引用
    public AssetReference characterReference;
    
    public void LoadCharacter()
    {
        characterReference.LoadAssetAsync<GameObject>().Completed += (handle) => 
        {
            Instantiate(handle.Result);
        };
    }
    ```
    

### 4. 资源释放与内存管理

1. **释放单个资源**:
    
    ```csharp
    Addressables.Release(handle);
    ```
    
2. **释放 AssetReference**:
    
    ```csharp
    characterReference.ReleaseAsset();
    ```
    
3. **释放多个资源**:
    
    ```csharp
    Addressables.Release(loadHandle);
    ```
    

### 5. 高级用法

1. **预加载和预实例化**:
    
    ```csharp
    // 预加载资源但不立即使用
    var preloadHandle = Addressables.DownloadDependenciesAsync("preload_group");
    ```
    
2. **资源位置检查**:
    
    ```csharp
    var locationHandle = Addressables.LoadResourceLocationsAsync("character");
    locationHandle.Completed += (op) => 
    {
        foreach(var location in op.Result)
        {
            Debug.Log($"Location: {location.PrimaryKey}, Type: {location.ResourceType}");
        }
    };
    ```
    
3. **远程内容目录更新**:
    
    ```csharp
    var updateHandle = Addressables.CheckForCatalogUpdates();
    updateHandle.Completed += (op) => 
    {
        if (op.Result.Count > 0)
        {
            // 有更新可用，应用更新
            var updateCatalogHandle = Addressables.UpdateCatalogs(op.Result);
        }
    };
    ```
    

### 6. 构建和部署流程

1. **构建 Addressables 内容**:
    
    - 打开 Addressables Groups 窗口
    - 点击 Build > New Build > Default Build Script
2. **设置远程部署**:
    
    - 配置 Addressables 配置文件的远程加载路径
    - 选择 Build > New Build > Content Update
3. **增量内容更新**:
    
    - 修改远程资源内容
    - 使用内容更新工作流程构建只包含变化的资源
    - 上传新生成的内容到远程服务器

### 7. 分析和优化

1. **使用 Addressables Profiler**:
    
    - Window > Asset Management > Addressables > Event Viewer
    - 分析资源加载和卸载时间
    - 跟踪内存使用情况
2. **分析冗余**:
    
    - Window > Asset Management > Addressables > Analyze
    - 检查重复的资源
    - 查找未使用的资源
3. **优化策略**:
    
    - 合理分组，减少 bundle 数量
    - 使用标签优化加载和内存管理
    - 预加载常用资源
    - 及时释放不需要的资源

## 最佳实践

1. **地址和标签命名**:
    
    - 使用清晰、一致的命名规则
    - 考虑使用路径风格的地址，如 "characters/hero"
    - 用标签表示功能或类别，如 "enemies"、"level1"
2. **资源分组策略**:
    
    - 按功能模块分组
    - 按加载时机分组 (预加载、按需加载)
    - 按更新频率分组 (频繁更新、稳定内容)
3. **内存管理**:
    
    - 跟踪所有加载的资源
    - 使用适当的资源释放模式
    - 定期检查内存使用情况
4. **异步加载处理**:
    
    - 使用协程或 async/await 管理异步操作
    - 实现加载屏幕或进度指示
    - 考虑加载超时和错误处理
5. **运行时更新**:
    
    - 实现应用启动时的目录更新检查
    - 提供用户可视的更新进度
    - 处理下载失败和恢复策略

## 结论

Addressable Asset System 代表了 Unity 资源管理系统的一次重大进步，它简化了开发流程，提高了性能，并增强了处理大型项目的能力。通过采用 Addressables，开发者可以实现：

- 更精细的资源控制
- 更高效的内存管理
- 更简单的热更新流程
- 更快的加载时间
- 更小的初始安装包大小

虽然学习曲线相比传统方法略有提高，但长期来看，Addressables 带来的好处远远超过了初始的学习成本，特别是对于内容丰富的大型项目。