【小松教你手游开发】【unity系统模块开发】Unity5.5.2UI打包AssetBundle
之前已经有几篇文章写打包AssetBundle,但毕竟没有实际在项目中写过都写的比较浅。
刚好最近项目更新Unity5.5.2就顺便由我来更新ui打包流程
这里就把这次的经验写一下
这里还是稍微解释一下打包的基本目的:
打包ui就是把你做的界面打包出来成assetbundle包,讲道理你就把每个界面打成bundle包在游戏中你就可以直接加载来用,但是这样子的话你的每个bundle包就会非常的大,为什么呢,是因为这样子每个界面的bundle包里都包含这个界面用到的字体,贴图atlas,texture,shader还可以有你自己使用的动态组件。
这样子你的同一份资源都复制了很多份出来。
当然不能这样子做
所以我们就需要把这些atlas,font给剥离开来独立打包
不过新的打包方式让我们不用再手动剥离开来这些东西,也就是没有了剥离之后要重新引用的烦恼。
所要做的事就是把需要打包的东西改一改它的AssetBudnle名再一起build就好了
总的来说,就是把一个ui界面的下的atlas,font,texture,component记录下来,
通过修改这个ui.prefab,atlas,font,texture,compoent的预制件的bundle名,最后调一下打包函数就可以了
(有的项目texutre和shader是有自己写的管理类来加载,需要把引用置空的,这时候就需要实例ui.prefab,并把引用赋空,拷贝一份到另外一个文件夹中)
上一篇ui打包文章
http://blog.csdn.net/chrisfxs/article/details/55046339
而这次的流程是
1.清理数据,建立文件夹
2.遍历所有文件夹下的ui界面prefab
3.遍历所有prefab下的compoent(动态组件)
4.找出所有的资源(如atlas,font)用个list记录下来
5.修改这些资源和这个ui.prefab的assetbundle名
6.build
补充一点!
游戏加载assetbundle的时候要先加载atlas,font这些资源,最后加载ui资源
这样才能保证引用没有丢失
下面是代码
//#if UNITY_5_MODEusing UnityEngine;using UnityEditor;using System.Collections.Generic;using UnityEditor.SceneManagement;using UnityEngine.SceneManagement;using System.IO;public class BuildAssetbundle{ public static string m_destFileName = "Assetbundle";#if UNITY_ANDROID public static string PlatformExtension = ".android"; public static string Extension = ".x.android"; public static BuildTarget Target = BuildTarget.Android; public static string ASSETBUNDLE_PATH = Application.dataPath + "/../AndroidResources/StreamingAssets"; public static string FULL_ASSETBUNDLE_PATH = ASSETBUNDLE_PATH + "/" + m_destFileName;#elif UNITY_IOS public static string PlatformExtension = ".ios"; public static string Extension = ".x.ios"; public static BuildTarget Target = BuildTarget.iOS; public static string ASSETBUNDLE_PATH = Application.dataPath + "/../IosResources/StreamingAssets";#endif#if BUILD_UI //需要打包的资源 public static List fontList = new List(); public static List atlasList = new List(); public static List componentList = new List(); public static List panelList = new List(); public static void BuildOnePanel() { ClearAllBundleName(); CleanTempData(); GameObject[] selectGameObjects = Selection.gameObjects; UnityEngine.Object selectFloder = Selection.activeObject; if (selectGameObjects != null && selectGameObjects.Length != 0) { foreach (GameObject selGb in selectGameObjects) { string selGbPath = AssetDatabase.GetAssetPath(selGb); if (selGbPath.StartsWith(BuildUtils.PANEL_ASSET_PATH)) { if (selGbPath.EndsWith(BuildUtils.PREFAB_SUFFIX)) { GameObject uiInstance = PrefabUtility.InstantiatePrefab(selGb) as GameObject; CheckComponent(uiInstance); FindRefrence(uiInstance.transform); string prefabPath = BuildUtils.TEMP_PANEL_PREFAB_PATH + "/" + uiInstance.name + BuildUtils.PREFAB_SUFFIX; PrefabUtility.CreatePrefab(prefabPath, uiInstance); GameObject prefabAsset = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; panelList.Add(prefabAsset); UnityEngine.Object.DestroyImmediate(uiInstance); } else { Debug.LogWarning("选择的资源 " + selGb.name + " 不是界面Prefab!"); } } else { Debug.LogWarning("只能打包放在 " + BuildUtils.PANEL_ASSET_PATH + " 下面的界面Prefab"); } } StartBuildGameObjects(); BuildUtils.DeleteFileWithSuffixs(ASSETBUNDLE_PATH, new string[]{".manifest",""}, true, false); ShowBundleFolder(); EditorUtility.DisplayDialog("提示", "打包完成!", "确定"); } else { Debug.LogWarning("只能对 " + BuildUtils.PANEL_ASSET_PATH + " 下面的UI使用!"); } } public static void BuildAllPanel(bool hint) { ClearAllBundleName(); if (!hint || EditorUtility.DisplayDialog("提示", "确定打包 " + BuildUtils.PANEL_ASSET_PATH + " 下所有界面?", "确定", "取消")) { CleanTempData(); string[] files = Directory.GetFiles(BuildUtils.PANEL_ASSET_PATH); if (files.Length != 0) { foreach (string file in files) { if (file.EndsWith(BuildUtils.PREFAB_SUFFIX)) { GameObject uiInstance = PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath(file)) as GameObject; string ui_panel_prefab_name = Path.GetFileNameWithoutExtension(file); CheckComponent(uiInstance); FindRefrence(uiInstance.transform); string prefabPath = BuildUtils.TEMP_PANEL_PREFAB_PATH + "/" + uiInstance.name + BuildUtils.PREFAB_SUFFIX; PrefabUtility.CreatePrefab(prefabPath, uiInstance); GameObject prefabAsset = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; panelList.Add(prefabAsset); UnityEngine.Object.DestroyImmediate(uiInstance); } } StartBuildGameObjects(); BuildUtils.DeleteFileWithSuffixs(ASSETBUNDLE_PATH, new string[]{".manifest",""}, true, false); } if (hint) { ShowBundleFolder(); EditorUtility.DisplayDialog("提示", BuildUtils.PANEL_ASSET_PATH + " 下的界面界面全部打包完成!", "确定"); } } } /// /// 对每一个要打包的Prefab保存组件信息,找出Atlas、Font等// /// /// COM. static void FindRefrence(Transform com) { PrefabHierarchyInfo info = com.gameObject.AddComponent(); info.transforms = com.gameObject.GetComponentsInChildren(true); info.myuianchors = com.gameObject.GetComponentsInChildren(true); UILabel[] uilabels = com.gameObject.GetComponentsInChildren(true); UISprite[] uisprites = com.gameObject.GetComponentsInChildren(true); UITexture[] uitextures = com.gameObject.GetComponentsInChildren(true); foreach (UILabel lab in uilabels) { UIFont font = lab.bitmapFont; if (font == null) { Debug.LogWarning(com.gameObject.name + " 下出现未使用UIFont或者没有知道字体的UIlabel, " + lab.gameObject.name); continue; } string uifontPath = AssetDatabase.GetAssetPath(font); if (!uifontPath.StartsWith(BuildUtils.UIFONT_PREFAB_PATH)) Debug.LogWarning("使用了没有放在" + BuildUtils.UIFONT_PREFAB_PATH + " 目录下的UIFont:" + uifontPath); if (!fontList.Contains(font)) { fontList.Add(font); } lab.text = ""; } foreach (UISprite sp in uisprites) { UIAtlas atlas = sp.atlas; if (atlas == null) continue; string atlasPath = AssetDatabase.GetAssetPath(atlas); if (!atlasPath.StartsWith(BuildUtils.ATLAS_PREFAB_PATH)) Debug.LogWarning("使用了未放在" + BuildUtils.ATLAS_PREFAB_PATH + "目录下的Atlas:" + atlasPath); sp.RecordAtlasName = atlas.name; if (!atlasList.Contains(atlas)) atlasList.Add(atlas); } foreach (UITexture t in uitextures) { t.mainTexture = null; t.shader = null; } com.parent = null; } /// /// 对找出的需要打包资源打包// /// private static void StartBuildGameObjects() { EditorUtility.UnloadUnusedAssetsImmediate(); AssetbundleCommonFun.SetAssetBundlesName(fontList, Extension, false); AssetbundleCommonFun.SetAssetBundlesName(atlasList, Extension, false); AssetbundleCommonFun.SetAssetBundlesName(componentList, Extension, false); AssetbundleCommonFun.SetAssetBundlesName(panelList, Extension, false); AssetDatabase.Refresh(); BuildAll(); CleanTempData(); ClearAllBundleName(); } //在一次打包过程中,出现过的Component,检测多个Panel里面的(DynamicComponent)重名// static List componentNames = new List(); //支持无限深度的Component嵌套 static void CheckComponent(GameObject go) { string prefabPath = ""; Dictionary> allComponent = new Dictionary>(); FindComponents(go.transform, 0, ref allComponent); uint[] indexs = new uint[allComponent.Keys.Count]; allComponent.Keys.CopyTo(indexs, 0); System.Array.Sort(indexs); for (int i = indexs.Length - 1; i >= 0; i--) { foreach (Transform com in allComponent[indexs[i]]) { FindRefrence(com); prefabPath = BuildUtils.TEMP_COMPONENT_PREFAB_PATH + "/" + com.name + BuildUtils.PREFAB_SUFFIX; PrefabUtility.CreatePrefab(prefabPath, com.gameObject); GameObject prefabAsset = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; componentList.Add(prefabAsset); } } for (int i = indexs.Length - 1; i >= 0; i--) { foreach (Transform com in allComponent[indexs[i]]) { UnityEngine.Object.DestroyImmediate(com.gameObject); } } } /// /// 递归寻找动态组件// /// /// Tf. /// Index. /// Dict. static void FindComponents(Transform tf, uint index, ref Dictionary> dict) { if (tf.name.Contains("(DynamicComponent)")) { foreach (Transform sonTf in tf) { if (componentNames.Contains(sonTf.name)) { UnityEngine.Debug.LogError(tf.name + " 子物体中出现同名的动态组件 " + sonTf.name); UnityEngine.Object.DestroyImmediate(sonTf); continue; } if (!sonTf.name.Contains("(DynamicComponent)")) { componentNames.Add(sonTf.name); if (!dict.ContainsKey(index)) dict.Add(index, new List()); dict[index].Add(sonTf); if (sonTf.childCount > 0) FindComponents(sonTf, index + 1, ref dict); } else { if (sonTf.childCount > 0) FindComponents(sonTf, index + 1, ref dict); } } } else { foreach (Transform sonTf in tf) { if (sonTf.childCount > 0) FindComponents(sonTf, index + 1, ref dict); } } } static void CleanTempData() { fontList.Clear(); atlasList.Clear(); componentList.Clear(); componentNames.Clear(); }#endif #region tools static void BuildAll() { EditorUtility.ClearProgressBar(); AssetbundleCommonFun.CreatePath(ASSETBUNDLE_PATH + "/Assetbundle"); BuildPipeline.BuildAssetBundles(ASSETBUNDLE_PATH + "/Assetbundle", BuildAssetBundleOptions.ChunkBasedCompression, Target); } //[MenuItem("[AssetBundles]/Build Bundles Independent")] static void BuildBundlesIndependent() { List
using UnityEngine;using UnityEditor;using System.IO;using System.Collections.Generic;using System.Text;public class AssetbundleCommonFun{#if UNITY_ANDROID private static string strLocalFileName = "Android_LocalFilesInfo.asset";#elif UNITY_IOS private static string strLocalFileName = "Ios_LocalFilesInfo.asset";#endif private static List componentList = new List(); static AssetImporter m_importer = null; public static void ClearAllBundleName() { string[] allBundleNames = AssetDatabase.GetAllAssetBundleNames(); List hasBundleNameAssets = new List(); foreach (string n in allBundleNames) { foreach (string p in AssetDatabase.GetAssetPathsFromAssetBundle(n)) { hasBundleNameAssets.Add(p); } } float idx = 0f; foreach (string asset in hasBundleNameAssets) { SetBundleName(asset, "", false); EditorUtility.DisplayProgressBar("清除所有Bundle名称", "当前处理文件:" + Path.GetFileName(asset), idx++ / hasBundleNameAssets.Count); } EditorUtility.ClearProgressBar(); AssetDatabase.RemoveUnusedAssetBundleNames(); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); } public static void CreatePath(string path) { string NewPath = path.Replace("\\", "/"); string[] strs = NewPath.Split('/'); string p = ""; for (int i = 0; i < strs.Length; ++i) { p += strs[i]; if (i != strs.Length - 1) { p += "/"; } if (!Path.HasExtension(p)) { if (!Directory.Exists(p)) Directory.CreateDirectory(p); } } } public static string SetBundleName(string path, string name, bool isForce = false) { if (!isForce) { if (Directory.Exists(path)) { return null; } } string dictName = Path.GetDirectoryName(path); string fileName = Path.GetFileNameWithoutExtension(path); string extension = Path.GetExtension(path); dictName = dictName.Replace("UIResources_temp", "UIResources"); if (!isForce) { if (extension.Equals(".dll") || extension.Equals(".cs") || extension.Equals(".js") || (name != "" && fileName.Contains("atlas") && !extension.Equals(".prefab"))) { return null; } } m_importer = AssetImporter.GetAtPath(path); if (name != "") { m_importer.assetBundleName = dictName + "/" + fileName + name; } else { m_importer.assetBundleName = ""; } AssetDatabase.Refresh(); return m_importer.assetBundleName; } public static string GetBundleName(string path, string name, bool isForce = false) { string retBundleName = null; if (!isForce) { if (Directory.Exists(path)) { return retBundleName; } } string dictName = Path.GetDirectoryName(path); string fileName = Path.GetFileNameWithoutExtension(path); string extension = Path.GetExtension(path); if (!isForce) { if (extension.Equals(".dll") || extension.Equals(".cs") || extension.Equals(".js") || (name != "" && fileName.Contains("atlas") && !extension.Equals(".prefab"))) { return null; } } if (name != "") { retBundleName = dictName + "/" + fileName + name; //Object tex = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); //if (tex is Texture2D) //{ // SetTexture(tex as Texture2D); //} } Debug.Log("Asset name: " + fileName); AssetDatabase.Refresh(); return retBundleName; } static void FindComponents(Transform tf, uint index, ref Dictionary> dict) { if (tf.name.Contains("(DynamicComponent)")) { foreach (Transform sonTf in tf) { if (componentList.Contains(sonTf.name)) { Debug.LogWarning("same name component..."); UnityEngine.Object.DestroyImmediate(sonTf); continue; } if (!sonTf.name.Contains("(DynamicComponent)")) { Debug.Log("找到组件" + sonTf.name + "深度" + index); componentList.Add(sonTf.name); if (!dict.ContainsKey(index)) dict.Add(index, new List()); dict[index].Add(sonTf); if (sonTf.childCount > 0) FindComponents(sonTf, index + 1, ref dict); } else { if (sonTf.childCount > 0) FindComponents(sonTf, index + 1, ref dict); } } } else { foreach (Transform sonTf in tf) { if (sonTf.childCount > 0) FindComponents(sonTf, index + 1, ref dict); } } } public static void SetTexture(Texture2D tex) { string path = AssetDatabase.GetAssetPath(tex); TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; if (importer != null) { if (!(importer.textureType == TextureImporterType.NormalMap || importer.normalmap)) importer.mipmapEnabled = false; importer.npotScale = TextureImporterNPOTScale.ToNearest; importer.textureType = TextureImporterType.Default; importer.spriteImportMode = SpriteImportMode.None; importer.isReadable = false; if (path.Contains("_alpha8")) { importer.textureFormat = TextureImporterFormat.Alpha8; } else { importer.textureFormat = SetTextureFormat(tex, importer); } importer.maxTextureSize = 8192; AssetDatabase.ImportAsset(path); } } static TextureImporterFormat SetTextureFormat(Texture t, TextureImporter importer) { TextureImporterFormat TextureFormat;#if UNITY_IPHONE if (fun(t.width) && fun(t.height)) { if (importer.DoesSourceTextureHaveAlpha()) { TextureFormat = TextureImporterFormat.PVRTC_RGBA4; } else { TextureFormat = TextureImporterFormat.PVRTC_RGB4; } } else { TextureFormat = TextureImporterFormat.ETC2_RGBA8; }#elif UNITY_ANDROID if (fun(t.width) && fun(t.height)) { if (importer.DoesSourceTextureHaveAlpha()) { //importer.alphaIsTransparency = true; TextureFormat = TextureImporterFormat.ETC2_RGBA8; } else TextureFormat = TextureImporterFormat.ETC_RGB4; } else { if (importer.DoesSourceTextureHaveAlpha()) { //importer.alphaIsTransparency = true; TextureFormat = TextureImporterFormat.RGBA16; } else TextureFormat = TextureImporterFormat.RGB16; Debug.LogWarning("Texture " + t.name + " 尺寸不为2的幂次方,无法使用ETC压缩,当前使用 " + TextureFormat.ToString()); }#else TextureFormat = TextureImporterFormat.RGBA32;#endif return TextureFormat; } static bool fun(int v) { bool flag = false; if ((v > 0) && (v & (v - 1)) == 0) flag = true; return flag; } public static void ExportModifyFilesInfo(string path) { string extension = ""; string[] retFils = Directory.GetFiles(path, "*.manifest", SearchOption.AllDirectories); //LocalFilesInfo filesInfo = ScriptableObject.CreateInstance(); foreach (var item in retFils) { File.Delete(item); } } private static void FileCopy(string sourceFileName, string destFileName) { string dictName = Path.GetDirectoryName(destFileName); CreatePath(dictName); File.Copy(sourceFileName, destFileName); } private static string GetMD5HashFromFile(string fileName) { try { FileStream file = new FileStream(fileName, FileMode.Open); System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); byte[] retVal = md5.ComputeHash(file); file.Close(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < retVal.Length; i++) { sb.Append(retVal[i].ToString("x2")); } return sb.ToString(); } catch (System.Exception ex) { throw new System.Exception("GetMD5HashFromFile() fail, error:" + ex.Message); } } public static void ChangeTextureFormat(Object obj) { Object[] dependObjects; dependObjects = EditorUtility.CollectDependencies(new Object[] { obj }); foreach (Object val in dependObjects) { if (val is Texture2D) { SetTexture(val as Texture2D); } } AssetDatabase.Refresh(); } public static void SetAssetBundlesName(List list, string Extension, bool needRefresh) where T : Object { for (int i = 0; i < list.Count; i++) { SetAssetBundleName(list[i], Extension, needRefresh); } } public static void SetAssetBundleName(T asset, string Extension, bool needRefresh) where T : Object { string path = AssetDatabase.GetAssetPath(asset); SetBundleName(path, Extension, needRefresh); }}