Unity热更新系列之AssetBundle(2) ——详述如何打AssetBundle资源
2018-02-01 10:24
706 查看
Unity热更新系列之AssetBundle(2) ——详述如何打AssetBundle资源
前一段时间,写了篇笔记,详细的讲了如何设置资源的Assetbundle 名称,和其中的区别。最主要的区别其实就在于项目中要不要处理资源之间的依赖。
详述如何设置Assetbundle Name
在设置好资源名称后,输出资源的过程并不复杂。只需要调用内置的API
BuildPipeline.BuildAssetBundles(outputPath, BuildAssetBundleOptions, BuildTarget);
就可以输出已经设置好名称的资源。
具体的对应关系,在上面的文章中已经介绍过了。
现在介绍一下,如果我们想要做UI热更新时,打资源的流程。
该方法中,我们不处理资源间的依赖,而是使用另一种方式来自己记录并处理依赖。
模拟真实的手游项目中的MVC模式,每一个UI面板都会有一个相应的UI面板类,这些面板类继承同一个基类并会统一到一个面板管理器类中,方便游戏其他逻辑访问和调用。
我们新建一个场景,在场景中导入NGUI插件,并随手拉一个界面出来。
并定义出相应的脚本,其中各个面板的基类PadBase定义如下:
public class PadBase : MonoBehaviour { public List<UIAtlas> allAtlas; public List<UIFont> allFonts; protected virtual void Awake() { } public virtual void Start () { } public virtual void OnEnable() { } public virtual void OnDisable() { } public virtual void Update() { } }
这个脚本中除了定义了常用生命周期的虚函数外,最重要的就是定义了变量allAtlas和allFonts。
这是因为在UI热更新中,每一个UI上面最重要的资源可以分为三部分分别打成AssetBundle,分别是面板的预制体本身、面板所引用的图集和面板所引用的字体。
之所以这样划分,是因为对于一个游戏中,所有的面板所引用的图集和字体是相互耦合的,如果我们将每一个面板上引用到的所有资源
如果当然我们可以按照上一篇文章中说的方法,将他们分开打包,然后加载的时候使用Manifest文件处理相互之间的依赖关系。
但是这里我们需要用自己的方法来处理UI上的依赖,因为在游戏运行过程中,内存占用过高的元凶,和造成性能瓶颈的元凶,往往都是过多的UI图集资源。如果使用Manifest处理依赖,U3D会自动的将该面板所引用的资源长期占用在内存中,不方便我们进行内存的管理。
用这种方法,我们可以自PadBase类中,每一个面板执行OnDisable时,自己来处理是否要释放内存资源。
这样在其每一个子类中,都会继承相应的变量,用来在热更新UI时,记录面板上的引用信息。
这样做的原理就是,当一个预制体上面挂了脚本,并且该脚本上有一些Pulic 变量在预制体上赋了值,那么该变量的值就会记录在预制体上,并随之打入AssetBundle资源。
我们在打资源时,预制体上通过这样的方式储存了引用的图集和字体信息,真正打资源时,需要将图集和字体置为空,这样打出的资源里面虽然讲面板引用的所有的图集和字体都打在了一起,但其实只打进去了图集和字体的预制体,真正占用内存最大的资源是图集上引用的Texture和字体预制体上引用的字体库
举个例子,我们测试的面板中引用了两个图集Fantasy Atlas和Wooden Atlas
如果我们不做任何操作,直接给Pad_Test预制体设置一个AssetBundle Name,打出资源如下
现在我们将引用的图集、字体上的预制体都置空
打出的资源结果如下:
这里我们发现,第一次面板总资源大小为596KB,第二次,面板总资源大小为7KB。
这两次资源主要就差在,第一个资源将面板上引用到的图集和字体的预制体、材质球、材质球引用到的Texture、字体库全都打进了一个AssetBundle。
而第二次资源相比第一次资源只是少了Texture和字体库。
经过比较,可以得到,在面板引用的资源中,图集和字体的预制体本身并不大,全都打在一起也才7KB,在项目中是可以接受的。相比之下Texture和字体库才是占用资源大小的最大元凶。
所以我们需要整理一下UI热更时打资源的流程
之所以分了这么多琐碎的步骤,最主要是因为需要将图集和字体先整体打一遍,这时候是要带着Texture和字体库的,打完之后需要将图集和字体上的材质引用置空,然后将其和面板预制体打在一起(测试面板只有7KB)。由于我们自己处理了依赖关系,所以使用不到Manifest文件。
这样打出的资源,我们可以在游戏运行时,动态加载面板的预制体资源,加载完后,通过该预制体上记录的变量信息(图集和字体的名字),再去加载它引用到的图集和字体,并使用
Atlas.replacement = AtlasLoadFromAssetBundle
将面板类上的变量
List<UIAtlas> allAtlas;中的图集全都替换掉就可以了。
但是这样做虽然可以做到面板内容的更新,但是每次出包打资源的步骤太过繁琐。所以我们就需要写个插件,来帮助我们打UI资源。
using UnityEngine; using System.Collections; using UnityEditor; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using System.Text; public class ExportManager : MonoBehaviour { //记录图集和材质的对应关系,用来置空或重新赋值图集材质 private static Dictionary<UIAtlas, Material> atlasMaterialDic = new Dictionary<UIAtlas, Material>(); //记录字体和材质的对应关系,用来置空或重新赋值字体材质 private static Dictionary<UIFont, Font> fontMaterialDic = new Dictionary<UIFont, Font>(); //所有的面板类 private static List<PadBase> allPadBase = new List<PadBase>(); [MenuItem("资源打包/打开操作面板")] static void OpenConfigWindow() { var wizard = EditorWindow.GetWindow(typeof(ExportWindow)) as ExportWindow; wizard.minSize = new Vector2(300, 300); } /// <summary> /// 收集所有面板上所使用的图集,为每一个PadBase的AllAtlas赋值 /// </summary> static public void CollectAllUsedAtlasFont() { atlasMaterialDic.Clear(); fontMaterialDic.Clear(); allPadBase.Clear(); //找到所有的PadBase string[] filePaths = Directory.GetFiles(Application.dataPath + "/Panel/PadPrefabs", "*.prefab", SearchOption.AllDirectories); for (int i = 0; i < filePaths.Length; i++) { FileInfo fi = new FileInfo(filePaths[i]); string path = "Assets" + filePaths[i].Replace(Application.dataPath, ""); Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); GameObject go = obj as GameObject; PadBase pb = go.GetComponent<PadBase>(); if (go != null && pb != null) { if (!allPadBase.Contains(pb)) { allPadBase.Add(pb); } } if (EditorUtility.DisplayCancelableProgressBar("收集所有PadBase:", filePaths[i], (float)i / (float)filePaths.Length)) { break; } } //收集每一个面板上的图集和字体信息 for (int i = 0; i < allPadBase.Count; i++) { CollectAtlasInChildren(allPadBase[i].transform, allPadBase[i]); if (PrefabUtility.GetPrefabParent(allPadBase[i]) != null) { PrefabUtility.ReplacePrefab(allPadBase[i].gameObject, PrefabUtility.GetPrefabParent(allPadBase[i]), ReplacePrefabOptions.ConnectToPrefab); } if (EditorUtility.DisplayCancelableProgressBar("收集所有图集信息:", allPadBase[i].name, (float)i / (float)allPadBase.Count)) { break; } } EditorUtility.ClearProgressBar(); } static private void CollectAtlasInChildren(Transform tf, PadBase padBase) { List<UIAtlas> atlasList = new List<UIAtlas>(); List<UIFont> fontsList = new List<UIFont>(); //收集面板引用的所有图集 UISprite[] spriteArr = tf.GetComponentsInChildren<UISprite>(true); for (int i = 0; i < spriteArr.Length; i++) { if (spriteArr[i].atlas && !atlasList.Contains(spriteArr[i].atlas)) { atlasList.Add(spriteArr[i].atlas); if (!atlasMaterialDic.ContainsKey(spriteArr[i].atlas)) { atlasMaterialDic.Add(spriteArr[i].atlas, spriteArr[i].atlas.spriteMaterial); } } } //收集面板引用的所有字体 UILabel[] fontArr = tf.GetComponentsInChildren<UILabel>(true); for (int i = 0; i < fontArr.Length; i++) { if (fontArr[i].bitmapFont && !fontsList.Contains(fontArr[i].bitmapFont)) { fontsList.Add(fontArr[i].bitmapFont); if (!fontMaterialDic.ContainsKey(fontArr[i].bitmapFont)) { fontMaterialDic.Add(fontArr[i].bitmapFont, fontArr[i].bitmapFont.dynamicFont); } } } //顺便在这里清理所有的Texture UITexture[] texArr = tf.GetComponentsInChildren<UITexture>(true); for (int i = 0; i < texArr.Length; i++) { texArr[i].mainTexture = null; texArr[i].material = null; } //删除引用的其他字体 GUIText[] guiText = tf.GetComponentsInChildren<GUIText>(true); for (int i = 0; i < guiText.Length; i++) { Destroy(guiText[i]); } padBase.allAtlas = atlasList; padBase.allFonts = fontsList; } /// <summary> /// 将所有的图集上面引用到的材质置为空 /// </summary> public static void SetAllAtlasFontMaterialNull() { if (allPadBase == null || allPadBase.Count <= 0) { Debug.LogError("Please Collect All Used Atlas first !"); return; } PadBase[] allObj1 = allPadBase.ToArray(); //遍历所有的面板 for (int i = 0; i < allObj1.Length; i++) { //遍历面板引用图集 for (int j = 0; j < allObj1[i].allAtlas.Count; j++) { if (allObj1[i].allAtlas[j] != null) { if (!atlasMaterialDic.ContainsKey(allObj1[i].allAtlas[j])) { atlasMaterialDic.Add(allObj1[i].allAtlas[j], allObj1[i].allAtlas[j].spriteMaterial); } allObj1[i].allAtlas[j].spriteMaterial = null; } } //遍历面板引用字体 for (int j = 0; j < allObj1[i].allFonts.Count; j++) { if (allObj1[i].allFonts[j] != null) { //动态字体 if (!fontMaterialDic.ContainsKey(allObj1[i].allFonts[j])) { fontMaterialDic.Add(allObj1[i].allFonts[j], allObj1[i].allFonts[j].dynamicFont); } allObj1[i].allFonts[j].dynamicFont = null; //Bitmap字体 if (allObj1[i].allFonts[j].atlas != null) { if (!atlasMaterialDic.ContainsKey(allObj1[i].allFonts[j].atlas)) { atlasMaterialDic.Add(allObj1[i].allFonts[j].atlas, allObj1[i].allFonts[j].atlas.spriteMaterial); } allObj1[i].allFonts[j].atlas.spriteMaterial = null; } } } if (EditorUtility.DisplayCancelableProgressBar("图集字体置空:", allObj1[i].name, (float)i / (float)allObj1.Length)) { break; } } EditorUtility.ClearProgressBar(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } /// <summary> /// 将所有的图集上面引用到的材质重新赋值 /// </summary> public static void SetAllAtlasMaterialBack() { if (atlasMaterialDic == null || atlasMaterialDic.Count <= 0) { Debug.LogError("Please Collect All Used Atlas first !"); return; } if (fontMaterialDic == null || fontMaterialDic.Count <= 0) { Debug.LogError("Please Collect All Used Atlas first !"); return; } //置回图集材质 foreach (var item in atlasMaterialDic) { item.Key.spriteMaterial = item.Value; } //置回字体材质 foreach (var item in fontMaterialDic) { item.Key.dynamicFont = item.Value; } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } /// <summary> /// 设置输出平台 /// </summary> private static BuildTarget target { get { if (ExportWindow.isPC) { return BuildTarget.StandaloneWindows; } else if (ExportWindow.isAndriod) { return BuildTarget.Android; } else if (ExportWindow.isIOS) { return BuildTarget.iOS; } else { return BuildTarget.StandaloneWindows; } } } /// <summary> /// 设置AssetBundle名称和输出路径下的子文件夹 /// </summary> /// <param name="folderPath"></param> public static void SetAssetBundleName(string folderPath) { //搜集图集和面板预制体 string[] filePaths = Directory.GetFiles(folderPath, "*.prefab", SearchOption.AllDirectories); //搜集图片 string[] texPaths = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories).Where(t => t.EndsWith(".png") || t.EndsWith(".jpg")).ToArray(); List<string> searchPaths = new List<string>(); searchPaths.AddRange(filePaths); searchPaths.AddRange(texPaths); //遍历设置AssetBundle名称 for (int i = 0; i < searchPaths.Count; i++) { FileInfo fi = new FileInfo(searchPaths[i]); string path = "Assets" + searchPaths[i].Replace(Application.dataPath, ""); Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); if (obj != null) { //判断是不是动态Texture if (searchPaths[i].Contains("DynamicTexture")) { AssetImporter.GetAtPath(path).assetBundleName = GetAssetBundleType(path) + "/" + obj.name.Replace(" ", "_") + ".unity3d"; continue; } //判断是不是图集、面板或者字体 try { GameObject go = obj as GameObject; if (go != null) { if (go.GetComponent<UIAtlas>() != null || go.GetComponent<PadBase>() != null || go.GetComponent<UIFont>() != null) { AssetImporter.GetAtPath(path).assetBundleName = GetAssetBundleType(path) + "/" + obj.name.Replace(" ", "_") + Path.GetExtension(path) + ".unity3d"; } } } catch (System.Exception) { throw; } } if (EditorUtility.DisplayCancelableProgressBar("正在设置AssetBundle名:", obj.name, (float)i / (float)filePaths.Length)) { break; } } EditorUtility.ClearProgressBar(); } private static string GetAssetBundleType(string path) { string str = string.Empty; if (path.ToLower().Contains("atlas")) { str = "atlas"; } if (path.ToLower().Contains("shader")) { str = "shader"; } if (path.ToLower().Contains("prefabs")) { str = "panel"; } if (path.ToLower().Contains("texture")) { str = "texture"; } if (path.ToLower().Contains("font")) { str = "font"; } //todo: add other folder return str; } [MenuItem("Assets/Set Select AssetBundle Name")] public static void SetSelectAssetBundleName() { foreach (var item in Selection.objects) { string path = AssetDatabase.GetAssetPath(item); AssetImporter.GetAtPath(path).assetBundleName = GetAssetBundleType(path) + "/" + item.name.Replace(" ", "_") + Path.GetExtension(path) + ".unity3d"; } } /// <summary> /// 设置AssetBundle名称和输出路径下的子文件夹 /// </summary> /// <param name="removePath">清理路径,没有则全清</param> public static void RemoveAllAssetBundleName(string removePath = null) { if (string.IsNullOrEmpty(removePath)) { removePath = Application.dataPath; } string allFixs = "*.prefab|*.mp3|*.mat|*.shader|*.FBX|*.png|*.jpg|*.controller"; string[] files = System.IO.Directory.GetFiles(removePath, "*.*", SearchOption.AllDirectories).Where((t) => { string[] arr = t.Split('.'); string fix = arr[arr.Length - 1]; if (allFixs.Contains(fix)) { return true; } else { return false; } }).ToArray(); if ((files != null) && (files.Length > 0)) { for (int i = 0; i < files.Length; ++i) { string localPath = "Assets" + files[i].Replace(Application.dataPath, ""); if (!string.IsNullOrEmpty(localPath)) { localPath = localPath.ToLower(); } //有些资源是无法当做AssetBundle的,比如.cs类型的脚本 //当处理这些时这里会报错,直接省略掉 if (localPath.Contains(".cs")) { continue; } AssetImporter importer = AssetImporter.GetAtPath(localPath); if (importer != null && importer.assetBundleName != null) { importer.assetBundleName = string.Empty; } if (EditorUtility.DisplayCancelableProgressBar("清理AssetBundleName:", files[i], (float)i / (float)files.Length)) { break; } } EditorUtility.ClearProgressBar(); } AssetDatabase.RemoveUnusedAssetBundleNames(); } /// <summary> /// 一键出资源 /// </summary> /// <param name="outputPath"></param> public static void ExportResourceAtuo(string atlasPath, string panelPath, string texturePath, string fontPath, string outputPath) { //收集图集、字体 CollectAllUsedAtlasFont(); //打图集、字体 RemoveAllAssetBundleName(panelPath); SetAssetBundleName(atlasPath); SetAssetBundleName(fontPath); ExportResource(outputPath, true); //材质置空 SetAllAtlasFontMaterialNull(); //打预制体、Texture RemoveAllAssetBundleName(atlasPath); RemoveAllAssetBundleName(fontPath); SetAssetBundleName(panelPath); SetAssetBundleName(texturePath); ExportResource(outputPath, true); //图集置回 SetAllAtlasMaterialBack(); outputPath += "/" + GetAssetBundleFolderName(target); if (EditorUtility.DisplayDialog("资源导出完成", "是否删除Manifest文件?", "确定", "取消")) { DeleteManifest(outputPath); } System.Diagnostics.Process.Start(outputPath, outputPath); } /// <summary> /// 删除指定路径下的Manifest文件 /// </summary> /// <param name="path"></param> public static void DeleteManifest(string path) { string[] allPaths = System.IO.Directory.GetFiles(path, "*.manifest", SearchOption.AllDirectories); for (int i = 0; i < allPaths.Length; i++) { File.Delete(allPaths[i]); if (EditorUtility.DisplayCancelableProgressBar("正在删除Manifest:", allPaths[i], (float)i / (float)allPaths.Length)) { break; } } EditorUtility.ClearProgressBar(); } /// <summary> /// 输出AssetBundle资源 /// </summary> /// <param name="outputPath"></param> public static void ExportResource(string outputPath, bool isAuto = false) { outputPath += "/" + GetAssetBundleFolderName(target); if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath); Debug.Log("输出路径" + outputPath); BuildPipeline.BuildAssetBundles(outputPath, BuildAssetBundleOptions.None, target); if (!isAuto) { System.Diagnostics.Process.Start(outputPath, outputPath); } } private static string GetAssetBundleFolderName(BuildTarget target) { string str = string.Empty; switch (target) { case BuildTarget.StandaloneWindows: str = "assetbundles"; break; case BuildTarget.iOS: str = "assetbundles_iphone"; break; case BuildTarget.Android: str = "assetbundles_android"; break; default: break; } return str; } }
插件需要的核心逻辑就这么多,接下来我们需要写一个相应的操作面板
using UnityEngine; using System.Collections; using UnityEditor; public class ExportWindow : EditorWindow { /// <summary> /// 打包目标平台 /// </summary> public static BuildTarget planform = BuildTarget.StandaloneWindows; /// <summary> /// 图集资源文件夹 /// </summary> public static string atlasFolderPath = Application.dataPath + "/Panel/Atlas"; /// <summary> /// 面板预制体资源文件夹 /// </summary> public static string panelFolderPath = Application.dataPath + "/Panel/PadPrefabs"; /// <summary> /// 动态Texture资源文件夹 /// </summary> public static string textureFolderPath = Application.dataPath + "/Panel/DynamicTexture"; /// <summary> /// 字体资源文件夹 /// </summary> public static string fontFolderPath = Application.dataPath + "/Panel/Font"; /// <summary> /// AssetBundle输出文件夹 /// </summary> public static string outPutBundlePath = Application.dataPath.Replace("/Assets", ""); /// <summary> ///Apk输出文件夹 /// </summary> public static string outPutApkPath = Application.dataPath; /// <summary> ///游戏大版本 /// </summary> public static string bigVersion = "2.0"; public static bool isPC = false; public static bool isAndriod = false; public static bool isIOS = false; void OnGUI() { EditorGUIUtility.labelWidth = 300; EditorGUIUtility.fieldWidth = 300; //选择平台 GUILayout.BeginHorizontal(); GUILayout.Label("目标平台:", GUILayout.Width(120f)); //PC if (isPC != GUILayout.Toggle(isPC, "PC")) { isPC = !isPC; isAndriod = !isPC; isIOS = !isPC; } //Android if (isAndriod != GUILayout.Toggle(isAndriod, "Andriod")) { isAndriod = !isAndriod; isPC = !isAndriod; isIOS = !isAndriod; } //IOS if (isIOS != GUILayout.Toggle(isIOS, "IOS")) { isIOS = !isIOS; isPC = !isIOS; isAndriod = !isIOS; } GUILayout.EndHorizontal(); //收集所有面板上使用到的图集、字体 GUILayout.BeginHorizontal(); if (GUILayout.Button("Collect All Used Atlas")) { ExportManager.CollectAllUsedAtlasFont(); } GUILayout.EndHorizontal(); //将所有面板上使用到的材质置为空 GUILayout.BeginHorizontal(); if (GUILayout.Button("Set All Atlas Material Null")) { ExportManager.SetAllAtlasFontMaterialNull(); } //将所有面板上使用到的图集重新赋值 if (GUILayout.Button("Set All Atlas Material Back")) { ExportManager.SetAllAtlasMaterialBack(); } GUILayout.EndHorizontal(); //清除项目中的AssetBindleName GUILayout.BeginHorizontal(); if (GUILayout.Button("Remove All AssetBundle Name")) { ExportManager.RemoveAllAssetBundleName(); } GUILayout.EndHorizontal(); //删除所有的Manifset文件 GUILayout.BeginHorizontal(); if (GUILayout.Button("Delete All Manifest")) { ExportManager.DeleteManifest(outPutBundlePath); } GUILayout.EndHorizontal(); //选择图集所在路径 GUILayout.BeginHorizontal(); GUILayout.Label("选择图集文件夹:", GUILayout.Width(100)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); atlasFolderPath = GUILayout.TextField(atlasFolderPath); if (GUILayout.Button("Input Folder", GUILayout.Width(100))) { string newAtlasFolderPath = EditorUtility.OpenFolderPanel("Select Folder", atlasFolderPath, ""); if (!string.IsNullOrEmpty(newAtlasFolderPath)) { atlasFolderPath = newAtlasFolderPath; Repaint(); } } GUILayout.EndHorizontal(); //设置所选文件夹下所有的图集预制体的AssetBundle名 GUILayout.BeginHorizontal(); if (GUILayout.Button("Set AssetBundle Name")) { if (string.IsNullOrEmpty(atlasFolderPath)) { Debug.LogError("Path is Null !!!"); return; } ExportManager.SetAssetBundleName(atlasFolderPath); } GUILayout.EndHorizontal(); //输出AssetBundle资源 GUILayout.BeginHorizontal(); GUILayout.Label("选择输出文件夹:", GUILayout.Width(100)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); outPutBundlePath = GUILayout.TextField(outPutBundlePath); if (GUILayout.Button("Output Folder", GUILayout.Width(100))) { string newOutputFolderPath = EditorUtility.OpenFolderPanel("Select Folder", outPutBundlePath, ""); if (!string.IsNullOrEmpty(newOutputFolderPath)) { outPutBundlePath = newOutputFolderPath; Repaint(); } } GUILayout.EndHorizontal(); //导出所有资源 GUILayout.BeginHorizontal(); if (GUILayout.Button("Export AssetBundle")) { if (string.IsNullOrEmpty(outPutBundlePath)) { Debug.LogError("Path is Null !!!"); return; } ExportManager.ExportResource(outPutBundlePath); } GUILayout.EndHorizontal(); //一键打资源 //选择图集文件夹 GUILayout.BeginHorizontal(); GUILayout.Label("选择图集文件夹:", GUILayout.Width(100)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); atlasFolderPath = GUILayout.TextField(atlasFolderPath); if (GUILayout.Button("Select Folder", GUILayout.Width(100))) { string newOutputFolderPath = EditorUtility.OpenFolderPanel("Select Folder", atlasFolderPath, ""); if (!string.IsNullOrEmpty(newOutputFolderPath)) { atlasFolderPath = newOutputFolderPath; Repaint(); } } GUILayout.EndHorizontal(); //选择面板预制体文件夹 GUILayout.BeginHorizontal(); GUILayout.Label("选择面板预制体文件夹:", GUILayout.Width(100)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); panelFolderPath = GUILayout.TextField(panelFolderPath); if (GUILayout.Button("Select Folder", GUILayout.Width(100))) { string newOutputFolderPath = EditorUtility.OpenFolderPanel("Select Folder", panelFolderPath, ""); if (!string.IsNullOrEmpty(newOutputFolderPath)) { panelFolderPath = newOutputFolderPath; Repaint(); } } GUILayout.EndHorizontal(); //选择动态Texture文件夹 GUILayout.BeginHorizontal(); GUILayout.Label("选择动态Texture文件夹:", GUILayout.Width(100)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); textureFolderPath = GUILayout.TextField(textureFolderPath); if (GUILayout.Button("Select Folder", GUILayout.Width(100))) { string newOutputFolderPath = EditorUtility.OpenFolderPanel("Select Folder", textureFolderPath, ""); if (!string.IsNullOrEmpty(newOutputFolderPath)) { textureFolderPath = newOutputFolderPath; Repaint(); } } GUILayout.EndHorizontal(); //选择字体文件夹 GUILayout.BeginHorizontal(); GUILayout.Label("选择字体文件夹:", GUILayout.Width(100)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); fontFolderPath = GUILayout.TextField(fontFolderPath); if (GUILayout.Button("Select Folder", GUILayout.Width(100))) { string newOutputFolderPath = EditorUtility.OpenFolderPanel("Select Folder", fontFolderPath, ""); if (!string.IsNullOrEmpty(newOutputFolderPath)) { fontFolderPath = newOutputFolderPath; Repaint(); } } GUILayout.EndHorizontal(); //一键打资源 GUILayout.BeginHorizontal(); if (GUILayout.Button("一键打资源")) { if (string.IsNullOrEmpty(outPutBundlePath)) { Debug.LogError("Path is Null !!!"); return; } ExportManager.ExportResourceAtuo(atlasFolderPath, panelFolderPath, textureFolderPath, fontFolderPath, outPutBundlePath); } GUILayout.EndHorizontal(); } public enum AssetBundleType { normal, Atlas, } }
运行如下:
测试项目的结构目录如下:
这里面的资源路径,需要根据项目的目录不同而进行具体修改。
为了兼顾灵活性,每一个功能都给出了单独的操作按钮。因为项目大的时候,全部清理一次AssetBundle Name的时间很长,所以该功能没有集成在一键出资源里面。
输出资源结构路径如下:
这个结构路径会影响到后面的资源加载逻辑。
所有资源的信息如下:
相关文章推荐
- Unity热更新系列之AssetBundle(1) ——详述如何设置AssetBundle Name
- Unity资源打包学习笔记(二)、如何实现高效的unity AssetBundle热更新
- [置顶] [Unity]AssetBundle资源更新以及多线程下载
- Unity AssetBundle打包与资源更新
- Unity热更新系列之AssetBundle(3) ——资源文件 xml 清单的生成与读取
- Unity5系列资源管理AssetBundle——更新实现
- Unity热更新专题(五)如何打包AssetBundle
- Unity AssetBundle打包与资源更新
- Unity5 AssetBundle系列——资源加载卸载以及AssetBundleManifest的使用
- Unity热更新专题(五)如何打包AssetBundle
- [Unity Asset]AssetBundle系列——游戏资源打包
- Unity AssetBundle打包与资源更新
- [Unity]AssetBundle资源更新以及多线程下载
- unity开发-记录项目目录管理及作用 Assetbundle 存放及资源更新
- Unity3D 资源热更新之AssetBundle(Unity5.x)基础用法
- Unity5.x ScriptableObject数据存储结合AssetBundle打包实现数据资源热更新
- Unity资源打包之Assetbundle
- Unity资源处理机制(Assets/WWW/AssetBundle/...)读取和加载资源方式详解
- Unity3d热更新全书-加载(二)如何在不用AssetBundle的前提下动态加载预设
- AssetBundle系列——场景资源之解包(二)