一:AssetBundle介绍
AssetBundle是将资源使用unity提供的一种用于存储的压缩格式打包后的集合,它可以存储任何一种untiy可以识别的资源,
一般情况下AssetBundle的具体开发流程如下:
- 创建AssetBundle,开发者在unity编辑器中通过脚本所需要的资源打包成AssetBundle文件
- 上传服务器。开发者将打包好的AssetBundle文件上传至服务器中。使得游戏客户端能够获取当前的资源,进行游戏的更新
- 下载AssetBundle,首先将其下载到本地设备中,然后通过AssetBundle的加载模块将资源加到游戏中
- 加载,通过untiy提供的API可以加载资源里面包含的资源来更新客户端
- 卸载AssetBundle,卸载之后可以节省内存资源,并且要保证资源的正常更新
二:AssetBundle多平台打包
2.1创建AssetBundle
(1)只有在Asset窗口中的资源才可以打包,我们单击GameObject->Cube,然后在Asset窗口创建一个预设体,命名为cubeasset,讲Cube拖到该预设体上。
(2)单击刚创建的预制件cubeasset,在编辑器界面右下角的属性窗口底部有一个名为”AssetBundle”的创建工具。接下来创建即可,空的可以通过单击菜单选项”New…”来创建,将其命名为”cubebundle”,名称固定为小写,如果使用了大写字母之后,系统会自动转换为小写格式。
2.2打包AssetBundle
AssetBundle创建之后需要导出,这一个过程就需要编写相应的代码实现,从unity5.x之后,提供了一套全新简单的API来实现打包功能。大大简化了开发者手抖遍历资源自行打包的过程,更加方便快捷。需要在Asset目录下创建Editor目录,表示该脚本是对于编辑器的一个扩展。脚本写完之后,也不需要进行挂载,会自动在unity的菜单栏中生成。点击子菜单,既可以进行打包AssetBundle。具体代码如下
using UnityEditor;
public class ExportAssets : MonoBehaviour {
[@MenuItem("Test/Build Asset Bundles")]
static void BuildAssetBundles(){
BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath,
BuildAssetBundleOptions.UncompressedAssetBundle,
BuildTarget.StandaloneOSXUniversal);
}
}
所有的AssetBundle已经被导出,此时每一个AssetBundle资源会有一个和文件相关的Mainfest的文本类型的文件,该文件提供了所打包的资源的CRC和资源依赖信息
除此之外,还有一个AssetBundle文件会在生成的时候被创建,记录整个资源列表以及列表之间的关系
2.3AssetBundle的压缩类型
untiy引擎为我们提供了三种压缩策略来处理AssetBundle的压缩
- LZMA
- LZ4
- 不压缩
**LZMA格式:**默认情况下,打包生成的AssetBundle都会被压缩。在u3d中,AssetBundle的标准压缩格式便是LZMA(一种序列化流文件)。因此默认情况下,打出的AssetBundle包处于LZMA格式的压缩状态,在使用AssetBundle前需要解压缩。
使用LZMA格式压缩的AssetBundle的包体积最小,但是相应的会增加压缩时的时间
**LZ4:**unity5.3之后的版本增加了LZ4格式压缩, 由于LZ4压缩比一般,因此经过压缩后的AssetBundle包体的体积比较大,但是LZ4格式的好处在于解压时间相对短
若要使用LZ4格式压缩,只需要在打包的时候开启
BuildAssetBundleOptions.ChunkBasedCompression即可。
BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath,
BuildAssetBundleOptions.ChunkBasedCompression);
**不压缩:**当然,我们也可以不对AssetBundle进行压缩,没有经过压缩的包体积最大,但是访问速度最快。若要使用不压缩策略,只要在打包的时候开启
BuildAssetBundleOptions.UncompressedAssetBundle即可。
BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath,
BuildAssetBundleOptions.UncompressedAssetBundle);
static void BuildAssetBundlesAndroid(){
Object obj = AssetDatabase.LoadMainAssetAtPath("Assets/Test.png");
BuildPipeline.BuildAssetBundle(obj, null,
Application.streamingAssetsPath + "/Test.assetbundle",
BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
| BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.StandaloneWindows);
}
三:AssetBundle资源加载和卸载
3.1AssetBundle加载
在5.3版本中的新AssetBundle系统中,旧有的一些动态加载API已经被新的API所取代,具体内容如下
4.x-5.2版本中的AssetBundle.CreateFromFile方法,变成了AssetBundle.LoadFromFile
4.x-5.2版本中的AssetBundle.CreateFromMemory,变成AssetBundle.LoadFromMemoryAsync方法
4.x-5.2版本中AssetBundle.CreateFromMemoryImmediate,变成AssetBundle.LoadFromMemory
因此我们学习的内容都是以新的为准
使用AssetBundle动态加载资源首先要获取AssetBundle对象,第二步才是从AssetBundle中加载目标资源。
因此我们要关注如何在运行的时候获取AssetBundle的对象
要在运行时加载AssetBundle对象主要可以分为俩个途径
- 先获取www对象,再通过www.assetBundle获取AssetBundle对象
- 直接获取AssetBundle
到此没找特别好的材料 等后续找到好的资料,再补充这块
3.2AssetBundle内部资源加载
新版的AssetBundle中,加载资源的API已经变成了一下的几个:
- LoadAsset:从资源包中加载指定的资源
- LoadAllAsset:加载当前资源包中所有的资源
- LoadAssetAsync:从资源包中异步加载资源
Texture mat = (Texture)data.assetBundle.LoadAsset ("1");
GetComponent<MeshRenderer> ().material.mainTexture = mat;
data.assetBundle.Unload (false);
//2同步加载所有的GameObject类型
GameObject[] obj = data.assetBundle.LoadAllAssets<GameObject> ();
//3Async加载,根据名称进行加载
AssetBundleRequest req= data.assetBundle.LoadAssetAsync("1");
yield return req;
if (req.isDone) {
Texture tex = (Texture)req.asset;
}
//4 LoadAllAssets的yibu版本
AssetBundleRequest req1=data.assetBundle.LoadAllAssetsAsync
<GameObject>();
从AssetBundle加载资源的常用API
- public Object LoadAsset(string name,Type type);
通过给定的名字和资源类型,加载资源。加载是会自动加载其依赖的资源,即Load一个Prefab时,会自动Load其引用的Texture资源 - public Object[] LoadAllAsset(Type type);
一次性加载Bundle中给定资源的所有资源 - public AssetBundleRequest LoadAssetAsync(string name,Type type);
该接口时Load的异步版本
资源卸载
资源卸载部分的变化不大,使用的仍然是Unload方法。
该方法会卸载运行时内存中包含的bundle中的所有资源
当传入参数为true,则不仅仅内存中的AssetBundle对象包含的资源会被销毁,根据这些资源实例化而来的游戏内对象也会销毁
当传入false,则仅仅销毁内存中的AssetBundle对象包含的资源
四:AssetBundle服务器下载
我们采用下面的案例,场景进入的时候,依次从服务器给三个已经创建的对象加载纹理,材质,以及根据预设创建一个新的对象。
服务器我们简单的使用python进行搭建,进入到存放asset资源目录下,输入以下命令就可以搭建一个简单的文件服务器:
python -m SimpleHTTPServer 8080
浏览器输入 http://127.0.0.1:8080/ 可以查看搭建的情况。
具体过程如下:
(1)新建一个场景,创建立方体,球体和一个空对象,分别用来测试下载纹理,材质和预设体。
(2)分别将材质和纹理图片,预设体创建成为AssetBundle资源,具体的命名自己把握,后来需要根据名称进行获取。
(3)打包成功之后,将内容添加到对应的服务器之中,下面开始脚本的编写。
(4)新建脚本如下,分别测试三个内容的下载。
public class DownloadAsset : MonoBehaviour {
public GameObject goCube; //演示修改纹理
public GameObject goSphere;//演示修改材质
public Transform newPosition;//演示根据预设体创建对象
//url=IP+文件名
private string url1=“http://127.0.0.1:8080/image”;
private string url2=“http://127.0.0.1:8080/mater”;
private string url3=“http://127.0.0.1:8080/cubebundle”;
// Use this for initialization
void Start () {
//downTexture ();
downMat ();
//downPrefab ();
}
void downTexture()
{
StartCoroutine (download_Texture(url1));
}
IEnumerator download_Texture(string url){
WWW downAsset = new WWW (url);
yield return downAsset;
goCube.GetComponent ().material.mainTexture
= (Texture)downAsset.assetBundle.LoadAsset (“01”);
downAsset.assetBundle.Unload (false);
}
void downMat(){
StartCoroutine (download_Mat(url2));
}
IEnumerator download_Mat(string url){
WWW downAsset = new WWW (url);
yield return downAsset;
goCube.GetComponent<Renderer> ().material
= (Material)downAsset.assetBundle.LoadAsset
("Red");
downAsset.assetBundle.Unload (false);
}
void downPrefab(){
StartCoroutine (download_Prefab(url3));
}
IEnumerator download_Prefab(string path){
WWW downloadAsset = new WWW (path);
yield return downloadAsset;
GameObject gpPrefabs = (GameObject)Instantiate
(downloadAsset.assetBundle.LoadAsset("Cube"));
gpPrefabs.GetComponent<Renderer> ().material.color =
Color.yellow;
gpPrefabs.transform.position =
newPosition.transform.position;
downloadAsset.assetBundle.Unload (false);
downloadAsset.Dispose ();
当然,我们这里也可以使用异步加载的方式加载AssetBundle的资源,如下所示:
AssetBundle bundle = downloadAsset.assetBundle;
AssetBundleRequest request = bundle.LoadAssetAsync
("Cube",typeof(GameObject));
yield return request;
GameObject gpPrefabs = (GameObject)Instantiate
(request.asset);
gpPrefabs.GetComponent<Renderer> ().material.color = Color.yellow;
gpPrefabs.transform.position =newPosition.transform.position;
downloadAsset.assetBundle.Unload (false);
downloadAsset.Dispose ();
五:AssetBundle原理分析
在AssetBundle的下载和加载过程中,以及Assets加载和实例化过程中,AssetBundle以及加载的Assets都会占用内存。
(1)AssetBundle的卸载采用Assetbundle.Unload(bool)接口。
(2)Assets的卸载有两种方式:
①.Assetbundle.Unload(true)。这会强制卸载掉所有从AssetBundle加载的Assets。
②.Resource.UnloadUnusedAssets()和 Resources.UnloadAsset。这会卸载掉所有没有用到的Assets。需要注意的是,该接口作用于整个系统,而不仅仅是当前的AssetBundle,而且不会卸载从当前AssetBundle文件中加载并仍在使用的Assets。
(3)对于实例化出来的对象,可以使用GameObject.Destroy或GameObject.DestroyImmediate。注意的是:官方说法是这样的,如果使用GameObject.Destroy接口,unity会将真正的删除操作延后到一个合适的时机统一进行处理,但会在渲染之前。
WW对象和WWW.asssetbundle加载的AssetBundle对象都会对Web Stream数据持有引用。AssetBundle对象同时也会引用到从它加载的所有Assets。按照官方说法,真正的数据都是存放在Web Stream数据中(如纹理、模型),而WWW和AssetBundle对象只是一个结构指向了Web Stream数据。
对于WWW对象,可以使用www=null或www.dispose来卸载。这两者是有区别的,www=null不会立即释放内存,而是系统的自动回收机制启动时回收。www.dispose则会立即调用系统的回收机制来释放内存。当WWW对象被释放后,其对于Web Stream数据的引用计数也会相应减1。
对于Web Stream数据,它所占用的内存会在其引用计数为0时,被系统自动回收。例如:当上图中的AssetBundle对象和WWW对象被释放后,Web Stream数据所占内存也会被系统自动回收。
六:AssetBundle依赖加载
如果一个或者多个 UnityEngine.Objects 引用了其他 AssetBundle 中的 UnityEngine.Object,那么 AssetBundle 之间就产生的依赖关系。相反,如果 UnityEngine.ObjectA 所引用的UnityEngine.ObjectB 不是其他 AssetBundle 中的,那么依赖就不会产生。
假若这样(指的是前面两个例子的后者,既不产生依赖的情况),被依赖对象(UnityEngine.ObjectB)将被拷贝进你创建的 AssetBundle(指包含 UnityEngine.ObjectA 的 AssetBundle)。
更进一步,如果有多个对象(UnityEngine.ObjectA1、UnityEngine.ObjectA2、UnityEngine.ObjectA3…)引用了同一个被依赖对象(UnityEngine.ObjectB),那么被依赖对象将被拷贝多份,打包进各个对象各自的 AssetBundle。
如果一个 AssetBundle 存在依赖性,那么要注意的是,那些包含了被依赖对象的 AssetBundles,需要在你想要实例化的对象的加载之前加载。Unity 不会自动帮你加载这些依赖。
想想看下面的例子, Bundle1 中的一个材质(Material)引用了 Bundle2 中的一个纹理(Texture):
在这个例子中,在从 Bundle1 中加载材质前,你需要先将 Bundle2 加载到内存中。你按照什么顺序加载 Bundle1 和 Bundle2 并不重要,重要的是,想从 Bundle1 中加载材质前,你需要先加载 Bundle2。
(1)AssetBundle.LoadFromMemoryAsync
这个方法的参数是一个包含了 AssetBundle 数据的字节数组。如果需要的话,你还可以传入一个 CRC(循环冗余校验码) 参数。如果 AssetBundle 使用 LZMA 算法压缩,那么 AssetBundle 在加载的时候会被解压。如果 AssetBundle 使用 LZ4 算法压缩,它将直接以压缩形式被加载。
IEnumerator LoadFromMemoryAsync(string path)
{
AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return createRequest;
AssetBundle bundle = createRequest.assetBundle;
var prefab = bundle.LoadAsset.(“MyObject”);
Instantiate(prefab);
}
AssetBundle.LoadFromFile
这个 API 在加载本地存储的未压缩 AssetBundle 时具有很高效率。如果 AssetBundle 是未压缩,或者是数据块形式(LZ4 算法压缩)的,LoadFromFile 将从磁盘中直接加载它。如果 AssetBundle 是高度压缩(LZMA 算法压缩)的,在将它加载进入内存前,会首先将它解压。
下面是一个如何使用这个方法的例子:
public class LoadFromFileExample extends MonoBehaviour
{
function Start() {
var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, “myassetBundle”));
if (myLoadedAssetBundle == null) {
Debug.Log(“Failed to load AssetBundle!”);
return;
}
var prefab = myLoadedAssetBundle.LoadAsset.(“MyObject”);
Instantiate(prefab);
}
}
注意:在安卓设备上,如果 Unity 是5.3或者更老的版本,这个方法在读取资源流路径(Streaming Assets path)的时候会失败。这是因为那个路径是在一个 .jar 文件的内部。Unity5.4 以及更高的版本没有这个问题,可以正常的读取资源流。
WWW.LoadFromCacheOrDownload
这个 API 已经被废弃(建议使用 UnityWebRequest)(三思:这句话不是我加的,官方文档中就是有这句话)
这个 API 对于从远程服务器加载 AssetBundles,或者加载本地 AssetBundles 都很有用。这个 API 是 UnityWebRequest 不尽如人意的老版本。
从远程服务器加载的 AssetBundle 将会被自动缓存。如果 AssetBundle 是压缩形式的,一个工作线程将加速解压这个 AssetBundle 并写入缓存。一旦一个 AssetBundle 已经被解压且被缓存,它将完全像使用 AssetBundle.LoadFromFile 方法一样被加载。
下面是一个如何使用这个方法的例子:
using UnityEngine;
using System.Collections;
public class LoadFromCacheOrDownloadExample : MonoBehaviour
{
IEnumerator Start ()
{
while (!Caching.ready)
yield return null;
var www = WWW.LoadFromCacheOrDownload("http://myserver.com/myassetBundle", 5);
yield return www;
if(!string.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
yield return;
}
var myLoadedAssetBundle = www.assetBundle;
var asset = myLoadedAssetBundle.mainAsset;
}
}
由于缓存 AssetBundle 字节数据的开销较大,建议所有开发者在使用 WWW.LoadFromCacheOrDownload 方法时,确保 AssetBundles 都比较小——最多几兆字节。同样建议所有开发者在内存比较有限的平台(比如移动设备)上使用这个方法时,确保同时只下载一个 AssetBundle,防止内存泄漏。
如果缓存文件夹没有足够的空间来缓存额外的文件,LoadFromCacheOrDownload 将会从缓存中迭代删除最近最少使用的 AssetBundles,直到有足够的空间来存储新的 AssetBundle。如果空间还是不够(比如硬盘满了,或者所有缓存的文件都正在被使用),LoadFromCacheOrDownload() 将绕开缓存,直接将文件以流的形式存进内存。
如果想要使用 LoadFromCacheOrDownload 的版本变量,方法参数(第二个参数)需要改变。如果参数与当前缓存的 AssetBundle 的版本变量一致,那么就可以从缓存中加载这个 AssetBundle。
UnityWebRequest
UnityWebRequest 有个专门的 API 来处理 AssetBundles。首先,你需要使用 UnityWebRequest.GetAssetBundle 方法来创建你的 web 请求。在请求返回后,将请求放入 DownloadHandlerAssetBundle.GetContent(UnityWebRequest) 作为参数。GetContent 方法将返回你的 AssetBundle 对象。
在下载完 AssetBundle 后,你同样可以使用 DownloadHandlerAssetBundle 类的 assetBundle 属性来加载 AssetBundle,这就和使用 AssetBundle.LoadFromFile 方法一样高效。
下面有个例子展示:如何加载一个包含两个 GameObjects 的 AssetBundle,并实例化它们。想要运行这段程序,我们只需要调用 StartCoroutine(InstantiateObject()) 方法:
IEnumerator InstantiateObject()
{
string uri = “file:///” + Application.dataPath + “/AssetBundles/” + assetBundleName;
UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
GameObject cube = bundle.LoadAsset(“Cube”);
GameObject sprite = bundle.LoadAsset(“Sprite”);
Instantiate(cube);
Instantiate(sprite);
}
使用 UnityWebRequest 的优点是,它允许开发者用更灵活的方式来处理下载的数据,并且潜在地排除了不必要的内存占用。和 UnityEngine.WWW 类相比,这是更现代,也更推荐的 API。
从 AssetBundles 中加载资源
现在,你已经成功下载了你的 AssetBundle,是时候从中加载一些资源。
通常的代码片段:
T objectFromBundle = bundleObject.LoadAsset(assetName);
T 是你想加载的资源类型。
当你决定如何加载资源的时候,有一对方法供使用。我们可以使用 LoadAsset、LoadAllAssets 方法,以及与它们对应的异步方法: LoadAssetAsync、LoadAllAssetsAsync。
下面是一个从一个 AssetBundle 中同步加载资源的例子:
加载一个 GameObject:
GameObject gameObject = loadedAssetBundle.LoadAsset(assetName);
加载所有资源:
Unity.Object[] objectArray = loadedAssetBundle.LoadAllAssets();
现在,和上面展示的方法(要么返回你正在加载的对象,要么返回一组对象)不同的是,异步方法返回的是一个 AssetBundleRequest。
在可以使用资源前,你需要等待处理完成。如下:
AssetBundleRequest request = loadedAssetBundleObject.LoadAssetAsync(assetName);
yield return request;
var loadedAsset = request.asset;
以及:
AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync();
yield return request;
var loadedAssets = request.allAssets;
一旦你已经加载好你的资源,是时候行动了!你可以像使用 Unity 中的其他对象一样使用加载的对象。
加载 AssetBundle Manifests(资源清单)
加载 AssetBundle manifests 非常的有用。尤其是当处理 AssetBundle 依赖关系的时候。为了获取可以使用的 AssetBundleManifest,你需要加载一个额外的 AssetBundle(即那个和文件夹名称相同的文件),并且从中加载出一个 AssetBundleManifest 类型的对象。从 AssetBundle 中加载 manifest 完全和从中加载其他资源一样,如下:
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset(“AssetBundleManifest”);
现在,你可以通过上面例子获取到的 manifest 对象来使用 AssetBundleManifest 类的 API。从现在开始,你可以使用这个 manifest 来获取关于 AssetBundle 的信息,包括:依赖数据、hash 数据,以及版本变量数据。
还记得前面章节我们讨论过的,如果一个 bundleA 对 bundleB 有依赖,那么在从 bundleA 中加载任何资源之前,我们需要先加载 bundleB 吗?Manifest 对象就使得动态查找正在加载的依赖关系成为可能。比如我们想要加载一个名叫“assetBundle”的 AssetBundle 的所有依赖:
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset(“AssetBundleManifest”);
string[] dependencies = manifest.GetAllDependencies(“assetBundle");
foreach(string dependency in dependencies)
{
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency));
}
现在,你已经加载了 AssetBundle、AssetBundle 依赖,以及其他资源,是时候讨论如何管理这些加载好的 AssetBundles 了。
在 Objects 被从场景中移除的时候,Unity 不会自动将它们卸载。资源的清理是在某个特定时机被触发,当然也可以手动触发。
知道什么时候加载和卸载一个 AssetBundle 很重要。不合时宜的卸载 AssetBundle 可能导致重复对象(duplicating objects)错误,或者其他未预料到的情况,比如纹理丢失
理解如何管理 AssetBundle 最重要的事是什么时候调用 AssetBundle.Unload(bool) 方法,以及该方法的参数应该传入 true 还是 false。该方法卸载 AssetBundle 的头信息;方法参数决定了是否同时卸载从 AssetBundle 中加载并实例化的所有 Objects。
如果你传入 true 参数,那么你从 AssetBundle 中加载的所有对象将被卸载,即便这些对象正在被使用。这就是我们前面提到的,导致纹理丢失的原因。
假设 Material M 是从 AssetBundle AB 中加载的,如下:
如果 AB.Unload(true) 被调用,那么任何使用 Material M 的实例都将被卸载并消除,即便它们正在场景中被使用。
如果 AB.Unload(false) 被调用,那么将切断所有使用 Material M 的实例与 AssetBundle AB 的联系。
如果 AssetBundle AB 在被卸载后不久再次被加载,Unity 并不会将已经存在的使用 Material M 的实例与 AssetBundle AB 重新联系。因此将存在两份被加载的 Material M。
通常情况下,使用 AssetBundle.Unload(false) 不会获得理想情况。大多数项目应该使用 AssetBundle.Unload(true) 方法,以避免内存中出现重复对象(duplicating objects)。
大多数项目应该使用 AssetBundle.Unload(true) 方法,并且要采取措施确保没有重复对象。两种通常采取的措施如下:
在应用的生命周期中找到合适的时机来卸载 AssetBundle,比如关卡之间,或者加载场景的时候。
为每个对象采取引用计数管理方法,只有当 AssetBundle 的所有对象都没有被使用的时候,再卸载 AssetBundle。这样就可以避免应用出现重复对象的问题。
如果应用必须使用 AssetBundle.Unload(false) 方法,对象将只能在以下两种情况下被卸载:
消除对象的所有引用,包括场景中的和代码中的。之后,调用
Resources.UnloadUnusedAssets。
没有额外附加特性地加载一个场景。这将消除当前场景的所有对象,并自动调用 Resources.UnloadUnusedAssets。
如果你不想自己管理加载的 AssetBundle、依赖关系,以及资源,你可能需要使用 AssetBundle 管理器。(AssetBundle Manager,下一章节将介绍。)
public class LoadAssetBundle : MonoBehaviour {
public string manifestFilePath;
// Use this for initialization
void Start () {
manifestFilePath = Application.streamingAssetsPath + “/StreamingAssets”;
}
void GetData(){
AssetBundle assetBundle = AssetBundle.CreateFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
string[] dependencies = manifest.GetAllDependencies(“mater.assetbundle");
foreach(string dependency in dependencies)
{
AssetBundle.CreateFromFile(Application.streamingAssetsPath+"/"+dependency);
}
AssetBundle bundle= AssetBundle.CreateFromFile
(Application.streamingAssetsPath+"/mater.assetbundle");
Material mat = (Material)bundle.LoadAsset ("Red");
GetComponent<MeshRenderer> ().material = mat;
}