学习 Unity AssetBundle(AB包)

1,070 阅读8分钟

学习 Unity AssetBundle(AB包)

AssetBundle 是为了资源更新

  • AssetBundle 是一个压缩包包含 模型/贴图/预制体/声音/场景,可以在程序运行时被加载。

  • AssetBundle 自身存在想换依赖关系

  • AssetBundle 压缩包可以采用 LZMA/LZ4 压缩算法,减少压缩包大小。

  • 把一些安装包的内容放在 AssetBundle 里面可以减少安装包大小。

AssetBundle 可以分为一个序列化文件多个资源文件

  • 序列化文件:模型/预制体/场景

  • 资源文件:贴图/声音

AssetBundle 的使用步骤

1 指定资源 AssetBundle 属性

  • 1.1 设置资源的目录

    image-20230509152816633

    资源包的名字不区分大小写

    资源包的后缀可以随意

    image-20230509152918522

    None代表不讲当前资源打进 AssetBundle

    New...代表新建一个资源包名称,如果新建的名称已经存在则会自动使用之前名称。

    Remove Unused Names移除新建没有用到的资源包名字

    image-20230509153200456

    比如上面设置 AssetBundle 的资源包名称叫做wall,后缀叫做assetBundle,那么生成的资源包名字就是wall.assetBundle

    通过/可以划分目录,比如资源包名称叫做prefabs/wall则是创建一个prefabs文件夹,把wall存放在prefabs 文件夹里面。

  • 1.2 设置资源的名称

    这个默认即可

2 构造 AssetBundle 包

构造 AssetBundle 包需要通过写编辑器插件来做到打包,对于编辑器插件目前我也不会,后续我会写学习的编辑器插件的文章。目前只能跟着教程一步步的来了。

2.1 新建一个 Editor 文件夹用来保存编辑器插件脚本

image-20230509154332461

2.2 在 Editor 目录新建一个脚本用来打包 AssetBundle

image-20230509154856909

using UnityEditor;
using System.IO;

public class CreateAssetBundles
{
    // 在 Assets 菜单新增加一个 Build AssetBundles 子菜单用来执行 BuildAllAssetBundles 方法
    [MenuItem("Assets/Build AssetBundles")]
    public static void BuildAllAssetBundles()
    {
        string assetBundlePath = "AssetBundles";
        if (!Directory.Exists(assetBundlePath))
        {
            // 如果 AssetBundles 目录不存在则创建目录
            Directory.CreateDirectory(assetBundlePath);
        }
        // 编译所有 AssetBundle 存放在主目录 AssetBundles 下面
        // BuildAssetBundleOptions 编译 AssetBundle 类型 默认为 None
        // BuildTarget 编译目标目前在 macOS 上所以设置为 StandaloneOSX
        BuildPipeline.BuildAssetBundles(assetBundlePath, BuildAssetBundleOptions.None, BuildTarget.StandaloneOSX);
    }
}
  • BuildAssetBundleOptions

    • None 构建没有任何特殊选项的assetBundle。

      会使用 LZMA 算法进行压缩,压缩包更小,但是加载时间更长。(通常选择默认即可)

    • UncompressedAssetBundle 在创建AssetBundle时不要压缩数据。

      不压缩,包大,但是加载快

    • ChunkBasedCompression 在创建AssetBundle时使用基于块的LZ4压缩。

      LZ4 没有 LZMA 压缩率高,但是加载指定的资源不需要解压全部。

      使用 LZ4 压缩可以获得不压缩媲美的加载速度,而且大小比不压缩小。

    • 其他不常用

  • BuildTarget

    • StandaloneOSX 构建独立的macOS (Intel 64位)。
    • StandaloneWindows 构建一个独立的Windows。
    • iOS 创建一个iOS播放器。
    • Android 构建一个Android .apk独立应用。
    • StandaloneWindows64 构建一个Windows 64位的独立版本。
    • WebGL 构建 WebGL 应用
    • WSAPlayer 创建一个Windows Store Apps播放器。
    • StandaloneLiunx 构建一个64位的Linux独立版本
    • PS4 创建PS4单机版。
    • XboxOne 制作Xbox One单机版。
    • tvOS 构建苹果的tvOS平台。
    • Switch 创建一个任天堂Switch播放器
    • Stadia 建立一个独立的Stadia。
    • LinuxHeadlessSimulation 建立一个LinuxHeadlessSimulation独立。
    • PS5 构建到PlayStation 5平台。

回到编辑器重新编译,已经在 Assets 目录看到新增加的功能了。

image-20230509161949667

点击 Build AssetBundles 菜单

image-20230509162538790

需要打包的资源已经被打包出来了。

AssetBundles 给当前的目录生成对应的 AssetBundle 文件

AssetBundles.mainfest 对应目录 AssetBundle 的描述文件

wall.assetBundle 我们需要打包 AssetBundle 的文件

wall.assetBundle.mainfest 我们打出来 AssetBundle 文件的描述文件

3 上传 AssetBundle 包

使用 Caddy 搭建测试服务器进行测试

Caddy官网 caddyserver.com/

安装 Caddy (macOS)
brew install caddy
启动服务
# [~/Downloads] 代表你需要启动那个目录作为静态文件目录 [1010] 表示你开放的端口
caddy file-server --browse --root ~/Downloads --listen :1010

从网络下载 AssetBundle

private IEnumerator LoadAssetBundleFromWeb()
{
    // 下载的 AssetBundle 的路径
    string uri = "http://127.0.0.1:1010/wall.assetbundle";
    // 创建下载 AssetBundle 的请求
    UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri);
    // 等待下载完毕
    yield return request.SendWebRequest();
    // 如果下载结果不是成功 则打印出错误信息
    if (request.result != UnityWebRequest.Result.Success)
    {
        print(request.error);
    }
    else
    {
        // 下载成功则加载 AssetBundle
        assetBundle = DownloadHandlerAssetBundle.GetContent(request);
      	// request.downloadedBytes 可以将下载的资源放在本地进行缓存
        // request.downloadProgress 可以获取下载的进度
    }
}

4 加载 AssetBundle 包和读取包里面的资源

4.1 在需要加载资源的场景新建一个空物体

image-20230509170147777

空物体的名称可以随意

4.2 新建一个加载 AB 包的脚本挂在到我们新建的游戏物体上面

image-20230509170347007

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LoadAB : MonoBehaviour
{
    private AssetBundle assetBundle;

    private void Start()
    {
        bool isRelease = false;
        if (isRelease)
        {
            // TODO: 从网络加载资源
        }
        else
        {
            // 从本地加载资源
            LoadAsstBundleFromFile();
        }
        // 从资源包加载 Wall 的预制体资源
        GameObject go = assetBundle.LoadAsset<GameObject>("Wall");
        // 展示在当前屏幕上
        Instantiate(go);
    }

    private void LoadAsstBundleFromFile()
    {
        // 从当前工程下面 AssetBundles/wall.assetbundle 加载资源
      	// !如果有依赖其他 AB 包 则需要先加载依赖的 AB 包
      	// 先加载那个包没有要求 但是使用前必须保证依赖的包已经加载到内存里面
        assetBundle = AssetBundle.LoadFromFile("AssetBundles/wall.assetbundle");
    }
}

我们运行 Unity 工程

image-20230509171556069

此时我们存在于 AssetBundle 里面的资源已经被加载出来了。

5 AssetBundle 读取里面的资源

1 根据名称获取指定的资源

// 从资源包加载 Wall 的预制体资源
GameObject go = assetBundle.LoadAsset<GameObject>("Wall");

// 从异步进行加载
AssetBundleRequest bundleRequest = assetBundle.LoadAssetAsync<GameObject>("Wall");
yield return bundleRequest;
GameObject go1 = bundleRequest.asset as GameObject;

2 获取 AssetBundle 里面所有的资源

Object[] assets = assetBundle.LoadAllAssets();

// 从异步进行加载
AssetBundleRequest bundleRequest = assetBundle.LoadAllAssetAsync();
yield return bundleRequest;
Object[] assets1 = bundleRequest.allAssets as Object[];

加载 AssetBundle 资源的四种方式

1 AssetBundle.LoadFromMemoryAsync

从内存进行加载

IEnumerator LoadFromMemoryAsync(string path)
    {
        AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
        yield return createRequest;
        AssetBundle bundle = createRequest.assetBundle;
        var prefab = bundle.LoadAsset<GameObject>("MyObject");
        Instantiate(prefab);
    }

2 AssetBundle.LoadFromFile

从本地的文件进行加载

var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));
if (myLoadedAssetBundle == null)
{
    Debug.Log("Failed to load AssetBundle!");
    return;
}
var prefab = myLoadedAssetBundle.LoadAsset<GameObject>("MyObject");
Instantiate(prefab);

3 WWW.LoadFromCacheOrDownload(废弃)

如果网络资源在缓存不存在就下载,否则就从缓存进行加载

4 UnityWebRequest

通过网络将资源进行下载

private IEnumerator LoadAssetBundleFromWeb()
{
    // 下载的 AssetBundle 的路径
    string uri = "http://127.0.0.1:1010/wall.assetbundle";
    // 创建下载 AssetBundle 的请求
    UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri);
    // 等待下载完毕
    yield return request.SendWebRequest();
    // 如果下载结果不是成功 则打印出错误信息
    if (request.result != UnityWebRequest.Result.Success)
    {
        print(request.error);
    }
    else
    {
        // 下载成功则加载 AssetBundle
        assetBundle = DownloadHandlerAssetBundle.GetContent(request);
    }
}

AssetBundle 资源分组策略(仅供参考)

1 按照逻辑实体分组

1.1 一个 UI 界面或者所有 UI 界面一个包

界面里面的贴图和布局信息一个包

1.2 一个角色或者所有角色一个包

这个角色的模型和动画一个包

1.3 所有的场景共享元素一个包

包括贴图和模型

2 按照类型分组

2.1 所有声音文件一个包

2.2 所有的 Shader 一个包

2.3 所有模型一个包

2.4 所有材质一个包

3 按照使用分组

3.1 按照某一段时间用的资源一个包

3.2 按照关卡,一个关卡一个包

3.3 一个场景一个包

4 分组总结

把经常更新的资源单独放在一个包,把不经常更新的资源放在一个包。

把需要同时加载的资源放在一个包

可以把共享的资源放在一个包

把需要同时加载的小资源放在一个包

如果对于同一种资源有多个版本,可以按照后缀来区分。

Manifest 文件

  • CRC 校验码是校验下载文件是否被改动
  • Assets 描述包里面有什么样的资源
  • Dependencies: 描述了是否依赖其他 AB 包

获取 Manifest 文件的内容

// 正常的获取到最上层的 AssetBundle
AssetBundle assetBundle1 = AssetBundle.LoadFromFile("AssetBundles/AssetBundles");
// 名字 AssetBundleManifest 是定死的
AssetBundleManifest manifest = assetBundle1.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

获取到所有的包名

string[] bundleNames = manifest.GetAllAssetBundles();

获取对应包的所有依赖包

manifest.GetAllDependencies("prefabs/cube.unity3d")

AssetBundle 卸载

UnLoad(false) 只会卸载不使用的资源

我们可以通过 Resources.UnloadUnusedAssets() 进行卸载没有卸载的资源

在场景切换的时候自动会调用 Resources.UnloadUnusedAssets() 进行卸载

UnLoad(true) 会卸载包里面所有的资源 如果包里面资源被其他引用,就会导致资源找不到

文件校验

文件校验可以分为 CRC MD5 SHA1

不同\校验方式CRCMD5SHA1
算法多项式除法替代/轮转等替代/轮转等
校验长度16位或者32位16个字节20个字节
检验称呼CRC 值哈希值/散列值哈希值/散列值
安全性安全性比较弱安全性很高安全性最高
效率
用途通信数据校验安全领域安全领域

AssetBundle 常见问题

依赖包重复问题

  • 把共享的资源打包在一起

  • 分割包,按照需要进行下载

  • 把共享的部分打成一个单独的包 (推荐)

图集重复

把图集都单独打成一个单独的包,需要用到的依赖这个单独的包

Android 纹理

使用所有 Android OpenGL ES 3 设备都支持的 ETC2

iOS 文件句柄过度使用

使用 Unity 5.3.2p2 以上的版本

Asset Bundle 浏览工具

浏览工具通过 Package Manager 输入 github.com/Unity-Techn… 进行安装

Configure

image-20230510162722786

在 Configure 可以查看 AssetBundle 预估的大小和依赖还有包的内容

Build

image-20230510162815515

可以进行设置参数进行打包,可以替代我们写的打包脚本了。