buildAsset分析(三)——BuildAssetBundle
来源:互联网 发布:汉王pdf ocr mac版 编辑:程序博客网 时间:2024/06/07 06:19
当然前面两篇的都是为这篇的代码服务的。BuildAssetBundle.cs这个文件就是主要的打包assetbundle操作了。
首先是这些声明:
//assetbundle资源 static string assetFilePath = "/assetbundles"; static string buildCfgFilePath = Application.dataPath + "/Editor/Build/build.txt"; static string levelCfgFilePath = Application.dataPath + "/Editor/Build/level.txt"; static string buildResourceCfg = Application.dataPath + "/Editor/Build/Resource.txt"; //打包环境设置 static BuildAssetBundleOptions options = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle; static BuildTarget buildPlatform = BuildTarget.StandaloneWindows; //保存所有的scene信息 static List<string> mScenes = new List<string>(); //保存所有Resource信息 static List<string> mResources = new List<string>(); //保存所有的Asset信息 场景+Resource //保存所有要用到的资源,并按依赖数量分好了level //int:代表level //string:文件的路径,包括路径和文件名. //AssetUnit:string文件的各种信息存放在这里面 static Dictionary<int, Dictionary<string, AssetUnit>> allLevelAssets = new Dictionary<int, Dictionary<string, AssetUnit>>();
然后接下来的3个函数不言而喻:
这三个函数都是放到editor中的,
显示到Editor中就是这样子的。
//Andriod打包 [MenuItem("Build/BuildAndriod")] public static void BuildAndriod() { buildPlatform = BuildTarget.Android; BuildResourceFromUnityRule(); } //IOS打包 [MenuItem("Build/BuildIOS")] public static void BuildIOS() { buildPlatform = BuildTarget.iPhone; BuildResourceFromUnityRule(); } //Windows打包 [MenuItem("Build/BuildWindows")] public static void BuildWindows() { buildPlatform = BuildTarget.StandaloneWindows; BuildResourceFromUnityRule(); }
当然最主要的还是BuildResourceFromUnityRule()这个函数:
public static void BuildResourceFromUnityRule() { //清空数据 //assetbundleFilePath:Application.dataPath + "/assetbundles/" if (Directory.Exists(ResourceCommon.assetbundleFilePath)) Directory.Delete(ResourceCommon.assetbundleFilePath, true); //刷新数据 Caching.CleanCache(); AssetDatabase.Refresh(); //获取所有Scene信息 //通过自己定义的levelCfgFilePath目录的文件来获取所有Scene的路径, //然后存到mScenes里面 GetBuildScenes(); //获取所有需要build的文件, //然后保存到mResources中,当然保存的都是路径信息 GetBuildResources(); //get到所有mScenes和mResources中的 //资源的所有依赖,这样所有要用到的资源都get到了, //然后按依赖个数把这些资源分等级。 //就是填充allLevelAssets这个Dictionary GetAllAssets(); //保存Asset资源信息 //这个函数里面生成了所有文件(未打包)的XML信息, //并保存到Assets/assetbundles/AssetInfo.bytes文件中 DumpAssetInfoFile(); //打包asset //打包Assets/Resources下的所有资源(排除了无用资源) //打包到Assets/assetbundles目录下 BuildResource(); //保存资源配置assetbundle //所有打包好的资源的信息统一保存到 //Assets/assetbundles/Resource.bytes文件中(XML格式) DumpResourceFile(); //根据所有assetbundle文件生成版本信息 //对每一个打包好的文件计算MD5值, //并把文件基本信息存成XML文件version.bytes //生成在Assets/assetbundles目录下,然后再复制到Assets/Resources目录下 //这里面也顺便把文件size存到了对应的AssetUnit里面 DumpVersionFile(); //根据生成的assetbundle文件大小填充 assetInfo.byte文件 //读取之前存到AssetUnit里面的的size,然后修改AssetInfo.bytes文件 AddAssetSizeToAssetInfoFile(); AssetDatabase.Refresh(); DebugEx.LogError("BuildResource finish", "BuildResource", true); }
剩下的就是具体实现了,太长了,不过还是一下子贴出来好了:
//获取需要打包Resources目录下所有资源信息 //Assets/Resources目录下所有文件存到mResources里,除了meta文件和Version.bytes文件 [MenuItem("Build/Tools/GetBuildResources")] public static void GetBuildResources() { mResources.Clear(); //Resource资源路径 string resourcePath = Application.dataPath + "/Resources/"; string[] files = Directory.GetFiles(resourcePath, "*.*", SearchOption.AllDirectories); foreach (string file in files) { string suffix = BuildCommon.getFileSuffix(file); if (suffix == "meta") continue; string realFile = file.Replace("\\", "/"); realFile = realFile.Replace(Application.dataPath, "Assets"); if (realFile == "Assets/Resources/Version.bytes") continue; mResources.Add(realFile); } } //获取选中需要打包的Resources资源信息 [MenuItem("Build/Tools/GetSelectedBuildResources")] public static void GetSelectedBuildResources() { mResources.Clear(); //Resource资源路径 //string resourcePath = Application.dataPath + "/Resources/"; //string[] files = Directory.GetFiles(resourcePath, "*.*", SearchOption.AllDirectories); //foreach (string file in files) //{ // string suffix = BuildCommon.getFileSuffix(file); // if (suffix == "meta" || suffix == "cs") // continue; // string realFile = file.Replace("\\", "/"); // realFile = realFile.Replace(Application.dataPath, "Assets"); // mResources.Add(realFile); //} Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); if (selection.Length == 0) return; foreach (Object obj in selection) { string assetPath = AssetDatabase.GetAssetPath(obj); mResources.Add(assetPath); Debug.Log("select:" + assetPath); } } //获取需要打包的Scene目录下的所有资源信息 public static void GetBuildScenes() { mScenes.Clear(); //获取场景路径 FileStream fs = new FileStream(levelCfgFilePath, FileMode.Open); StreamReader sr = new StreamReader(fs); string path = ""; while (path != null) { if (path != "") { mScenes.Add(path); } path = sr.ReadLine(); } sr.Close(); fs.Close(); } [MenuItem("Build/Tools/TestSceneAssets")] public static void TestSceneAssets() { string sceneName = "Assets/Scenes/1.unity"; string[] depends = AssetDatabase.GetDependencies(new string[] { sceneName }); foreach (string str in depends) { Debug.Log("depency: " + str); } } //获取所有的Asset [MenuItem("Build/Tools/GetAllAssets")] public static void GetAllAssets() { //清空操作 allLevelAssets.Clear(); List<string> allAssetPath = new List<string>(); //1添加场景路径 foreach (string scene in mScenes) { allAssetPath.Add(scene); } //2添加Resource资源路径 foreach(string resrPath in mResources) { allAssetPath.Add(resrPath); } //获取场景以及预制件中所有的资源信息 //保存场景文件以及所有的resource文件的依赖 string[] allExportAssets = AssetDatabase.GetDependencies(allAssetPath.ToArray()); //这里allFiles好像声明了并没用过 Dictionary<string, AssetUnit> allFiles = new Dictionary<string, AssetUnit>(); foreach (string p in allExportAssets) { //if (p.Contains(".dll") || p.Contains(".cs")) if (p.Contains(".dll")) continue; AssetUnit unit = new AssetUnit(p); //Asset等级 //level最低是1. int level = unit.mLevel; //存在 if (allLevelAssets.ContainsKey(level)) { allLevelAssets[level].Add(p, unit); } else { //添加等级 Dictionary<string, AssetUnit> levelAsset = new Dictionary<string, AssetUnit>(); allLevelAssets.Add(level,levelAsset); //添加asset信息 allLevelAssets[level].Add(p, unit); } } //创建Asset索引 BuildAssetUnitIndex(); } [MenuItem("Build/Tools/CheckResources")] public static void CheckResources() { AssetDatabase.Refresh(); //获取资源信息 GetBuildScenes(); GetBuildResources(); CheckAssetValid(); Debug.Log("Check finised"); } [MenuItem("Build/Tools/BuildSelected")] public static void BuildSelected() { //清空数据 if (Directory.Exists(ResourceCommon.assetbundleFilePath)) Directory.Delete(ResourceCommon.assetbundleFilePath, true); //刷新数据 Caching.CleanCache(); AssetDatabase.Refresh(); //获取资源信息 GetBuildScenes(); GetSelectedBuildResources(); //获取所有的打包asset GetAllAssets(); //保存Asset资源信息 DumpAssetInfoFile(); //打包asset BuildResource(); //保存资源配置assetbundle DumpResourceFile(); //根据所有assetbundle文件生成版本信息 DumpVersionFile(); //根据生成的assetbundle文件大小填充 assetInfo.byte文件 AddAssetSizeToAssetInfoFile(); AssetDatabase.Refresh(); DebugEx.LogError("BuildResource finish", "BuildResource", true); } public static void BuildResource() { AssetDatabase.Refresh(); //执行依赖性打包 //资源最大等级 int maxLevel = allLevelAssets.Count; if (maxLevel == 0) return; //从最低等级开始打包 for (int level = 1; level <= maxLevel; level++) { //启用以下所有资源的交叉引用, //直到调用PopAssetDependencies为止 //--这算是打包的必要步骤, BuildPipeline.PushAssetDependencies(); //获取不同等级的aaset Dictionary<string, AssetUnit> levelAssets = allLevelAssets[level]; //遍历该等级的所有asset打包 foreach(KeyValuePair<string, AssetUnit> pair in levelAssets) { //根据路径获取asset资源 Object asset = AssetDatabase.LoadMainAssetAtPath(pair.Value.mPath); if (null == asset) DebugEx.LogError("load " + pair.Value.mPath + " failed!!!", "BuildResource", true); //生成打包保存路径 //assetbundleFileSuffix = ".bytes"; string savePath = pair.Value.mPath.Insert(6, assetFilePath) + ResourceCommon.assetbundleFileSuffix; BuildCommon.CheckFolder(BuildCommon.getPath(savePath)); //打包名称去Asset //场景资源(xxx.unity)和普通资源分开 //普通资源 //打包成xxx.bytes文件 //可以到Assets/assetbundles这个目录下去看看 if (pair.Value.mSuffix != "unity")//普通资源 { string assetName = pair.Value.mPath.Replace("Assets/", ""); //资源打包 //这函数过时了 if (!BuildPipeline.BuildAssetBundleExplicitAssetNames( new Object[] { asset }, new string[] { assetName }, savePath, options, buildPlatform)) DebugEx.LogError("build assetbundle " + savePath + " failed!!!", "BuildResource", true); } //场景资源,没有依赖场景的 else { AssetDatabase.Refresh(); BuildPipeline.PushAssetDependencies(); string error = BuildPipeline.BuildStreamedSceneAssetBundle(new string[]{pair.Value.mPath}, savePath, buildPlatform); if (error != "") DebugEx.LogError(error, "BuildResource", true); BuildPipeline.PopAssetDependencies(); Debug.Log("scene path" + pair.Value.mPath); //pair.Value.mPath } } } //popdepency依赖 for (int level = 1; level <= maxLevel; level++) { BuildPipeline.PopAssetDependencies(); } } //创建Asset资源信息 //把所有资源的信息保存到一个xml文件里面 public static void DumpAssetInfoFile() { if(allLevelAssets.Count ==0) return; ////创建所有资源Asset列表 XmlDocument doc = new XmlDocument(); XmlElement root = doc.CreateElement("AllAssets"); //遍历所有Asset数据 for (int level = 1; level <= allLevelAssets.Count; level++) { Dictionary<string, AssetUnit> levelAssets = allLevelAssets[level]; foreach (KeyValuePair<string, AssetUnit> asset in levelAssets) { string assetName = asset.Key; AssetUnit assetUnit = asset.Value; XmlElement ele = doc.CreateElement("Asset"); //设置路径名称 assetName = assetName.Replace("Assets/", ""); ele.SetAttribute("name", assetName); //设置asset索引 ele.SetAttribute("index", assetUnit.mIndex.ToString()); //设置等级 ele.SetAttribute("level", level.ToString()); List<AssetUnit> sortDepencys = new List<AssetUnit>(); //获取AssetUnit所有依赖,并排序 List<string> depencys = assetUnit.mAllDependencies; foreach (string depency in depencys) { AssetUnit depencyUnit = GetAssetUnit(depency); sortDepencys.Add(depencyUnit); } //排序 sortDepencys.Sort(SortAssetUnit); //保存依赖索引 string depencystr = ""; for (int i = 0; i < sortDepencys.Count; i++) { AssetUnit unit = sortDepencys[i]; if (i != sortDepencys.Count - 1) depencystr += unit.mIndex + ","; else depencystr += unit.mIndex; } ele.SetAttribute("depency", depencystr.ToString()); root.AppendChild(ele); } } doc.AppendChild(root); //这是assetbundleFilePath的声明:写在ResourceCommon里面 //public static string assetbundleFilePath = Application.dataPath + "/assetbundles/"; BuildCommon.CheckFolder(BuildCommon.getPath(ResourceCommon.assetbundleFilePath)); doc.Save(ResourceCommon.assetbundleFilePath + "AssetInfo.bytes"); Debug.Log("CreateAssetInfo success!!!"); } //创建资源索引 public static void DumpResourceFile() { ////创建所有资源Asset列表 XmlDocument doc = new XmlDocument(); XmlElement root = doc.CreateElement("AllResources"); XmlElement resource = doc.CreateElement("Resources"); root.AppendChild(resource); foreach (string res in mResources) { string ex = BuildCommon.getFileSuffix(res); string path = res.Replace("Assets/", ""); //去掉后缀 path = path.Replace("." + ex, ""); XmlElement ele = doc.CreateElement("file"); ele.SetAttribute("name", path); ele.SetAttribute("type", ex); resource.AppendChild(ele); } //创建所有需要打包的Level列表 XmlElement sceneRes = doc.CreateElement("Level"); root.AppendChild(sceneRes); foreach (string scene in mScenes) { XmlElement ele = doc.CreateElement("file"); string path = scene.Replace("Assets/", ""); path = path.Replace(".unity", ""); ele.SetAttribute("name", path); ele.SetAttribute("type", "unity"); sceneRes.AppendChild(ele); } doc.AppendChild(root); BuildCommon.CheckFolder(BuildCommon.getPath(ResourceCommon.assetbundleFilePath)); doc.Save(ResourceCommon.assetbundleFilePath + "Resource.bytes"); Debug.Log("CreateResourceCfg success!!!"); } [MenuItem("Build/Tools/DumpVersionFile")] public static void DumpVersionFile() { List<string> allFiles = new List<string>(); BuildCommon.GetFiles(ResourceCommon.assetbundleFilePath, allFiles, true); XmlDocument doc = new XmlDocument(); XmlElement root = doc.CreateElement("Version"); root.SetAttribute("Number", "1.0.0"); root.SetAttribute("Big", "false"); foreach (string element in allFiles) { int size = 0; string md5 = GetFileMD5(element, ref size); XmlElement ele = doc.CreateElement("file"); string relativePath = element.Replace(Application.dataPath + "/assetbundles/", ""); ele.SetAttribute("fpath", relativePath); ele.SetAttribute("size", size.ToString()); ele.SetAttribute("md5", md5); root.AppendChild(ele); //保存大小信息到AssetUnit中 //一开始初始化AssetUnit的时候,size为0, //在这里把size存进AssetUnit string assetName = "Assets/" + relativePath; assetName = assetName.Substring(0, assetName.Length - 6); AssetUnit assetUnit = GetAssetUnit(assetName); if(assetUnit != null) assetUnit.mAssetSize = size; } doc.AppendChild(root); string assetBundleVersionPath = ResourceCommon.assetbundleFilePath + "Version.bytes"; doc.Save(assetBundleVersionPath); //拷贝到对应的Resource目录 string resourceVersionPath = Application.dataPath + "/Resources/" + "Version.bytes"; File.Copy(assetBundleVersionPath, resourceVersionPath, true); Debug.Log("Dump Version Success!!!"); } //添加asset信息 private static void AddAssetSizeToAssetInfoFile() { //添加asset信息 XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(ResourceCommon.assetbundleFilePath + "AssetInfo.bytes"); XmlNodeList xnl = xmlDoc.SelectSingleNode("AllAssets").ChildNodes; for (int i = 0; i < xnl.Count; i++) { XmlElement xe = (XmlElement)xnl.Item(i); string assetName = xe.GetAttribute("name"); //获取对应的AssetUnit AssetUnit unit = GetAssetUnit("Assets/" + assetName); xe.SetAttribute("bundlesize", unit.mAssetSize.ToString()); } xmlDoc.Save(ResourceCommon.assetbundleFilePath + "AssetInfo.bytes"); } //计算文件MD5值,顺便获得文件size //其实我觉得这函数写到BuildCommon里比较好 private static string GetFileMD5(string fpath, ref int size) { FileStream fs = new FileStream(fpath, FileMode.Open); MD5 md5 = MD5.Create(); byte[] vals = md5.ComputeHash(fs); string ret = BitConverter.ToString(vals); ret = ret.Replace("-", ""); size = (int)fs.Length; fs.Close(); return ret; } //按等级顺序创建AssetUnit的索引信息 //给allLevelAssets中的所有level中的所有AssetUnit的mIndex赋上值 //从level0的第一个AssetUnit开始,第一个AssetUnit的index为0,然后后面的AssetUnit依次index++ private static void BuildAssetUnitIndex() { if (allLevelAssets.Count == 0) return; int index = 0; for (int level = 1; level <= allLevelAssets.Count; level++) { Dictionary<string, AssetUnit> levelAssets = allLevelAssets[level]; foreach (KeyValuePair<string, AssetUnit> pair in levelAssets) { AssetUnit unit = pair.Value; unit.mIndex = index; index++; } } } //根据Asset名称获取对应的AssetUnit private static AssetUnit GetAssetUnit(string name) { if (allLevelAssets.Count == 0) return null; for (int level = 1; level <= allLevelAssets.Count; level++) { Dictionary<string, AssetUnit> assetUnit = allLevelAssets[level]; if (assetUnit.ContainsKey(name)) return assetUnit[name]; } return null; } //检查所有的打包资源 private static void CheckAssetValid() { //清空操作 allLevelAssets.Clear(); List<string> allAssetPath = new List<string>(); //1添加场景路径 foreach (string scene in mScenes) { allAssetPath.Add(scene); } //2添加Resource资源路径 foreach(string resrPath in mResources) { allAssetPath.Add(resrPath); } //获取场景以及预制件中所有的资源信息 string[] allExportAssets = AssetDatabase.GetDependencies(allAssetPath.ToArray()); List<string> checkAssets = new List<string>(); foreach (string p in allExportAssets) { if (p.Contains(".dll")) continue; string assetName = Path.GetFileName(p); assetName = assetName.ToLower(); if(checkAssets.Contains(assetName)) { DebugEx.Log("the asset " + assetName + "has already exsited"); } else { checkAssets.Add(assetName); } } } //对AssetUnit进行排序 private static int SortAssetUnit(AssetUnit unit1, AssetUnit unit2) { int res = 0; if (unit1.mLevel > unit2.mLevel) { res = 1; } else if (unit1.mLevel < unit2.mLevel) { res = -1; } return res; }}
太长了,不过文章开头应该能理清大概步骤了,然后后面很长的代码就是具体的实现代码,这样应该会好理解一些。
阅读全文
0 0
- buildAsset分析(三)——BuildAssetBundle
- buildAsset分析(二)——BuildCommon
- buildAsset分析(一)——AssetUnit
- BuildAsset-场景测试
- BuildAssetBundle with .FBX
- BuildPipeline.BuildAssetBundle 编译资源包
- Unity 4.X BuildAssetBundle 策略
- OpenLayers 项目分析——(三)BaseTypes
- OpenLayers 项目分析——(三)BaseTypes (续)
- OpenLayers 项目分析——(三)BaseTypes
- 加密算法(三)——【MD5算法实现过程分析】
- GCC源码分析(三)——中间语言
- 软工总结(三)——需求分析
- 设计一门脚本语言——(三)词法分析
- Spice代码分析(三)——red_worker: red_init
- Cordova CLI源码分析(三)——初始化
- Spice代码分析(三)——red_worker: red_init
- Giraph源码分析(三)—— 消息通信
- Appium基础篇3-第一个appium自动化脚本之自动安装apk包到手机
- sql server 获取最后一条插入的记录的主键
- ANSI,ASCII,Unicode的区别与联系
- 1027. Colors in Mars (20)
- datacharts
- buildAsset分析(三)——BuildAssetBundle
- 2018年搜狐秋季校招校招大数据研发笔试编程题—Kolakoski序列
- 抓包神器Charles使用教程(二) 主要抓包调试功能操作
- 《OC基础教程》读书笔记5-创建简单的用户界面
- 美团二面,后台开发
- Android中HandlerThread面试相关知识点
- 解析Java为什么不接受合法的HTTPS证书
- 干货 | TensorFlow的55个经典案例
- JavaNote2