1.AssetBundle打包
unity 5.x版本AssetBundle打包,只需要設置好AssetBundle的名稱后,unity會自動將其打包,無需處理其他,唯獨需要做的是設置好個AssetBundle的名稱。
注意:AssetBunlde的名稱只能設置小寫字母,即使你寫成大寫也會被自動轉置成大寫字母,而且名稱中支持“/”,如:“AssetBundles/cube.unity3d”,.unity3d的后綴是自己設置的,可以不設置
代碼:

using UnityEngine; using UnityEditor; using System.IO; public class CreateAssetBundles : Editor { [MenuItem("Tools/Build AssetBundles")] static void BuildAllAssetBundles() { BuildPipeline.BuildAssetBundles(Application.dataPath + "/AssetBundles", BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.Android); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.LogWarning("打包成功"); } [MenuItem("Tools/設置固定名")] public static void saveAsStaticName() { string Path = "Prefabs"; string abName = "building.ab"; SetVersionDirAssetName(Path, abName);//第一個參數是路徑 第二個參數是Ab名字 默認前綴為 Application.dataPath + "/"+ Path } [MenuItem("Tools/設定文件名")] public static void saveAsPrefabName() { string Path = "Prefabs"; SetAssetNameAsPrefabName(Path);//第一個參數是路徑 } public static void SetVersionDirAssetName(string fullPath, string abName) { var relativeLen = fullPath.Length + 8; // Assets 長度 fullPath = Application.dataPath + "/" + fullPath + "/"; if (Directory.Exists(fullPath)) { EditorUtility.DisplayProgressBar("設置AssetName名稱", "正在設置AssetName名稱中...", 0f); var dir = new DirectoryInfo(fullPath); var files = dir.GetFiles("*", SearchOption.AllDirectories); for (var i = 0; i < files.Length; ++i) { var fileInfo = files[i]; EditorUtility.DisplayProgressBar("設置AssetName名稱", "正在設置AssetName名稱中...", 1f * i / files.Length); if (!fileInfo.Name.EndsWith(".meta")) { var basePath = fileInfo.FullName.Substring(fullPath.Length - relativeLen);//.Replace('\\', '/'); var importer = AssetImporter.GetAtPath(basePath); if (importer && importer.assetBundleName != abName) { importer.assetBundleName = abName; } } } EditorUtility.ClearProgressBar(); } } public static void SetAssetNameAsPrefabName(string fullPath) { var relativeLen = fullPath.Length + 8; // Assets 長度 fullPath = Application.dataPath + "/" + fullPath + "/"; if (Directory.Exists(fullPath)) { EditorUtility.DisplayProgressBar("設置AssetName名稱", "正在設置AssetName名稱中...", 0f); var dir = new DirectoryInfo(fullPath); var files = dir.GetFiles("*", SearchOption.AllDirectories); for (var i = 0; i < files.Length; ++i) { var fileInfo = files[i]; string abName = fileInfo.Name; EditorUtility.DisplayProgressBar("設置AssetName名稱", "正在設置AssetName名稱中...", 1f * i / files.Length); if (!fileInfo.Name.EndsWith(".meta")) { var basePath = fileInfo.FullName.Substring(fullPath.Length - relativeLen);//.Replace('\\', '/'); var importer = AssetImporter.GetAtPath(basePath); //abName = AssetDatabase.AssetPathToGUID(basePath); if (importer && importer.assetBundleName != abName) { importer.assetBundleName = abName; } } } EditorUtility.ClearProgressBar(); } } /// <summary> /// AssetBundleManifestName == 對應AB依賴列表文件 /// </summary> private static string AssetBundle_BuildDirectory_Path = @Application.streamingAssetsPath + "/../../../" + "AssetBundles"; private static string AssetBundle_TargetDirectory_Path = @Application.streamingAssetsPath + "/" + "ABFiles"; [MenuItem("Tools/Asset Bundle/Build Asset Bundles", false, 0)] public static void BuildAssetBundleAndroid() { //Application.streamingAssetsPath對應的StreamingAssets的子目錄 DirectoryInfo AB_Directory = new DirectoryInfo(AssetBundle_BuildDirectory_Path); if (!AB_Directory.Exists) { AB_Directory.Create(); } FileInfo[] filesAB = AB_Directory.GetFiles(); foreach (var item in filesAB) { Debug.Log("******刪除舊文件:" + item.FullName + "******"); item.Delete(); } #if UNITY_ANDROID BuildPipeline.BuildAssetBundles(AB_Directory.FullName, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.Android); #elif UNITY_IPHONE BuildPipeline.BuildAssetBundles(AB_Directory.FullName, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.iOS); #else BuildPipeline.BuildAssetBundles(AB_Directory.FullName, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.StandaloneWindows64); #endif Debug.Log("******AssetBundle打包完成******"); Debug.Log("將要轉移的文件夾是:" + AssetBundle_TargetDirectory_Path); FileInfo[] filesAB_temp = AB_Directory.GetFiles(); DirectoryInfo streaming_Directory = new DirectoryInfo(AssetBundle_TargetDirectory_Path); FileInfo[] streaming_files = streaming_Directory.GetFiles(); foreach (var item in streaming_files) { item.Delete(); } AssetDatabase.Refresh(); foreach (var item in filesAB_temp) { if (item.Extension == "") { item.CopyTo(AssetBundle_TargetDirectory_Path + "/" + item.Name, true); } } AssetDatabase.Refresh(); Debug.Log("******文件傳輸完成******"); } private static string _dirName = ""; /// <summary> /// 批量命名所選文件夾下資源的AssetBundleName. /// </summary> [MenuItem("Tools/Asset Bundle/Set Asset Bundle Name")] static void SetSelectFolderFileBundleName() { UnityEngine.Object[] selObj = Selection.GetFiltered(typeof(Object), SelectionMode.Unfiltered); foreach (Object item in selObj) { string objPath = AssetDatabase.GetAssetPath(item); DirectoryInfo dirInfo = new DirectoryInfo(objPath); if (dirInfo == null) { Debug.LogError("******請檢查,是否選中了非文件夾對象******"); return; } _dirName = dirInfo.Name; string filePath = dirInfo.FullName.Replace('\\', '/'); filePath = filePath.Replace(Application.dataPath, "Assets"); AssetImporter ai = AssetImporter.GetAtPath(filePath); ai.assetBundleName = _dirName; SetAssetBundleName(dirInfo); } AssetDatabase.Refresh(); Debug.Log("******批量設置AssetBundle名稱成功******"); } static void SetAssetBundleName(DirectoryInfo dirInfo) { FileSystemInfo[] files = dirInfo.GetFileSystemInfos(); foreach (FileSystemInfo file in files) { if (file is FileInfo && file.Extension != ".meta" && file.Extension != ".txt") { string filePath = file.FullName.Replace('\\', '/'); filePath = filePath.Replace(Application.dataPath, "Assets"); AssetImporter ai = AssetImporter.GetAtPath(filePath); ai.assetBundleName = _dirName; } else if (file is DirectoryInfo) { string filePath = file.FullName.Replace('\\', '/'); filePath = filePath.Replace(Application.dataPath, "Assets"); AssetImporter ai = AssetImporter.GetAtPath(filePath); ai.assetBundleName = _dirName; SetAssetBundleName(file as DirectoryInfo); } } } /// <summary> /// 批量清空所選文件夾下資源的AssetBundleName. /// </summary> [MenuItem("Tools/Asset Bundle/Reset Asset Bundle Name")] static void ResetSelectFolderFileBundleName() { UnityEngine.Object[] selObj = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Unfiltered); foreach (UnityEngine.Object item in selObj) { string objPath = AssetDatabase.GetAssetPath(item); DirectoryInfo dirInfo = new DirectoryInfo(objPath); if (dirInfo == null) { Debug.LogError("******請檢查,是否選中了非文件夾對象******"); return; } _dirName = null; string filePath = dirInfo.FullName.Replace('\\', '/'); filePath = filePath.Replace(Application.dataPath, "Assets"); AssetImporter ai = AssetImporter.GetAtPath(filePath); ai.assetBundleName = _dirName; SetAssetBundleName(dirInfo); } AssetDatabase.Refresh(); Debug.Log("******批量清除AssetBundle名稱成功******"); } }
上述代碼中有一些測試函數,使用時可進行測試
2.AssetBundle加載
下述代碼中,cube,sphere等都是在unity項目中自己創建的3d預制體,自行創建即可。創建完成使用1中的方法打包AssetBundle。注意代碼中的cube大小寫問題,小寫的是設置的AssetBundle名稱,大寫的是Cube預制體的名稱,不要混淆。
代碼:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class AssetBundleLoad : MonoBehaviour { // Use this for initialization void Start () { this.load1(); //this.load2(); //this.load3(); //this.load4(); } void load1() { //1.先加載cube后加載sphere //這種方式並沒有先加載cube的依賴文件,按理說應該加載出來的cube上是sphere是missing的,但是unity5.6.3f1 //加載並未missing,不知是不是unity版本的優化,不過好習慣還是先加載依賴文件,如load2()。 //加載assetbundlemanifest文件 AssetBundle assetBundleManifest = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/AssetBundles"); if(null != assetBundleManifest) { AssetBundleManifest manifest = assetBundleManifest.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); //加載cube AssetBundle bundle = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/cube.prefab"); GameObject obj = bundle.LoadAsset<GameObject>("Cube"); if(null != obj) { GameObject cube = Instantiate(obj); cube.transform.SetParent(GameObject.Find("UIRoot").transform); } //加載cube的依賴文件 string[] depends = manifest.GetAllDependencies("cube.prefab"); AssetBundle[] dependsAssetbundle = new AssetBundle[depends.Length]; for(int index = 0; index < depends.Length; ++index) { dependsAssetbundle[index] = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/" + depends[index]); obj = dependsAssetbundle[index].LoadAsset<GameObject>("Sphere"); if (null != obj) { GameObject sphere = Instantiate(obj); sphere.transform.SetParent(GameObject.Find("UIRoot").transform); } } } } void load2() { //2.先加載sphere再加載cube //加載assetBundleManifest文件 AssetBundle assetBundleManifest = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/AssetBundles"); if(null != assetBundleManifest) { AssetBundleManifest manifest = assetBundleManifest.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); //加載cube依賴文件 string[] depends = manifest.GetAllDependencies("cube.prefab"); AssetBundle[] dependsAssetbundle = new AssetBundle[depends.Length]; for (int index = 0; index < depends.Length; ++index) { dependsAssetbundle[index] = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/" + depends[index]); GameObject obj1 = dependsAssetbundle[index].LoadAsset<GameObject>("Sphere"); if (null != obj1) { GameObject sphere = Instantiate(obj1); sphere.transform.SetParent(GameObject.Find("UIRoot").transform); } } //加載cube AssetBundle bundle = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/cube.prefab"); GameObject obj = bundle.LoadAsset<GameObject>("Cube"); if(null != obj) { GameObject sphere = Instantiate(obj); sphere.transform.SetParent(GameObject.Find("UIRoot").transform); } } } void load3() { //3.只加載cube不加載sphere //無需加載assetBundleManifest文件,直接加載cube AssetBundle bundle = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/cube.prefab"); GameObject obj = bundle.LoadAsset<GameObject>("Cube"); if (null != obj) { GameObject sphere = Instantiate(obj); sphere.transform.SetParent(GameObject.Find("UIRoot").transform); } } void load4() { //4.兩個預制打包成同一個AssetBundle //無需加載assetBundleManifest文件,直接加載cube AssetBundle bundle = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/cube2.prefab"); GameObject obj = bundle.LoadAsset<GameObject>("Cube_1"); if (null != obj) { GameObject cube = Instantiate(obj); cube.transform.SetParent(GameObject.Find("UIRoot").transform); } obj = bundle.LoadAsset<GameObject>("Cube_2"); if (null != obj) { GameObject cube = Instantiate(obj); cube.transform.SetParent(GameObject.Find("UIRoot").transform); } } }

using UnityEngine; public class Relation : MonoBehaviour { //掛在cube預制上的測試腳本 public GameObject sphere_go; public GameObject capsule_go; // Use this for initialization void Start () { ScriptRelation t = new ScriptRelation(); t.printLog(); } // Update is called once per frame void Update () { } }

using UnityEngine; public class ScriptRelation{ public void printLog() { Debug.Log("xxxxxxxxxxxxxxxxxx"); } }

using System.Collections; using System.Collections.Generic; using UnityEngine; public class TestAssetBundle : MonoBehaviour { public string AssetBundleName = "cube"; private string dir = ""; private AssetBundle bundle = null; private Object asset = null; private GameObject go = null; private AssetBundleManifest manifest = null; // Use this for initialization void Start () { dir = Application.dataPath + "/AssetBundles/"; } void OnGUI() { if (GUILayout.Button("LoadAssetBundle", GUILayout.Width(200), GUILayout.Height(50))) { LoadBundle(); } if (GUILayout.Button("LoadAsset", GUILayout.Width(200), GUILayout.Height(50))) { LoadAsset(); } if (GUILayout.Button("Instantiate", GUILayout.Width(200), GUILayout.Height(50))) { Instantiate(); } if (GUILayout.Button("Destory", GUILayout.Width(200), GUILayout.Height(50))) { Destroy(); } if (GUILayout.Button("Unload", GUILayout.Width(200), GUILayout.Height(50))) { UnLoad(); } if (GUILayout.Button("UnloadForce", GUILayout.Width(200), GUILayout.Height(50))) { UnLoadForce(); } if (GUILayout.Button("UnloadUnusedAssets", GUILayout.Width(200), GUILayout.Height(50))) { UnloadUnusedAssets(); } //bundle依賴包加載 if (GUILayout.Button("LoadAssetBundleManifest", GUILayout.Width(200), GUILayout.Height(50))) { LoadAssetBundleManifest(); } if (GUILayout.Button("LoadBundleAndDeps", GUILayout.Width(200), GUILayout.Height(50))) { LoadBundleAndDeps(); } } void LoadBundle() { bundle = AssetBundle.LoadFromFile(System.IO.Path.Combine(dir, AssetBundleName)); if(null == bundle) { Debug.LogError("LoadBundle Failed"); } } void LoadAsset() { if (null == bundle) return; asset = bundle.LoadAsset<Object>("Cube"); if (null == asset) Debug.LogError("LoadAsset Failed"); } void Instantiate() { if (null == asset) return; go = GameObject.Instantiate<Object>(asset) as GameObject; if (null == go) Debug.LogError("Instantiate Failed"); else { go.transform.SetParent(GameObject.Find("UIRoot").transform); } } void Destroy() { if (null == go) return; GameObject.Destroy(go); go = null; } /** * AssetBundle.unload(bool unloadAllLoadedObjects) 接口用來卸載AssetBundle文件。 * 參數為false時,調用該接口后,只會卸載AssetBundle對象自身,並不會影響AssetBundle中加載的Assets。 * 參數為true時,除了AssetBundle對象自身,所有從當前AssetBundle中加載的Assets也會被同時卸載,不管這個Assets是否還在使用中。 * 官方推薦參數一般設置為false,只有當很明確知道從AssetsBundle中加載的Assets不會被任何對象引用時,才將參數設置成true。 **/ void UnLoad() { if (null == bundle) return; bundle.Unload(false); //GameObject.Instantiate<Object>(asset); //asset可用 asset = null; bundle = null; } void UnLoadForce() { if (null == bundle) return; bundle.Unload(true); //GameObject.Instantiate<Object>(asset); //報錯:asset已被銷毀 asset = null; bundle = null; } void UnloadUnusedAssets() { Resources.UnloadUnusedAssets(); } void LoadAssetBundleManifest() { var bundle = AssetBundle.LoadFromFile(System.IO.Path.Combine(dir, "AssetBundles")); manifest = bundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); //unload bundle.Unload(false); bundle = null; } void LoadBundleAndDeps() { string bundleName = "cube"; string[] dependence = manifest.GetDirectDependencies(bundleName); for(int i=0; i<dependence.Length; ++i) { AssetBundle.LoadFromFile(System.IO.Path.Combine(dir, dependence[i])); } var bundle = AssetBundle.LoadFromFile(System.IO.Path.Combine(dir, bundleName)); var asset = bundle.LoadAsset<GameObject>("Cube"); bundle.Unload(false); bundle = null; go = GameObject.Instantiate<GameObject>(asset); } }
上述代碼有兩個測試腳本,AssetBundleLoad.cs只做了AssetBudle加載的測試。TestAssetBundle.cs做了AssetBundle加載,卸載,使用等測試,比較完整。
3.unity項目中的3個特殊文件夾
a.Resources(只讀)
Resources文件夾下的資源會被全部打包到apk或者ipa里面,相當與一個unity默認的AssetBundle,按理說是可以將游戲中所有的資源全部放在Resources進行加載,但是
Resources下的文件數量會影響游戲的啟動速度,越多越慢。其次游戲為了降低drawcall,需要對相同材質的圖像打包圖集,但是如果放在Resources里面是沒法打包圖集的。
Resources下的資源打包時會進行壓縮
b.StreamingAssets(只讀)
StreamingAssets文件夾下的資源會被全部打包到apk或者ipa里面
StreamingAssets文件夾下的資源不會影響游戲啟動速度
StreamingAssets文件夾下的資源不會被壓縮,所以占用內存較大
c.persistentDataPath(可讀寫)
persistentDataPath文件夾在unity項目中是不存在的,他是程序的沙盒文件夾,只有你游戲安裝完成之后,才會存在這個目錄,但是它可讀寫。
綜上三種文件夾,我們自己在使用時,Resources里面放的資源只是在本地開發階段使用,打包的時候會把Resources中的文件都考本出去打成AssetBundle包,然后再將打包好的AssetBundle放到StreamingAssets文件夾下打包apk。
游戲運行時再將資源考本到persistentDataPath文件夾下,因為這個目錄可讀寫,所以能夠用來做資源熱更新。
如有錯誤,敬請指正。
///////////****以下摘自:http://www.cnblogs.com/jiangshuai52511/p/6437239.html 作者:涼城舊巷舊少年 ******/////////
5.3.針對項目的建議
由於以上分析的幾種加載手段各有各的使用情景和特點。因此建議在我們的項目中按照以下情景使用這些方法:
- 隨游戲一同發布的AssetBundle(一般位於StreamingAssets文件夾中):
- 在打AssetBundle包時,使用LZ4壓縮格式進行打包(開啟BuildAssetBundleOptions.ChunkBasedCompression即可)。
- 在運行時需要加載AssetBundle對象時,使用LoadFromFile方法進行加載。
- 這樣做的好處是:即可以將AssetBundle文件壓縮,又可以兼顧加載速度,且節約內存。
- 作為更新包,需要從服務端下載的AssetBundle:
- 在打AssetBundle包時,使用默認的LZMA格式壓縮。
- 使用http://WWW.LoadFromCacheOrDownload方法下載並緩存AssetBundle包文件。
- 這樣做的好處是:獲得了最大的壓縮率,在下載過程中可以減少數據傳輸量。同時,在本地磁盤創建緩存之后,又可以兼顧之后的加載速度,且節約內存。
- 我們自己進行加密的AssetBundle:
- 在打AssetBundle包時,使用LZ4壓縮格式進行打包(開啟BuildAssetBundleOptions.ChunkBasedCompression即可)。
- 在運行時需要加載AssetBundle對象時,使用LoadFromMemory方法進行加載。(這也是從內存中使用流數據加載AssetBundle對象的僅有的使用場景。)
- 我們自己壓縮的AssetBundle:
- 我們自己也可以使用第三方庫或工具對生成的AssetBundle包文件進行壓縮,如果需要這樣做,則我們最好不要再使用Unity3D對 AssetBundle進行壓縮,因此在打包時選擇開啟 BuildAssetBundleOptions.UncompressedAssetBundle。
- 在運行時需要加載AssetBundle對象時,使用LoadFromFileAsync方法進行異步加載。
///////////****以上摘自:http://www.cnblogs.com/jiangshuai52511/p/6437239.html 作者:涼城舊巷舊少年 ******/////////
參考文章:
http://www.xuanyusong.com/archives/3229
http://www.cnblogs.com/AaronBlogs/p/6837682.html
http://www.cnblogs.com/kanekiken/p/7533510.html
http://www.cnblogs.com/murongxiaopifu/p/4199541.html
http://www.cnblogs.com/jiangshuai52511/p/6437239.html
http://blog.csdn.net/u010377179/article/category/6413676/3
http://blog.csdn.net/suifcd/article/details/51570003
AssetBundle內存相關:
http://blog.csdn.net/sgnyyy/article/details/39268215