写在前面:
很久很久很久。。没有更新了,当然写这个需要坚持,最近因为工作调整转了移动开发,之后的博客更新会以移动开发为主了,当然写博客纯粹是为了记录自己的学习过程,毕竟好记性不如硬键盘嘛,有什么错误的地方还望大家指正。
Unity自5.0开始提供了新的AssetBundle打包Api,以下是新旧Api对比:
打包方式:
public class ExportAssetBundles {
[MenuItem("Assets/Build AssetBundle From Selection - Track dependencies")]
static void ExportResource () {
// Bring up save panel
string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
if (path.Length != 0) {
// Build the resource file from the active selection.
Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, 0);
Selection.objects = selection;
}
}
[MenuItem("Assets/Build AssetBundle From Selection - No dependency tracking")]
static void ExportResourceNoTrack () {
// Bring up save panel
string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
if (path.Length != 0) {
// Build the resource file from the active selection.
BuildPipeline.BuildAssetBundle(Selection.activeObject, Selection.objects, path);
}
}
}
而在目前最新的Unity2017版本中,旧版本的打包方式已经被完全废弃了。
public class PackageAssetBundle : MonoBehaviour {
[MenuItem("Build/Build AssetBundles")]
static void buildAssetBundles()
{
string buildPath = Application.dataPath + "/Assets/Abs";//打包路径
if (!Directory.Exists(buildPath))
Directory.CreateDirectory(buildPath);
BuildPipeline.BuildAssetBundles(buildPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
}
}
可以看到打包的代码非常简单。
有几种方式:
第一种是直接在Inspecot面板进行指定:
图3
打包时Api将选择所有你指定了AssetBundle VarName的资源,然后统一进行打包,如图3中该资源被打包后最终会生成cube.ab和cube.ab.manifest两个文件,manifest文件中记录的是cube.ab的信息,如依赖资源等等。
第二种呢是在代码中进行指定:
public class BuildAssetBundlesBuildMapExample : MonoBehaviour
{
[MenuItem("Example/Build Asset Bundles Using BuildMap")]
static void BuildMapABs()
{
// Create the array of bundle build details.
AssetBundleBuild[] buildMap = new AssetBundleBuild[2];
buildMap[0].assetBundleName = "enemybundle";
string[] enemyAssets = new string[2];
enemyAssets[0] = "Assets/Textures/char_enemy_alienShip.jpg";
enemyAssets[1] = "Assets/Textures/char_enemy_alienShip-damaged.jpg";
buildMap[0].assetNames = enemyAssets;
buildMap[1].assetBundleName = "herobundle";
string[] heroAssets = new string[1];
heroAssets[0] = "char_hero_beanMan";
buildMap[1].assetNames = heroAssets;
BuildPipeline.BuildAssetBundles("Assets/ABs", buildMap, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
}
}
当然指定方式灵活度还是不错的,你可以直接Selection,然后再对Selection的资源构造成buildMap,再进行处理即可。
打包说完了,接着就是加载咯。
加载可以分为三个步骤:
1.首先读取Manifest文件,读取需要加载ab(AssetBundle)的依赖资源
2.加载依赖ab
3.加载ab
打包资源如下:
依赖关系:
Prefabs | depends | Materials |
---|---|---|
cube.ab | depend on | texure.ab |
capsule.ab | depend on | emission.ab |
我们只需要把材质挂在prefabs上即可,Unity新的打包方式会自动帮我们处理依赖关系的,运行打包代码后得到如下目录:
图5
show in explorer来看看:
图6
如图6所示,打包完成后,在打包路径下生成了一个和当前路径文件夹同名的AssetBundle文件和一个manifest文件,这两个文件是有用的,别随便删除了哈。。。
但是改个名字什么的还是可以的哈。
接着来看一下文件大小,毕竟我们使用依赖打包的目的是为了减小包的大小不是嘛:
ab | size |
---|---|
cube.ab | 1.72KB |
capsule.ab | 1.78KB |
texure.ab | 147KB |
emission.ab | 510KB |
哎,没错的吧,cube和capsule中确实剔除了材质,大小只有一点多KB,来看一下依赖信息,就是它:
图7
我们可以发现里面清晰的记录每个ab的依赖资源信息。
AssetBundle的加载方式有好几种,这里介绍两种常用的:
1.LoadFromFile:
public GameObject loadAssetBundleFiles(string filePath, string manifestBundleName, string abFileName, string prefabFileName)
{
/**
* 1.首先从打包路径获取依赖
* 这里加载的就是生成的同名文件ab,这里应该是filePath/Abs,当然会这个名称也可以修改
*/
AssetBundle manifestBundle = AssetBundle.LoadFromFile(getManifestFilePath(filePath, manifestBundleName));
/**
* 2.获取依赖资源列表
*/
if (manifestBundle != null)
{
try
{
AssetBundleManifest manifest = manifestBundle.LoadAsset("AssetBundleManifest");//固定加载方式,通过 assetbundle Abs加载Abs.manifest
manifestBundle.Unload(false);
//获取加载ab的依赖信息,参数为ab名称,如cube.ab
string[] dependsFile = manifest.GetAllDependencies(abFileName);
if (dependsFile.Length > 0)
{
//根据获取到的依赖信息加载所有依赖资源ab
AssetBundle[] dependsBundle = new AssetBundle[dependsFile.Length];
for (int i = 0; i < dependsFile.Length; i++)
{
String fp = generateAbsoluteFile(filePath, dependsFile[i]);
Debug.Log(String.Format("depends:{0}:{1}", i, dependsFile[i]));
dependsBundle[i] = AssetBundle.LoadFromFile(fp);
}
}
}
catch (InvalidCastException e)
{
Debug.LogException(e);
}
/**
* 3.最后加载ab
* 注意这里的LoadAsset的参数是Prefab的名称,无后缀,如cube而非cube.ab或cube.prefab
*/
AssetBundle ab = AssetBundle.LoadFromFile(generateAbsoluteFile(filePath, abFileName));
GameObject go = ab.LoadAsset(prefabFileName) as GameObject;
ab.Unload(false);
return go;
}
return null;
}
2.WWW加载:
IEnumerator wwwGetAssetBundles(string filePath, string manifestName, string abFileName, string prefabFileName)
{
//WWW www = WWW.LoadFromCacheOrDownload(getManifestFilePath(filePath), 0);
WWW www = new WWW(manifestName);
yield return www;
if (!string.IsNullOrEmpty(www.error))
{
Debug.LogError(www.error);
}
else
{
/**
* 1.首先从打包路径获取依赖
*/
AssetBundle manifestBundle = www.assetBundle;
/**
* 2.获取依赖资源列表
*/
if (manifestBundle != null)
{
AssetBundleManifest manifest = manifestBundle.LoadAsset("AssetBundleManifest");
manifestBundle.Unload(false);
www.Dispose();
string[] dependsFile = manifest.GetAllDependencies(abFileName);
if (dependsFile.Length > 0)
{
AssetBundle[] dependsBundle = new AssetBundle[dependsFile.Length];
for (int i = 0; i < dependsFile.Length; i++)
{
String fp = generateAbsoluteFile(filePath, dependsFile[i]);
Debug.Log(String.Format("depends:{0}:{1}", i, dependsFile[i]));
www = new WWW(fp);
yield return www;
if(www.error == null)
{
dependsBundle[i] = www.assetBundle;
www.Dispose();
}
}
}
/**
* 3.最后加载ab
*/
AssetBundle ab = AssetBundle.LoadFromFile(generateAbsoluteFile(filePath, abFileName));
GameObject go = ab.LoadAsset(prefabFileName) as GameObject;
if (go != null)
{
GameObject.Instantiate(go);
}
ab.Unload(false);
}
}
}
代码和第一种加载方式大致相同。
最后说一点使用时需要注意的地方,举个例子,比如说我们有一公用的资源,A、B、C三个ab都需要依赖D,那么在程序中我们需要加载A时会先加载D,然后加载A,这个时候B也需要被加载了,因为程序不知道D已经被加载了,所以再次去读取依赖信息,尝试加载D,这样就会消耗一些cpu的时间了,因此在我们实际开发中应该更加合理的去管理公用的ab资源,对于不常用的公用ab使用过后及时卸载,下次使用再进行加载,而对用经常使用需要常驻内存的ab资源,最好在程序中设置一个标识,标记其是否已经被加载,存在于内存中,然后在加载依赖该公用ab的ab资源时判断该公用ab是否已被加载,当然,也可能会存在一个ab依赖多个公用资源,这种情况就需要开发者更加谨慎的去处理依赖关系了,最好建立统一的管理,做到逻辑清晰。
Over:最后贴出
官网链接:BuildAssetBundles
Demo下载链接(Unity5.x):Demo地址
密码:m4q2