学习 Unity AssetBundle(AB包)
AssetBundle 是为了资源更新
-
AssetBundle 是一个压缩包包含
模型/贴图/预制体/声音/场景,可以在程序运行时被加载。 -
AssetBundle
自身存在想换依赖关系。 -
AssetBundle 压缩包可以采用
LZMA/LZ4压缩算法,减少压缩包大小。 -
把一些安装包的内容放在 AssetBundle 里面可以减少安装包大小。
AssetBundle 可以分为一个序列化文件和多个资源文件
-
序列化文件:模型/预制体/场景
-
资源文件:贴图/声音
AssetBundle 的使用步骤
1 指定资源 AssetBundle 属性
-
1.1 设置资源的目录
资源包的名字不区分大小写
资源包的后缀可以随意
None代表不讲当前资源打进 AssetBundleNew...代表新建一个资源包名称,如果新建的名称已经存在则会自动使用之前名称。Remove Unused Names移除新建没有用到的资源包名字比如上面设置 AssetBundle 的资源包名称叫做
wall,后缀叫做assetBundle,那么生成的资源包名字就是wall.assetBundle。通过
/可以划分目录,比如资源包名称叫做prefabs/wall则是创建一个prefabs文件夹,把wall存放在prefabs文件夹里面。 -
1.2 设置资源的名称
这个默认即可
2 构造 AssetBundle 包
构造 AssetBundle 包需要通过写编辑器插件来做到打包,对于编辑器插件目前我也不会,后续我会写学习的编辑器插件的文章。目前只能跟着教程一步步的来了。
2.1 新建一个 Editor 文件夹用来保存编辑器插件脚本
2.2 在 Editor 目录新建一个脚本用来打包 AssetBundle
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 目录看到新增加的功能了。
点击 Build AssetBundles 菜单
需要打包的资源已经被打包出来了。
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 在需要加载资源的场景新建一个空物体
空物体的名称可以随意
4.2 新建一个加载 AB 包的脚本挂在到我们新建的游戏物体上面
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 工程
此时我们存在于 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
| 不同\校验方式 | CRC | MD5 | SHA1 |
|---|---|---|---|
| 算法 | 多项式除法 | 替代/轮转等 | 替代/轮转等 |
| 校验长度 | 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
在 Configure 可以查看 AssetBundle 预估的大小和依赖还有包的内容
Build
可以进行设置参数进行打包,可以替代我们写的打包脚本了。