UsingAddressable.md 11.0 KB
Newer Older
O
migrate  
oceanxiao 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
# 使用Addressable Assets System进行资源按需加载
## 一、概述
对于Unity WebGL转换的小游戏启动耗时,资源下载通常是贡献最大的部分。这是由于手游APP往往很少针对首包资源进行特殊优化。
那么,接下来的问题是:小游戏中多大的首包资源合适? 剩余的游戏资源如何加载?
在此,我们建议得优化原则是:
> 1. 首包资源量不超过5M
> 2. 资源按需延迟加载,拆分得尽量细
   
本文介绍如何使用Unity新的资源管理流程[Addressable Assets System](https://docs.unity3d.com/Packages/com.unity.addressables@1.1/manual/index.html)进行资源的按需加载。

附可参考的项目:
https://git.weixin.qq.com/wechat-minigame/unity-webgl-addressable


## 二、Addressable在小游戏中的应用
### 2.1 什么是Addressable Assets System
Unity在2018版本中推出了Addressable Assets System(以下简称Addressable)的预览版本,并在2019的版本中已经成为正式版本,可以用于生产(仅表示发布时间,实际上大部分Unity版本都可正常使用)。
Addressable提供了以下能力:
> 低使用门槛:使用Addressable在开发前期就进入快速开发的阶段,使用任何你喜欢的资源管理技术,你都能快速的切换来Addressable系统中,几乎不需要修改代码。

> 依赖管理:Addressable系统不仅仅会帮你管理、加载你指定的内容,同时它会自动管理并加载好该内容的全部依赖。在所有的依赖加载完成,你的内容彻底可用时,它才会告诉你加载完成。

> 内存管理:Addressable不仅仅能记载资源,同时也能卸载资源。系统自动启用引用计数,并且有一个完善的Profiler帮助你指出潜在的内存问题。

> 内容打包:Addressable系统自动管理了所有复杂的依赖连接,所以即使资源移动了或是重新命名了,系统依然能够高效地找到准确的依赖进行打包。当你需要将打包的资源从本地移到服务器上面,Addressable系统也能轻松做到,几乎不需要任何代价。

### 2.2 相对于AssetsBundles的优势
O
oceanxiao 已提交
28 29
Unity中资源按需加载也可以使用老的AssetBundle,然而使用AB需要做不少的工作:标识Asset、组织Bundle、编译、Load/Unload、依赖关系以及后期维护的复杂工作。新一代的Addressable正是对这些痛点做了不少改进,开发者只需要将Asset设置为addressable然后加载即可,[功能强大并且学习曲线变得平滑]
(https://docs.google.com/document/d/1hPLNLdrF0qAvjEJTpKf-cuO_d4FCV0H2cqBeP1Zo6mA/edit)。
O
oceanxiao 已提交
30

O
migrate  
oceanxiao 已提交
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
<image src='../image/addressable7.png' width="600"/>


### 2.3 在小游戏中使用Addressable Assets System
无论是Addressable还是AssetBundle在微信小游戏底层都使用XHR进行远程资源访问,并使用微信小游戏文件存储系统进行缓存。对于已有的游戏资源,如果我们需要尽量少的工作量去做到像H5游戏按需加载,使用Addressable是最佳做法。


## 三、启动优化与资源优化实战
### 3.1 从首包开始
首包资源应该只包含首屏所需资源,比如Splash界面以及对应文案。
<image src='../image/addressable2.png' width="800"/>
首屏资源需要注意:
> 1. 导出场景不要勾选任何其他场景
> 2. 不要打包字体文件,字体往往压缩率很低。
> 3. 通过Addressable检查Bultin分组,特别注意不要随意放置资源到Resources目录,该目录将无条件被打包到首包中。
ocean2o11's avatar
ocean2o11 已提交
46 47 48 49 50

通常,Unity首资源包的压缩率是比较高的,因为大多数Unity built资源是以文本形式存在。开发者应尽量减少首资源包压缩后大小,以zip压缩后体积为准,3M左右最佳,不应超过5M。

部署首资源包需要注意:
> 1. 使用“小游戏分包”时,小游戏底层会自动进行压缩减少网络传输。
L
liangyizhou(周良宜) 已提交
51
> 2. 使用“CDN”时,务必在服务器对txt后缀开启“Brotli或gzip”。
O
migrate  
oceanxiao 已提交
52 53 54 55


### 3.2 资源按需加载
#### 3.2.1 场景动态加载
ocean2o11's avatar
ocean2o11 已提交
56
如前所述,我们构建时仅选择了splash/loading场景,那么主场景(如大厅/战斗等)该如何加载?此时我们可以**将每个场景单独作为Addressable分组**,在用到的时候才下载该场景包。
O
migrate  
oceanxiao 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
<image src='../image/addressable5.png' width="800"/>

使用Addressables.LoadSceneAsync可以动态加载场景与获取加载进度:
```
    IEnumerator LoadMain()
    {
        var handle = Addressables.LoadSceneAsync("Assets/RPGPP_LT/Scene/rpgpp_lt_scene_1.0.unity", LoadSceneMode.Single, true);
        handle.Completed  += (obj) =>
        {
            Debug.LogWarning($"Load async scene complete{obj.Status}");
        };

        while (!handle.IsDone)
        {
            // 在此可使用handle.PercentComplete进行进度展示
            yield return null;
         }
    }
```
选择分组,设置分组属性如下:
O
oceanxiao 已提交
77

O
migrate  
oceanxiao 已提交
78 79 80 81
<image src='../image/addressable8.png' width="800"/>

如果我们仅将场景作为分组,其中静态摆放的物件不单独设置为Addressable也会一并打包到场景所在bundle。那么,这是会产生一个问题:两个场景都使用同样资源是否产生冗余?答案是肯定的!!
那么,如何消除冗余呢?当我们Adressable面板的Tools-->Analyze进行分析时,可看到以下内容:
O
oceanxiao 已提交
82

O
migrate  
oceanxiao 已提交
83 84 85 86 87 88
<image src='../image/addressable9.png' width="800"/>
此时,我们应将这些冗余的内容单独进行设置为Addressable。而更为简单的做法是:选中“Check Duplicate Bundle Dependencies”,点击“Fixed Selected Rules”,工具会自动将冗余项逐个设置为Addressable。


### 3.2.2 物件动态加载
除了静态场景外,我们还会经常动态实例化(Instantiate)或在内存中创建资源对象。比如:
O
oceanxiao 已提交
89

O
migrate  
oceanxiao 已提交
90
<image src='../image/addressable3.png' width="600"/>
ocean2o11's avatar
ocean2o11 已提交
91 92

``` C#
O
migrate  
oceanxiao 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
public class LoadAssetScript : MonoBehaviour
{
    public GameObject somePrefab;
    private void Start()
    {
        Instantiate(somePrefab);
    }
}
```
然而,这样做有个非常严重的问题:Prefab本身以及依赖的所有资源需要在场景加载前完成。在小游戏环境,**这意味着即使还没调用Instantiate,这部分资源就必须准备好,这会极大影响场景初始化速度**

那么,如果我们希望把somePrefab以及它的依赖资源打包在Addressable分组进行按需下载应该如何做呢?答案是AssetReference。上述代码改写成:
```
public class LoadAssetScript : MonoBehaviour
{
    public AssetReference somePrefab;
    private void Start()
    {
        somePrefab.InstantiateAsync().Completed += (obj) =>
        {
           // 加载完成回调
        };
    }
}
``` 
或协程写法:
``` 
public class LoadAssetScript : MonoBehaviour
{
    public AssetReference somePrefab;
    private IEnumerator spawnSomathing()
    {
        var hanle = somePrefab.InstantiateAsync();
        while(!handle.IsDone) {yield return null;}
         // 加载完成回调
        var gameObject = hanle.Result;
    }
}
```
同时,对应的Prefab在editor中需设置为Addressable,并重新为somePrefab赋值。

ocean2o11's avatar
ocean2o11 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
### 3.3 小结
Unity WebGL转换的小游戏普遍存在首包资源较大的情况,而新Address提供了非常好的资源管理流程。我们建议开发者:
> 1. 精简首场景,首包资源中确保只包含轻量的首屏以及依赖资源
> 2. 延迟加载,避免业务逻辑需要全量资源的情况,设计上尽量按需加载
> 3. 资源拆分,利用Addressable进行更灵活和细粒度的拆解
> 4. 预加载,根据优先级设置需要预加载的分包,利用网络空闲期


## 四、如何优雅地异步加载

### 4.1 最基本的异步回调
``` C#
private void TextureHandle_Completed(AsyncOperationHandle<Texture2D> handle) {
    if (handle.Status == AsyncOperationStatus.Succeeded) {
        Texture2D result = handle.Result;
        // The texture is ready for use.
    }
}

void Start() {
    AsyncOperationHandle<Texture2D> textureHandle = Addressables.LoadAsset<Texture2D>("mytexture");
    textureHandle.Completed += TextureHandle_Completed;
}

``` 

### 4.2 使用协程
``` C#
public IEnumerator Start() {
    AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>("mytexture");

    //if the handle is done, the yield return will still wait a frame, but we can skip that with an IsDone check
    if(!handle.IsDone)
        yield return handle;

    if (handle.Status == AsyncOperationStatus.Succeeded) {
        Texture2D texture = handle.Result;
        // The texture is ready for use.
        // ...
    // Release the asset after its use:
        Addressables.Release(handle);
    }
}
```

### 4.3 使用await
``` C#
public async Start() {
    AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
    await handle.Task;
    // The task is complete. Be sure to check the Status is successful before storing the Result.
}
O
migrate  
oceanxiao 已提交
186
```
ocean2o11's avatar
ocean2o11 已提交
187 188 189 190 191 192 193 194


## 五、旧系统资源改造
资源系统迁移可参考Unity官方文档[Upgrading to the Addressables system](https://docs.unity3d.com/Packages/com.unity.addressables@1.18/manual/AddressableAssetsMigrationGuide.html#the-assetbundles-method)
## 5.1 Resource改造
使用这种方式加载资源,通常需要再Asset或其子目录下创建Resources的文件夹,然后使用类似这种方式加载:

``` C#
O
migrate  
oceanxiao 已提交
195 196 197 198 199
       TextAsset text = Resources.Load<TextAsset>("MyConfig");
```
然而,Resources目录的内容都会被打包进首包资源,对于小游戏来说是**不推荐使用的方式**
开发者在Addressable的default中能看到所有这些资源,我们需要将这些资源设置为“Addressable”,Unity将自动移动到“Resources_Moved”目录。
加载代码改写成:
ocean2o11's avatar
ocean2o11 已提交
200 201 202 203 204 205 206
``` C#
        var handle = Addressables.LoadAssetAsync<TextAsset>("MyConfig");
        if(!handle.IsDone) yield return handle;
         // 加载完成回调
         if (handle.Status == AsyncOperationStatus.Succeeded) {
            var gameObject = hanle.Result;
         }
O
migrate  
oceanxiao 已提交
207 208
```

ocean2o11's avatar
ocean2o11 已提交
209 210
## 5.2 AssetsBundle迁移
当打开Addressables Groups时,Unity提供了将所有AssetsBundle迁移到Addressable Asset Groups的功能。
O
migrate  
oceanxiao 已提交
211

ocean2o11's avatar
ocean2o11 已提交
212 213 214

## 六、部署
### 6.1  Addressable编译与部署
O
migrate  
oceanxiao 已提交
215 216
默认情况下,当编译Addressable资源时会输出到Library/com.unity.addressables/,项目发布为WebGL或转换为小游戏时Unity会自动拷贝Bundle文件到最终的生成目录下。我们只需要将对应的StreammingAssets上传到对应的CDN服务器即可。

ocean2o11's avatar
ocean2o11 已提交
217 218
### 6.2 资源预下载
 为了充分利用网络带宽,在网络空闲时可预下载游戏需要用到的AB包。详细配置请参考[使用预下载功能](UsingPreload.md)
O
migrate  
oceanxiao 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235


### 五、参考资料
1. Addressable Asset System for Unity (Overview)
https://docs.google.com/document/d/1hPLNLdrF0qAvjEJTpKf-cuO_d4FCV0H2cqBeP1Zo6mA/edit

2. 官方Demo
https://github.com/Unity-Technologies/Addressables-Sample

3. 视频范例
https://drive.google.com/file/d/1w-Lh_jsD2VSHNvkzexJLSc6bA3gUQC8d/view   

4. uwa关于Addressable的介绍
https://blog.uwa4d.com/archives/USparkle_Addressable4.html

4. Addressable与AssetBundle的对比
https://www.jianshu.com/p/8009c16fcab3