1

随着Unity版本的更新,加载AssetBundle的API也在不断变化,熟悉这些API才获得更好的性能,在这里对加载方式做个整理与总结。

WWW

(官方已淘汰WWW类,如果是5.x之前的老版本工程可以继续使用WWW,如果是5.x之后的工程请使用UnityWebRequest类替代)
Unty4.x - 5.x 用WWW类加载
WWW.LoadFromCacheOrDownload() 通过Url和版本号自动缓存资源包 注意必须是资源包 不能是其他格式 不能加密

www加载样例:

using UnityEngine;
using System.Collections;

public class LoadFromCacheOrDownloadExample : MonoBehaviour
{
    IEnumerator Start()
    {
        while (!Caching.ready)
            yield return null;

        using (var www = WWW.LoadFromCacheOrDownload("http://myserver.com/myassetBundle.unity3d", 5))
        {
            yield return www;
            if (!string.IsNullOrEmpty(www.error))
            {
                Debug.Log(www.error);
                yield return null;
            }
            var myLoadedAssetBundle = www.assetBundle;

            var asset = myLoadedAssetBundle.mainAsset;
        }
    }
}

UnityWebRequest

Unity2018.1以后 官方建议用UnityWebRequest类代替WWW类 据说WWW有性能问题
UnityWebRequest.GetAssetBundle() 兼容WWW.LoadFromCacheOrDownload()的功能 并进行了扩展
UnityWebRequest加载样例:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

/// <summary>
/// 下载测试
/// <para>原文地址:https://segmentfault.com/a/1190000019656656</para>
/// </summary>
public class WebRequestTest : MonoBehaviour {

    private void Start () {
        StartCoroutine(DoLoadFile());
        //StartCoroutine(DoLoadTexture());
        //StartCoroutine(DoLoadAssetBundle());
    }

    // 下载文本或二进制文件
    private IEnumerator DoLoadFile() {
        string url = Application.streamingAssetsPath + "/" + "test.txt";
        using (UnityWebRequest request = UnityWebRequest.Get(url)) {
            yield return request.SendWebRequest();
            if (request.isHttpError || request.isNetworkError) {
                // 下载出错
                print(request.error);
            } else {
                // 下载完成
                string text = request.downloadHandler.text;
                byte[] bytes = request.downloadHandler.data;
                // 优先释放request 会降低内存峰值
                request.Dispose();
            }
        }
    }

    // 下载图片
    private IEnumerator DoLoadTexture() {
        string url = Application.streamingAssetsPath + "/" + "test.png";
        using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(url)) {
            yield return request.SendWebRequest();
            if (request.isHttpError || request.isNetworkError) {
                // 下载出错
                print(request.error);
            } else {
                // 下载完成
                Texture2D texture = (request.downloadHandler as DownloadHandlerTexture).texture;
                // 优先释放request 会降低内存峰值
                request.Dispose();
            }
        }
    }

    // 下载AssetBundle
    private IEnumerator DoLoadAssetBundle() {
        string url = Application.streamingAssetsPath + "/" + "test.ab";
        using (UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url)) {
            yield return request.SendWebRequest();
            if (request.isHttpError || request.isNetworkError) {
                // 下载出错
                print(request.error);
            } else {
                // 下载完成
                AssetBundle assetBundle = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
                // 优先释放request 会降低内存峰值
                request.Dispose();
            }
        }
    }

}

最重要的API

1.UnityWebRequestAssetBundle.GetAssetBundle(url, hash)
通过http下载资源包并直接解压到本地磁盘 解压格式类似于Uncompression格式。
PC路径为:C:\Users\{UserName}\AppData\LocalLow\Unity\{CompanyName}_{ProductName}\{BundleName}\{Hash}\

2.AssetBundle.LoadFromFileAsync(url)
通过Url异步加载本地资源包,这是读取本地资源包最好的方法,速度也非常快。不建议用LoadFromFile() 虽然速度最快,但是阻塞主进程,影响体验。

3.AssetBundle.LoadFromMemoryAsync(bytes)
通过byte[]异步加载本地资源包,用来读取加密的资源包。注意资源包会完全缓存进内存中,内存占用巨大,少用。

4.AssetBundle.RecompressAssetBundleAsync(fromUrl, toUrl, compression)
注:Unity 2018.3新增API
可以把资源包重新压缩为Uncompression或LZ4格式,这下资源包的格式完全变得可控了,可以应用更多策略处理资源包。

以上4个API处理资源包完全够用了,剩下的就是一个http下载API

5.UnityWebRequest.Get(url)
通过http下载二进制文件到内存,可以通过File.WriteAllBytes(url, bytes)保存到本地,下载资源包或其他文件可以用这个。(这个并不是下载远程文件最好的API,因为不支持直接下载到本地磁盘,也不支持断点续传,如果要严格控制内存,可以考虑引入其他http插件实现)

官方文档1:
https://docs.unity3d.com/2018...

官方文档2:
https://docs.unity3d.com/Scri...
同步地从磁盘上的文件加载一个资源包。
该函数支持任何压缩类型的包。
在lzma压缩格式下,数据将被解压到内存中。未压缩格式和块压缩格式可以直接从磁盘读取。
与LoadFromFileAsync相比,这个版本是同步的,在创建AssetBundle对象之前不会返回。
这是加载AssetBundle最快的方法。

PS:从官方文档可以看出 似乎不压缩或LZ4要比LZMA性能更好 百度后发现确实如此
LZ4要比LZMA加载速度更快 内存占用更小 但是包体积稍大一些

参考资料:
《关于LZMA和LZ4压缩的疑惑解析》https://blog.csdn.net/weixin_...
《同样的场景,分别用LZMA和LZ4压缩方式,通过AssetBundle.LoadFromFile(Async)加载,内存上可以相差多少》
https://answer.uwa4d.com/ques...
《AssetBundleCompression Bundle的压缩格式》http://blog.sina.com.cn/s/blo...

各API加载测试:
先说结论:当使用无压缩和LZ4压缩格式时,加载速度极快,内存占用很小,lz4居然比无压缩还要快,有点意外,本以为无压缩会更快的。
当使用LZMA格式时,加载时间急剧增加,内存上升急剧增加,相当于在内存里完全缓存一个未压缩格式的资源包。

AssetBundle.LoadFromFileAsync(url)本地加载测试:

压缩格式 包体积 加载时间 内存上升
No 50.1M 17ms左右 1M左右
LZ4 48.5M 10ms左右 1M左右
LZMA 41.7M 2400ms左右 51M左右

各API加载测试:
测试资源包:test.ab 未压缩:50.1M LZ4压缩: 48.5M LZMA压缩:41.7M

压缩格式:LZ4 大小:48.5M 测试平台:PC 本地文件

API 加载时间 内存上升 说明
new WWW(path) 48ms 49M 排除误差 内存基本与资源体积一致
UnityWebRequest.Get(path) 48ms 49M 排除误差 内存基本与资源体积一致
UnityWebRequest.GetAsssetBundle(path) 96ms 1M 第一次需要下载并解压 缓存到本地
UnityWebRequest.GetAsssetBundle(path) 12ms 1M 第二次开始 加载速度很快 内存很小
AssetBundle.LoadFromFile(path) 3ms 1.1M 加载速度最快 内存很小 只缓存索引
AssetBundle.LoadFromFileAsync(path) 18ms 0.8M 加载速度极快 内存最小 异步版LoadFromFile
AssetBundle.LoadFromMemory(path) 78ms 52M 缓存解压后的资源 内存占用最大
AssetBundle.LoadFromMemoryAsync(path) 91ms 51M 缓存解压后的资源 异步版LoadFromMemory

压缩格式:LZMA 大小:41.7M 测试平台:PC 本地文件

API 加载时间 内存上升 说明
new WWW(path) 32ms 42M 排除误差 内存基本与资源体积一致
UnityWebRequest.Get(path) 32ms 42M 排除误差 内存基本与资源体积一致
UnityWebRequest.GetAsssetBundle(path) 2465ms 1M 第一次需要下载并解压 缓存到本地
UnityWebRequest.GetAsssetBundle(path) 12ms 1M 第二次开始 加载速度很快 内存很小
AssetBundle.LoadFromFile(path) 2244ms 52M 加载速度慢 内存最大 缓存解压后的资源
AssetBundle.LoadFromFileAsync(path) 2387ms 51M 加载速度最慢 内存很大 异步版LoadFromFile
AssetBundle.LoadFromMemory(path) 2249ms 52M 缓存解压后的资源 内存占用最大
AssetBundle.LoadFromMemoryAsync(path) 2377ms 51M 缓存解压后的资源 异步版LoadFromMemory

总结:
多看官方更新日志,多看官方样例,多看官方文档,熟悉官方API才能更好的掌控性能。
如果对资源包的处理比较复杂,需要自己定制资源包生成器和加载器。
注意资源包的压缩格式和加载API,对性能影响非常大。
LZMA:高压缩、低性能、高内存、体积最小格式。
LZ4 :中压缩、高性能、低内存、性价比最高格式。
两者性能差距非常大,使用LZMA加载巨慢,要特别注意。

LZMA和LZ4该如何选择呢?
小孩子才做选择题,成年人当然是全都要!既要高压缩,体积小的文件格式,又要加载速度快,内存小的性能优势,
高压缩和高性能要兼顾,可以考虑用高压缩格式传输文件,解压为低压缩格式后后保存到本地,用本地加载API提高速度。
WWW.LoadFromCacheOrDownload()和UnityWebRequest.GetAsssetBundle()提供的就是这个功能,推荐使用后者。

Unity 2018.3新增API提供了转换资源包压缩格式的功能,这样可以用zip或其他高压缩格式下载文件,解压后用API转换为LZ4格式高速读取,可以做到最小下载体积和最快加载速度兼顾,就是实现起来需要一些时间处理细节。

参考资料:
《Unity Assetbundle的加载方式的效率和内存占用》https://blog.csdn.net/u012740...
《WWW.LoadFromCacheOrDownLoad官方文档》https://docs.unity3d.com/2018...
《UnityWebRequest.GetAssetBundle官方文档》https://docs.unity3d.com/2018...

转载请标明原文地址:https://segmentfault.com/a/11...


冰封百度
233 声望43 粉丝

Unity游戏程序员一枚。生命不息,学习不止。