AssetBundle 是一种压缩文件,压缩时用到的压缩格式可以在打bundle的时候指定,BuildPipeline.BuildAssetBundles方法第二个参数可以指定压缩格式。
使用AssetBundle流程分为两部
1.把资源打成AssetBundle
先把资源(纹理,材质,模型)指定一个AssetBundle名字和后缀
然后调用BuildPipeline.BuildAssetBundles方法把资源打成AssetBundle
2.从AssetBundle中加载资源
常用的方式为用WWW先把AssetBundle下载下来(通过网络或者是从本地加载),然后从WWW中加载出来AssetBundle,然后从AssetBundle中加载出来资源,然后再把资源实例化(如果是纹理图片之类的可以直接使用)
3.关于内存
先来看看一个AssetBundle资源占的内存情况
用WWW下载完后,WWW会保留一份缓存,从WWW中加载出来AssetBundle后会有一份AssetBundle的缓存,从AssetBundle中加载出来资源后有一份资源的缓存,把资源实例化后会有一份实例化的缓存。所以我们要管理好这些缓存,及时把没用的清理掉。
我们先来写一个自动打AssetBundle的工具
首先创建一个文件夹 Art,我们把项目中需要的资源按类型放到这个文件夹
我们会把这里面的资源的AssetBundle的名字标记为它所在的文件夹的名字
自动标记名字和打Assetbundle的代码如下:
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
public class AssetBundleEditor {
static string outPath = null;
static string assetFullPath = Application.dataPath + "/Art/";
static string assetPath = @"Assets/Art/";
[MenuItem("Itools/BuildeAssetBundle")]
public static void BuildeAssetBundle() {
outPath = IPathTools.GetEditeABOutPath();
if (Directory.Exists(outPath)) {
DirectoryInfo di = new DirectoryInfo(outPath);
di.Delete(true);
}
Debug.Log("outPath "+ outPath);
Directory.CreateDirectory(outPath);
BuildPipeline.BuildAssetBundles(outPath, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget);
AssetDatabase.Refresh();
}
[MenuItem("Itools/MarkAssetBundle")]
public static void MarkAssetBundle() {
AssetDatabase.RemoveUnusedAssetBundleNames();
assetFullPath = FixedWindowsPath(assetFullPath);
Dictionary<string, string> bundlePathDic = new Dictionary<string, string>();
DirectoryInfo dir = new DirectoryInfo(assetFullPath);
FileSystemInfo[] fileSystemInfo = dir.GetFileSystemInfos();
for (int i=0;i< fileSystemInfo.Length;i++) {
FileSystemInfo tmpFile = fileSystemInfo[i];
if (tmpFile is DirectoryInfo) {
string tmpPath = Path.Combine(assetFullPath, tmpFile.Name);
//SceneOverView(tmpPath);
ErgodicDirectory(tmpPath, bundlePathDic);
}
}
AssetDatabase.Refresh();
}
//遍历目录
public static void ErgodicDirectory(string path, Dictionary<string, string> bundlePathDic) {
DirectoryInfo dir = new DirectoryInfo(path);
if (dir != null)
{
FileSystemInfo[] fileInfo = dir.GetFileSystemInfos();
for (int i = 0; i < fileInfo.Length; i++) {
FileSystemInfo tmpFile = fileInfo[i];
string tmpPath = Path.Combine(path, tmpFile.Name);
if (tmpFile is DirectoryInfo)
{
ErgodicDirectory(tmpPath, bundlePathDic);
}
else {
SetAssetBundleName(tmpFile, bundlePathDic);
}
}
}
else
{
Debug.Log("this path is not exit");
}
}
//设置assetbundle name
public static void SetAssetBundleName(FileSystemInfo fileSystemInfo,Dictionary<string, string> bundlePathDic) {
FileInfo fileInfo = fileSystemInfo as FileInfo;
if (fileInfo.Extension == ".meta")
{
return;
}
string assetBundleName = fileInfo.Directory.Name;
string fullPath = FixedWindowsPath(fileInfo.DirectoryName);
int tmpCount1 = fullPath.Length;
int tmpCount2 = assetFullPath.Length;
int assetPathCount = tmpCount1 - tmpCount2;
string assetBundlePath = fullPath.Substring(tmpCount2, assetPathCount);
string path = assetPath + assetBundlePath+"/" + fileInfo.Name;
Debug.Log("path "+ path);
AssetImporter importer = AssetImporter.GetAtPath(path);
//设置assetbundle name
importer.assetBundleName = assetBundleName;
//设置assetbundle 后缀
if (fileInfo.Extension == ".unity")
{
importer.assetBundleVariant = "u3d";
}
else
{
importer.assetBundleVariant = "ab";
}
//保存bundle的相对路径
int startIndex = fileInfo.FullName.IndexOf("Art");
int endIndex = fileInfo.FullName.Length;
int count = endIndex - startIndex;
string bundlePath = fileInfo.FullName.Substring(startIndex, count);
if (!bundlePathDic.ContainsKey(assetBundleName)) {
bundlePathDic.Add(assetBundleName, bundlePath);
}
}
//修正路径
public static string FixedWindowsPath(string path)
{
path = path.Replace("\\","/");
return path;
}
}
其中函数MarkAssetBundle,ErgodicDirectory,SetAssetBundleName为标记AssetBundle名字的
函数BuildeAssetBundle为打AssetBundle的
这时我们会在unity菜单中看到这两个工具
加载AssetBundle
先来看一下我们的例子
Art下有三个文件夹Load,Material,Texture,按照我们的规则这三个文件夹下的资源的AssetBundle的名字和这三个文件夹一样。
这样我们用上面写的自动打AssetBundle的工具就打出了三个AsserBundle包分别是load.ab,material.ab,texture.ab,其中后缀.ab是在自动打AssetBundle的工具代码中指定的。其中正方体Cube在load.ab包中,材质m_Material在material.ab中,纹理detail在texture.ab中。
其中正方体Cube用的材质是m_Material,材质m_Material用的纹理是detail。
我们打出的AssetBundle如下:
在文件夹中会看到这些东西
其中以.ab(我们自己指定的)结尾的是我们的AssetBundle包,以.manifest结尾的是该AssetBundle的信息
因为我们的正方体Cube用的材质是m_Material,而m_Material在material.ab中,所以load.ab依赖material.ab
打AssetBundle后会有一个和AssetBundle输出目录一样名字的AssetBundle和.manifest文件,因为我们最终把AssetBundle打到目录StreamingAssets/windows中了,所以我们会看到这样两个文件
其中windows.manifest文件中记录了我们所有的AssetBundle信息和依赖关系
假如我们想从load.ab中加载出来立方体Cube,因为立方体Cube依赖material.ab,所以我们要把material.ab加载出来,又因为material.ab依赖texture.ab,所以我们要把texture.ab加载出来
我们先写一个简单的加载资源的代码,在下一章中我们会把所有AssetBundle的依赖和被依赖关系都管理起来,也会写出对AssetBundle缓存的清理的接口
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Test : MonoBehaviour {
string manifestPath = "";
public AssetBundle manifestLoader;
public AssetBundleManifest assetManifest;
public Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();
bool isManifestFinished = false;
// Use this for initialization
void Start()
{
manifestPath = IPathTools.GetABManifestName();
StartCoroutine(LoadManifest());
string path = IPathTools.GetAssetBundlePath() + "/load.ab";
StartCoroutine(LoadAssetBundle(path));
}
public IEnumerator LoadManifest() {
WWW manifest = new WWW(manifestPath);
yield return manifest;
if (!string.IsNullOrEmpty(manifest.error))
{
Debug.Log(manifest.error);
}
else {
if (manifest.progress >= 1.0f) {
isManifestFinished = true;
Debug.Log("加载manifest成功");
manifestLoader = manifest.assetBundle;
assetManifest = manifestLoader.LoadAsset("AssetBundleManifest") as AssetBundleManifest;
//输出所有的AssetBundle的名字
string[] str = assetManifest.GetAllAssetBundles();
for (int i = 0; i < str.Length; i++)
{
Debug.Log(str[i]);
}
}
}
}
//加载AssetBundle
public IEnumerator LoadAssetBundle(string path)
{
if (!isManifestFinished)
{
yield return null;
}
WWW www = new WWW(path);
yield return www;
Debug.Log(path);
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
}
else
{
AssetBundle ab = www.assetBundle;
if (!abDic.ContainsKey(ab.name))
{
abDic.Add(ab.name, ab);
Debug.Log("abName " + ab.name);
}
string[] dependencies = assetManifest.GetAllDependencies(ab.name);
for (int i = 0; i < dependencies.Length; i++)
{
if (!abDic.ContainsKey(dependencies[i]))
{
string abPath = IPathTools.GetAssetBundlePath() + "/" + dependencies[i];
yield return LoadAssetBundle(abPath);
}
}
//测试实例化立方体Cube
if (ab.name.Equals("load.ab"))
{
AssetBundle mainAb = abDic["load.ab"];
Object o = mainAb.LoadAsset("Cube");
Instantiate(o, Vector3.zero, Quaternion.identity);
}
}
www.Dispose();
}
//循环加载AssetBundle依赖
public IEnumerator LoadDependenciesAssetBundle(string path) {
if (!isManifestFinished)
{
yield return null;
}
WWW www = new WWW(path);
yield return www;
Debug.Log("依赖 "+path);
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
}
else {
AssetBundle ab = www.assetBundle;
if (!abDic.ContainsKey(ab.name))
{
abDic.Add(ab.name, ab);
Debug.Log("依赖abName " + ab.name);
}
string[] dependencies = assetManifest.GetAllDependencies(ab.name);
for (int i = 0; i < dependencies.Length; i++)
{
string abPath = IPathTools.GetAssetBundlePath() + "/" + dependencies[i];
yield return LoadDependenciesAssetBundle(abPath);
}
}
}
void Destroy() {
//manifestLoader.Unload(false);
}
}