[Unity5.X]AssetBundle总结
AssetBundle简介当我们提到AssetBundle时,实际上有两层含义。
AssetBundle的使用流程
1.为将要被打包的资源设置AssetBundle名字我们给资源设置AssetBundle名字是为了让Unity对其分门别类的打包。 新建一个场景,创建一个Cube和一个Spere,分别命名为CubeWall和SpereWall,随便给他们拖拽一个材质球。这里我的材质球的名字为Box,它包含两种贴图和一个shader。 以上这些都是我们正常的创建对象的过程,现在把CubeWall和Sphere拖到Project窗口中做成预制体。 最下方有AssetBundle,点击None可以new一个AssetBundle的name,这里只能小写,我设定的名字是: 这样会生成一个prefab的目录,可以通过这种方式把相关的东西放在一个目录下。例如音效可以放在audio目录下之后构建。 在后边还有一个为none的属性,这个设定什么都无所谓,但是一旦设定了,那么AssetBundle的那么就是它的全名。例如设定后缀为unity3d,那么该AssetBundle的对象就是prefab/cuabwall.unity3d. 这样AssetBundle的名字就设定好了。 2.打包AssetBundle资源到本地这一步很简单,Unity提供了接口,我们只需要: public class BuildAssetBundle{
[MenuItem("AssetBundle/Build All AssetBundle")]
static void BuildAllAssetBundle() {
string path = "AssetBundles";//相对目录,和Asset同级
if (!Directory.Exists(path)) {
Directory.CreateDirectory(path);
}
BuildPipeline.BuildAssetBundles(path,BuildAssetBundleOptions.None,BuildTarget.StandaloneWindows64);
//None使用LZMA算法压缩,被压缩的包相比LZ4更多,但是解压时间更久,加载时间更久,解压是必须整体解压
//ChunkBasedCompressor LZ4压缩 可以指定加载具体的资源而无需全部解压
}
}
这样点击Build All AssetBundle后会在Asset的同级目录AssetBundle下生成如下文件: 名为AssetBundle的文件是和我们创建的目录的名字同名的,我们无法打开它,AssetBundle.manifest是我们需要关心的。 AssetBundle.manifest实际上是一个文本文件,可以用记事本打开: 可以认为是一种类似json的格式化的文本描述文件,它包含了所有的被打包的资源的名字和依赖关系,由图可以看出这个项目打包了两个资源,分别为prefab/spherewall.unity3d和prefab/cubewall 这是spherewall.unity3d对应的manifest文件,可以看出它有一个Asset属性,指明了这个资源在项目中的位置。 现在有了AssetBundle,我们就可以将其上传到服务器当中,在运行时必要的地方从服务器获取资源并实例化它。 3.上传AssetBundle资源到服务器其实这一步非常简单,只需要将AssetBundle打包生成的文件夹放到服务器的指定位置即可,这里不做演示。 4.在运行时动态加载AssetBundle打包的资源现在有了AssetBundle打包的资源,我们就可以在运行时加载AssetBundle对象了。 本地加载就是AssetBundle资源在本地,那么可以用如下代码加载: void AssetLoadFromFile() {//也有异步的方法
//相对路径
AssetBundle ab = AssetBundle.LoadFromFile(cubePath);
GameObject cubeWall = ab.LoadAsset<GameObject>("CubeWall");//获取prefab
Instantiate(cubeWall,Vector3.zero,Quaternion.identity);//实例化prefab
}
本地加载很简单,只需要LoadFromFile获取AssetBundle对象后再通过AssetBundle对象使用LoadAsset方法即可,有对应的异步的版本,这个查API即可。 远程加载在5.3之前都是用的WWW类,但是官方已经不推荐这么做了,在5.3之后包括Unity2017,都推荐使用UnityWebRequest来完成。 IEnumerator WebRequest() {
string cubeURL = @"http://localhost/AssetBundles/prefab/cubewall.unity3d";
UnityWebRequest request = UnityWebRequest.GetAssetBundle(shareURL);
yield return request.Send();//发送http请求
if(string.IsNullOrEmpty(request.error){//没有错误
AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
GameObject cubeWall = ab.LoadAsset<GameObject>("CubeWall");//获取prefab
Instantiate(cubeWall,Quaternion.identity);//实例化prefab
}
else{
yield break;
}
}
之后,在需要加载资源的地方StartCoroutine(WebRequest())即可。 还有使用的LoadFromMemory方法,该方法是直接从给定的byte[]中加载数据,如果网络传输使用的是TCP协议获得了一个二进制流文件,那么可以使用这种方式加载AssetBundle资源,详细使用可以查API。 关于AssetBundle的依赖我们已经走完了一个AssetBundle的工作流程,目前来说它运行的不错,可是有许多问题没有暴露出来。 在上述CubeWall和SphereWall这两个预制体中,我引用了同一个名为Box的材质球,这个材质球上有两张贴图和一个shader文件。 我把Box单独打包为一个名为share的AssetBundle文件,接下来我们再看看他们的体积有何变化: 可以很明显的看出,虽然多了一个share的68k,但是cubewall和spherewall的体积明显减小了,这对我们显然是非常有利的。 接下来我们再看看cubewall.manifest文件有什么变化: 这里多出一个Dependencies属性,给出了它依赖的资源的AssetBundle的存放位置。 实际上,我们总结了两个常用的经验 /* * 1.经常更新的资源单独打包,和不经常更新的资源分开 * 2.被公共引用的资源单独打包 */
到这里一切都不错,但是如果你还使用之前的加载资源的方法,你会发现,加载出来的cubewall和sphere是紫红色的,这是因为你没有加载share这个AssetBundle,那么它就找不到Box这个材质球以及对应的贴图,所以最终就呈现了这种样貌。 所以我们必须压迫加载share这个AssetBundle到内存中才可以,所以你的加载代码变成了这样: void AssetLoadFromFile() {//也有异步的方法
//相对路径
AssetBundle share = AssetBundle.LoadFromFile(sharePath);
//加载是被加载到内存当中
AssetBundle ab = AssetBundle.LoadFromFile(cubePath);
//加载assetbundle,如果没有加载它的依赖是不能正确显示cubeWall的,不存在加载的先后顺序,在LoadAsset之前即可
GameObject cubeWall = ab.LoadAsset<GameObject>("CubeWall");//获取prefab
Instantiate(cubeWall,Quaternion.identity);//实例化prefab
}
那么问题来了,你如何知道名为name的AssetBundle都依赖哪些东西呢? void LoadWithManifest() {
//主manifest文件,从这个文件中可以获取本项目所有的AssetBundle的信息,这些信息存储在manifest里边
AssetBundle manifestAssetBundle = AssetBundle.LoadFromFile("AssetBundles/AssetBundles");
AssetBundleManifest manifest = manifestAssetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//得到了本项目中所有AssetBundle的名字
//string[] assetBundleNames = manifest.GetAllAssetBundles();
//想加载某个AssetBundle,加载它所有的依赖AssetBundle,这些依赖关系存在于主manifest里边
//加载所有依赖
string[] dependencies = manifest.GetAllDependencies("prefab/cubewall.unity3d");
foreach(var name in dependencies) {
AssetBundle.LoadFromFile("AssetBundles/" + name);
}
//加载所有依赖
AssetBundle ab = AssetBundle.LoadFromFile(cubePath);
Instantiate(ab.LoadAsset<GameObject>("CubeWall"),Quaternion.identity);
}
附录 : AssetBundle BrowserAssetBundle Browser是Unity官方推出的一个非常方便查看项目的AssetBundle的依赖关系的一个插件。 在Build里可以构建AssetBundle文件,省去了我们自己写代码的麻烦(虽然并不长233)。 简易模板using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
public class LoadFromFile : MonoBehaviour {
/* * 推荐的方式 * 1.经常需要更新的资源打包 * 2.在经常需要更新的资源中,被共同依赖的单独打包到share当中 share/material share/audioclip share/lua 的目录结构 * 3.使用UnityWebRequest的方式从远端获取,使用AssetBundleManifest增加可控性 * 4.如果share的文件很多,而我只想获得x的依赖,那么使用GetAllDependence的方式先加载 */
private AssetBundleManifest MainManifest = null;
void Start () {
StartCoroutine(GetMainManifest());//获取MainManifest
StartCoroutine(LoadAsset("prefab/cubewall.unity3d","CubeWall"));
}
IEnumerator GetMainManifest() {
string url = @"http://localhost/AssetBundles/AssetBundles";
UnityWebRequest request = UnityWebRequest.GetAssetBundle(url);
yield return request.Send();
if (string.IsNullOrEmpty(request.error)) {
AssetBundle assetBundle = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
MainManifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
else {
//LogError()
yield break;
}
}
IEnumerator LoadDependencies(string assetName) {
string[] dependencies = MainManifest.GetAllDependencies(assetName);
foreach (var name in dependencies) {
string url = "http://localhost/AssetBundles/" + name;
UnityWebRequest request = UnityWebRequest.GetAssetBundle(url);
yield return request.Send();
if (string.IsNullOrEmpty(request.error)) {
AssetBundle assetBundle = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
}
else {
//LogError()
yield break;
}
}
}
IEnumerator LoadAsset(string assetName,string objName) {
while (MainManifest == null)
yield return null;
StartCoroutine(LoadDependencies(assetName));
string url = "http://localhost/AssetBundles/" + assetName;
UnityWebRequest request = UnityWebRequest.GetAssetBundle(url);
yield return request.Send();//发送http请求
if (string.IsNullOrEmpty(request.error)) {
AssetBundle assetBundle = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
GameObject go = ab.LoadAsset<GameObject>(objName);
Instantiate(go,Quaternion.identity);//实例化prefab
}
else {
//LogError()
yield break;
}
}
/* //Unity5.3及以上推荐的方式 IEnumerator WebRequest() { string shareURL = @"http://localhost/AssetBundles/share.unity3d"; string cubeURL = @"http://localhost/AssetBundles/prefab/cubewall.unity3d"; UnityWebRequest request = UnityWebRequest.GetAssetBundle(shareURL); yield return request.Send();//发送http请求 AssetBundle share = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; request = UnityWebRequest.GetAssetBundle(cubeURL); yield return request.Send();//发送http请求 AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; GameObject cubeWall = ab.LoadAsset<GameObject>("CubeWall");//获取prefab Instantiate(cubeWall,Quaternion.identity);//实例化prefab } void LoadWithManifest() { //主manifest文件,这些信息存储在manifest里边 AssetBundle manifestAssetBundle = AssetBundle.LoadFromFile("AssetBundles/AssetBundles"); AssetBundleManifest manifest = manifestAssetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); //得到了本项目中所有AssetBundle的名字 //string[] assetBundleNames = manifest.GetAllAssetBundles(); //想加载某个AssetBundle,这些依赖关系存在于主manifest里边 //加载所有依赖 string[] dependencies = manifest.GetAllDependencies("prefab/cubewall.unity3d"); foreach(var name in dependencies) { AssetBundle.LoadFromFile("AssetBundles/" + name); } //加载所有依赖 AssetBundle ab = AssetBundle.LoadFromFile(cubePath); Instantiate(ab.LoadAsset<GameObject>("CubeWall"),Quaternion.identity); } */
} (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |