AssetBundle 是一种压缩文件,压缩时用到的压缩格式可以在打bundle的时候指定,BuildPipeline.BuildAssetBundles方法第二个参数可以指定压缩格式。

使用AssetBundle流程分为两部

1.把资源打成AssetBundle

先把资源(纹理,材质,模型)指定一个AssetBundle名字和后缀

unity 安卓签名不一致_加载

然后调用BuildPipeline.BuildAssetBundles方法把资源打成AssetBundle

2.从AssetBundle中加载资源

常用的方式为用WWW先把AssetBundle下载下来(通过网络或者是从本地加载),然后从WWW中加载出来AssetBundle,然后从AssetBundle中加载出来资源,然后再把资源实例化(如果是纹理图片之类的可以直接使用)

3.关于内存

先来看看一个AssetBundle资源占的内存情况

用WWW下载完后,WWW会保留一份缓存,从WWW中加载出来AssetBundle后会有一份AssetBundle的缓存,从AssetBundle中加载出来资源后有一份资源的缓存,把资源实例化后会有一份实例化的缓存。所以我们要管理好这些缓存,及时把没用的清理掉。


我们先来写一个自动打AssetBundle的工具

首先创建一个文件夹 Art,我们把项目中需要的资源按类型放到这个文件夹

unity 安卓签名不一致_unity 安卓签名不一致_02

我们会把这里面的资源的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菜单中看到这两个工具

unity 安卓签名不一致_ci_03


加载AssetBundle

先来看一下我们的例子

unity 安卓签名不一致_bundle_04

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如下:

unity 安卓签名不一致_unity 安卓签名不一致_05

在文件夹中会看到这些东西

unity 安卓签名不一致_unity 安卓签名不一致_06

其中以.ab(我们自己指定的)结尾的是我们的AssetBundle包,以.manifest结尾的是该AssetBundle的信息

unity 安卓签名不一致_unity 安卓签名不一致_07

因为我们的正方体Cube用的材质是m_Material,而m_Material在material.ab中,所以load.ab依赖material.ab

打AssetBundle后会有一个和AssetBundle输出目录一样名字的AssetBundle和.manifest文件,因为我们最终把AssetBundle打到目录StreamingAssets/windows中了,所以我们会看到这样两个文件

unity 安卓签名不一致_ci_08


其中windows.manifest文件中记录了我们所有的AssetBundle信息和依赖关系

unity 安卓签名不一致_unity 安卓签名不一致_09


假如我们想从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);
    }
   
}