Unity新版AssetBundles使用手札

使用目的

之前的项目(单机弱联网),所有资源一直放置在Resources目录,后来因为期望将配置文件放到服务器.可以实现不发版本从而更新配置文件的目的 开始接触AssetBundles

生成AssetBundles

新版的AssetBundles(Unity 5.0+,2017,2018…) 相比旧版省事很多.看了下API原有的函数

BuildPipeline.BuildAssetBundle已经被弃用了.目前AB应该只使用

BuildPipeline.BuildAssetBundles 进行全部的AB文件打包.

打包代码

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
28
29
[MenuItem("CopyEngine/AssetBundles/Build All")]
static void BuildAllAssetBundles()
{
if (Directory.Exists(FOLDER_PATH))
{
CEOneKeyReleaseUtils.DeleteFolder(FOLDER_PATH);
}

Directory.CreateDirectory(FOLDER_PATH);

DoBuildBundles("Mac", BuildTarget.StandaloneOSX);
DoBuildBundles("Android", BuildTarget.Android);
DoBuildBundles("IOS", BuildTarget.iOS);
DoBuildBundles("Win", BuildTarget.StandaloneWindows);
Caching.ClearCache();
AssetDatabase.Refresh();
Debug.Log("Done build all assets bundles");
}

private static void DoBuildBundles(string _subFolder, BuildTarget _target)
{
var assetBundleDirectory = FOLDER_PATH + "/" + _subFolder;
if (!Directory.Exists(assetBundleDirectory))
{
Directory.CreateDirectory(assetBundleDirectory);
}

BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, _target);
}

缓存机制

缓存基础知识可以参考AssetBundle usage patterns4.2.1. Shipped with project~4.2.3. Built-in caching这块.

核心部分就是使用UnityWebRequest下载并读取AB

核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//mUrl为请求地址
//mHash是用作缓存的Hash值,从AssetBundleManifest中取得
using (var uwr = UnityWebRequest.GetAssetBundle(mUrl, mHash, 0))
{
yield return uwr.SendWebRequest();

if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.LogError("[CEAB] get ab on error:\n" + uwr.error + "\nURL:" + mUrl);
}
else
{
var bundle = DownloadHandlerAssetBundle.GetContent(uwr);
//做自己的操作
}
}

AssetBundleManifest

首先要注意的是,使用BuildPipeline.BuildAssetBundles打包生成AB目录,会有两个AssetBundleManifest文件. 比如上面的打包代码

1
DoBuildBundles("Android", BuildTarget.Android);

会在指定的目录下生成一个名叫Android的目录,其内部放置的AB素材是适合安卓平台下使用的. 同时会产生一个 名字叫Android但是没有后缀,另外一个有manifest后缀的文件(Android.manifest)

Android.manifest这个文件不是真正的AssetBundleManifest,没有文件名的那个文件才是.

*.manifest在旧版的API文档(低于Unity5.0)中有说明,是用于内部构建使用,也就是可以直接忽略不管的.

真正需要加载的文件是没有文件名的那个.

将AB文件放入StreamingAssets

将AB放入或者不放入StreamingAssets要看项目而定. 主要是在包体大小和进入等待时间之间做一个平衡吧.

核心代码

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
28
29
30
31
32
33
34
35
36
37
38
public static class CEABConfig
{
public static readonly string STREAMING_PATH_URL =
#if UNITY_EDITOR
Application.dataPath + "/StreamingAssets/AssetBundles/";
#elif UNITY_ANDROID
"jar:file://" + Application.dataPath + "!/assets/AssetBundles/";
#elif UNITY_IPHONE
Application.dataPath + "/Raw/AssetBundles/";
#elif UNITY_STANDALONE
Application.dataPath + "/StreamingAssets/AssetBundles/";
#else
string.Empty;
#endif


public static readonly string PLATFORM =
#if UNITY_EDITOR_WIN
"Win";
#elif UNITY_EDITOR_OSX
"Mac";
#elif UNITY_ANDROID
"Android";
#elif UNITY_IPHONE
"IOS";
#elif UNITY_STANDALONE_WIN
"Win";
#elif UNITY_STANDALONE_OSX
"Mac";
#else
string.Empty;
#endif
}

//调用方式:

var localFilePath = CEABConfig.STREAMING_PATH_URL + CEABConfig.PLATFORM + "/" + _abName;
var bundle = AssetBundle.LoadFromFile(localFilePath);

补充说明

在安卓平台StreamingAssets中的文件是放入一个jar包之中保存的,所以正常的方法是无法查找某一个文件是否存在的.

比如在Editor或者Standalone中是可以直接用File.Exist(xxx)判断是否存在某一个AB,从而决定是从StreamingAssets中加载还是从UnityWebRequest中请求.

但是在安卓中是不可以.

目前我想到的是两种方式, 一种是在生成AB包时候同时生成相应的配置文件,通过该文件来实现File.Exist(xxx)的逻辑.

另外一种是使用try-catch然后直接生取对应的AB文件. 进行异常监听.

目前我项目中使用的是第二种方式.

我AB的使用方式

目前我项目中的AB使用流程如下

1–> 使用文章开头提供的脚本在单独的项目中打包所有AB

使用单独项目原因是,如果主项目很大,每次打包要好久. 将需要打包的资源单独放置一个新项目中会快很多

2–> 将生成好的全平台(“Mac”,”Windows”,”Android”,”IOS”)AB,Copy到主项目的StreamingAssets目录下

同时Copy一份到主目录外(用于一键打包脚本)

这样做的好处是在开发阶段测试很方便,打包每个平台的测试工程均可,且无需联网下载AB

(代码中需要设置,在测试环境中只使用StreamingAssets中的AB)

3–> 使用一键发布脚本进行安卓或者IOS平台打包:

首先删除StreamingAssets目录下的全平台AB
再根据平台不同,将主目录外的备份文件.相应的Copy进来

4–> 正式工程取得某个AB时候

首先请求AssetBundleManifest文件(从服务器请求,不缓存)

然后从中取得该AB的Hash值. 用该值和StreamingAssets中的AB进行比较. 如果相同则直接从StreamingAssets中加载.(注意,不能使用UnityWebRequest进行加载,否则会复制出一份缓存文件来).

如果不相同,则使用该Hash做key 通过UnityWebRequest请求. 这样会把相应的AB缓存下来. 下次不需要再从服务器中下载.

坚持原创技术分享,您的支持将鼓励我继续创作!