/// <summary> /// 打包资源 /// </summary> public void BuildAssetBundle(string outputPath, RuntimePlatform platform) { //ab会先构建代码,提前构建,避免浪费时间 var ret = PublishPipeLineCI.CheckCode(); if (ret) { //生成Assetbundlebunle AssetBundleEditorToolsV2.GenAssetBundle(outputPath, platform); AssetDatabase.Refresh(); Debug.Log("资源打包完毕"); } }
/// <summary> /// 加载所有assetbundle /// </summary> /// <returns></returns> static IEnumerator IE_LoadAll() { var outpath = BApplication.BDEditorCachePath + "/AssetBundle"; if (!Directory.Exists(outpath)) { Directory.CreateDirectory(outpath); } loadDataMap.Clear(); //加载 var allRuntimeAssets = BApplication.GetAllRuntimeAssetsPath(); foreach (var asset in allRuntimeAssets) { var type = AssetBundleEditorToolsV2.GetMainAssetTypeAtPath(asset); if (type == null) { Debug.LogError("无法获得资源类型:" + asset); continue; } var idx = asset.IndexOf(AssetBundleBuildingContext.RUNTIME_PATH, StringComparison.OrdinalIgnoreCase); var runtimePath = asset.Substring(idx + AssetBundleBuildingContext.RUNTIME_PATH.Length); runtimePath = runtimePath.Replace(Path.GetExtension(runtimePath), ""); runtimePath = runtimePath.Replace("\\", "/"); //Debug.Log("【LoadTest】:" + runtimePath); List <LoadTimeData> loadList = null; if (!loadDataMap.TryGetValue(type.FullName, out loadList)) { loadList = new List <LoadTimeData>(); loadDataMap[type.FullName] = loadList; } var loadData = new LoadTimeData(); loadData.LoadPath = runtimePath; loadList.Add(loadData); //计时器 Stopwatch sw = new Stopwatch(); if (type == typeof(GameObject)) { //加载 sw.Start(); var obj = AssetBundleLoader.Load <GameObject>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; //实例化 if (obj != null) { sw.Restart(); var gobj = GameObject.Instantiate(obj); sw.Stop(); loadData.InstanceTime = sw.ElapsedTicks; //UI var rectTransform = gobj.GetComponentInChildren <RectTransform>(); if (rectTransform != null) { gobj.transform.SetParent(UI_ROOT, false); } else { gobj.transform.SetParent(SCENE_ROOT); } //抓屏 保存 var outpng = string.Format("{0}/{1}_ab.png", outpath, runtimePath.Replace("/", "_")); yield return(null); //渲染 GameView.Repaint(); GameView.Focus(); yield return(null); //抓屏 //TODO 这里有时候能抓到 有时候抓不到 ScreenCapture.CaptureScreenshot(outpng); //删除 GameObject.DestroyImmediate(gobj); } else { UnityEngine.Debug.LogError("【Prefab】加载失败:" + runtimePath); } } else if (type == typeof(TextAsset)) { //测试打印AssetText资源 sw.Start(); var textAsset = AssetBundleLoader.Load <TextAsset>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!textAsset) { UnityEngine.Debug.LogError("【TextAsset】加载失败:" + runtimePath); } else { UnityEngine.Debug.Log(textAsset.text); } } else if (type == typeof(Texture)) { sw.Start(); var tex = AssetBundleLoader.Load <Texture>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!tex) { UnityEngine.Debug.LogError("【Texture】加载失败:" + runtimePath); } break; } else if (type == typeof(Texture2D)) { sw.Start(); var tex = AssetBundleLoader.Load <Texture2D>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!tex) { UnityEngine.Debug.LogError("【Texture2D】加载失败:" + runtimePath); } } else if (type == typeof(Sprite)) { sw.Start(); var sp = AssetBundleLoader.Load <Sprite>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!sp) { UnityEngine.Debug.LogError("【Sprite】加载失败:" + runtimePath); } } else if (type == typeof(Material)) { sw.Start(); var mat = AssetBundleLoader.Load <Material>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!mat) { UnityEngine.Debug.LogError("【Material】加载失败:" + runtimePath); } } else if (type == typeof(Shader)) { sw.Start(); var shader = AssetBundleLoader.Load <Shader>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!shader) { UnityEngine.Debug.LogError("【Shader】加载失败:" + runtimePath); } } else if (type == typeof(AudioClip)) { sw.Start(); var ac = AssetBundleLoader.Load <AudioClip>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!ac) { UnityEngine.Debug.LogError("【AudioClip】加载失败:" + runtimePath); } } else if (type == typeof(AnimationClip)) { sw.Start(); var anic = AssetBundleLoader.Load <AnimationClip>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!anic) { UnityEngine.Debug.LogError("【AnimationClip】加载失败:" + runtimePath); } } else if (type == typeof(Mesh)) { sw.Start(); var mesh = AssetBundleLoader.Load <Mesh>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!mesh) { UnityEngine.Debug.LogError("【Mesh】加载失败:" + runtimePath); } } else if (type == typeof(Font)) { sw.Start(); var font = AssetBundleLoader.Load <Font>(runtimePath); sw.Stop(); loadData.LoadTime = sw.ElapsedTicks; if (!font) { UnityEngine.Debug.LogError("【Font】加载失败:" + runtimePath); } } else if (type == typeof(SpriteAtlas)) { sw.Start(); var sa = AssetBundleLoader.Load <SpriteAtlas>(runtimePath); sw.Stop(); if (!sa) { UnityEngine.Debug.LogError("【SpriteAtlas】加载失败:" + runtimePath); } loadData.LoadTime = sw.ElapsedTicks; } else if (type == typeof(ShaderVariantCollection)) { sw.Start(); var svc = AssetBundleLoader.Load <ShaderVariantCollection>(runtimePath); svc?.WarmUp(); sw.Stop(); if (!svc) { UnityEngine.Debug.LogError("【ShaderVariantCollection】加载失败:" + runtimePath); } loadData.LoadTime = sw.ElapsedTicks; } else if (type == typeof(AnimatorController)) { sw.Start(); var aniCtrl = AssetBundleLoader.Load <AnimatorController>(runtimePath); sw.Stop(); if (!aniCtrl) { UnityEngine.Debug.LogError("【AnimatorController】加载失败:" + runtimePath); } loadData.LoadTime = sw.ElapsedTicks; } else { sw.Start(); var gobj = AssetBundleLoader.Load <Object>(runtimePath); sw.Stop(); if (!gobj) { UnityEngine.Debug.LogError("【Object】加载失败:" + runtimePath); } UnityEngine.Debug.LogError("待编写测试! -" + type.FullName); } //打印 Debug.LogFormat("<color=yellow>{0}</color> <color=green>【加载】:<color=yellow>{1}ms</color>;【初始化】:<color=yellow>{2}ms</color> </color>", loadData.LoadPath, loadData.LoadTime / 10000f, loadData.InstanceTime / 10000f); yield return(null); } yield return(null); foreach (var item in loadDataMap) { Debug.Log("<color=red>【" + item.Key + "】</color>"); foreach (var ld in item.Value) { Debug.LogFormat( "<color=yellow>{0}</color> <color=green>【加载】:<color=yellow>{1}ms</color>;【初始化】:<color=yellow>{2}ms</color> </color>", ld.LoadPath, ld.LoadTime / 10000f, ld.InstanceTime / 10000f); } } yield return(null); EditorUtility.RevealInFinder(outpath); }
/// <summary> /// 开始构建AB /// </summary> public void StartBuildAssetBundle(BuildTarget buildTarget) { //-----------------------开始打包AssetBundle逻辑--------------------------- Debug.Log("【BuildAssetbundle】执行Build..."); //设置编辑器状态 BDEditorApplication.EditorStatus = BDFrameworkEditorStatus.BuildAssetBundle; var platform = BApplication.GetRuntimePlatform(buildTarget); var platformOutputPath = IPath.Combine(BuildParams.OutputPath, BApplication.GetPlatformPath(platform)); string abOutputPath = IPath.Combine(platformOutputPath, BResources.ART_ASSET_ROOT_PATH); //--------------------------------开始打包---------------------------------- //1.打包 Debug.Log("<color=green>----->1.进入打包逻辑</color>"); //整理abname this.MergeABName(BuildingAssetInfos); //对比差异文件 var changedAssetsInfo = GetChangedAssets(BuildingAssetInfos, buildTarget); //生成artconfig var assetbundleItemList = this.GenAssetBundleConfig(BuildingAssetInfos, BuildParams, platform); //打包 AssetDatabase.StartAssetEditing(); //禁止自动导入 { this.BuildAssetBundle(assetbundleItemList, changedAssetsInfo, BuildParams, platform); } AssetDatabase.StopAssetEditing(); //恢复自动导入 //2.清理 Debug.Log("<color=green>----->2.清理旧ab</color>"); //移除所有的ab RemoveAllAssetbundleName(); //删除本地没有的资源 var allABList = Directory.GetFiles(abOutputPath, "*", SearchOption.AllDirectories).Where((p) => string.IsNullOrEmpty(Path.GetExtension(p))); foreach (var abpath in allABList) { var abname = Path.GetFileName(abpath); var ret = assetbundleItemList.FirstOrDefault((abdata) => abdata.AssetBundlePath == abname); if (ret == null) { // File.Delete(abpath); File.Delete(abpath + ".manifest"); // var path = AssetDatabase.GUIDToAssetPath(abname); Debug.Log("【删除旧ab:】" + abname + " - " + path); } } //3.BuildInfo配置处理 Debug.Log("<color=green>----->3.BuildInfo相关生成</color>"); //设置ab的hash foreach (var abi in assetbundleItemList) { if (string.IsNullOrEmpty(abi.AssetBundlePath)) { continue; } var abpath = IPath.Combine(platformOutputPath, BResources.ART_ASSET_ROOT_PATH, abi.AssetBundlePath); var hash = FileHelper.GetMurmurHash3(abpath); abi.Hash = hash; } //获取上一次打包的数据,跟这次打包数据合并 var configPath = IPath.Combine(platformOutputPath, BResources.ART_ASSET_CONFIG_PATH); if (File.Exists(configPath)) { var lastAssetbundleItemList = CsvSerializer.DeserializeFromString <List <AssetBundleItem> >(File.ReadAllText(configPath)); foreach (var newABI in assetbundleItemList) { if (string.IsNullOrEmpty(newABI.AssetBundlePath)) { continue; } // //判断是否在当前打包列表中 // var ret = changedAssetsInfo.AssetDataMaps.Values.FirstOrDefault((a) => a.ABName.Equals(newABI.AssetBundlePath, StringComparison.OrdinalIgnoreCase)); var lastABI = lastAssetbundleItemList.FirstOrDefault((last) => newABI.AssetBundlePath.Equals(last.AssetBundlePath, StringComparison.OrdinalIgnoreCase)); //AB名相等 //&& newABI.Hash == last.Hash); //hash相等 //没重新打包,则用上一次的mix信息 if (lastABI != null && lastABI.Hash == newABI.Hash) { newABI.Mix = lastABI.Mix; } //否则mix = 0 } } //保存artconfig.info var csv = CsvSerializer.SerializeToString(assetbundleItemList); FileHelper.WriteAllText(configPath, csv); //保存BuildInfo配置 var buildinfoPath = IPath.Combine(platformOutputPath, BResources.EDITOR_ART_ASSET_BUILD_INFO_PATH); //缓存buildinfo var json = JsonMapper.ToJson(BuildingAssetInfos, true); FileHelper.WriteAllText(buildinfoPath, json); //4.备份Artifacts //this.BackupArtifacts(buildTarget); //5.检测本地的Manifest和构建预期对比 Debug.Log("<color=green>----->5.校验AB依赖</color>"); var abRootPath = IPath.Combine(BuildParams.OutputPath, BApplication.GetPlatformPath(platform), BResources.ART_ASSET_ROOT_PATH); var previewABUnitMap = BuildingAssetInfos.PreviewAssetbundleUnit(); var manifestList = Directory.GetFiles(abRootPath, "*.manifest", SearchOption.AllDirectories); //解析 manifestBuildParams.OutputPath for (int i = 0; i < manifestList.Length; i++) { var manifest = manifestList[i].Replace("\\", "/"); if (manifest.Equals(abRootPath + ".manifest")) { continue; } var lines = File.ReadLines(manifest); List <string> manifestDependList = new List <string>(); bool isStartRead = false; foreach (var line in lines) { if (!isStartRead && line.Equals("Assets:")) { isStartRead = true; } else if (line.Contains("Dependencies:")) { break; } else if (isStartRead) { var file = line.Replace("- ", ""); manifestDependList.Add(file.ToLower()); } } //对比依赖 var abname = Path.GetFileNameWithoutExtension(manifest); if (abname.Equals(BResources.ART_ASSET_ROOT_PATH, StringComparison.OrdinalIgnoreCase)) { continue; } previewABUnitMap.TryGetValue(abname, out var previewABDependList); if (previewABDependList == null) { Debug.LogError("【AssetbundleV2-验证】本地ab的配置不不存在:" + abname); Debug.LogError("path:" + AssetDatabase.GUIDToAssetPath(abname)); } else { //求差集 var except = manifestDependList.Except(previewABDependList); if (except.Count() != 0) { var local = JsonMapper.ToJson(manifestDependList, true); var preview = JsonMapper.ToJson(previewABDependList, true); Debug.LogError($"【AssetbundleV2-验证】本地AssetBundle依赖与预期不符:\n 本地:{local} \n 预期:{preview}"); } } } //6.资源混淆 Debug.Log("<color=green>----->6.混淆AB</color>"); if (BDEditorApplication.BDFrameworkEditorSetting.BuildAssetBundleSetting.IsEnableObfuscation) { AssetBundleEditorToolsV2.MixAssetBundle(BuildParams.OutputPath, platform); } //恢复编辑器状态 BDEditorApplication.EditorStatus = BDFrameworkEditorStatus.Idle; //BD生命周期触发 BDFrameworkPipelineHelper.OnEndBuildAssetBundle(this); //GenAssetBundleItemCacheList = abConfigList.ToList(); }
/// <summary> /// 最新包 /// </summary> void OnGUI_TestAssetBundle() { GUILayout.BeginVertical(); { GUILayout.Label("构建资源:", EditorGUIHelper.LabelH4); GUILayout.BeginHorizontal(); { if (GUILayout.Button("收集Keyword[Shader Feature]", GUILayout.Width(200), GUILayout.Height(25))) { ShaderCollection.CollectShaderVariant(); } if (GUILayout.Button("打包Shader测试", GUILayout.Width(120), GUILayout.Height(25))) { ShaderCollection.BuildShadersAssetBundle(); } } GUILayout.EndHorizontal(); if (GUILayout.Button("编辑AssetBundle颗粒度", GUILayout.Height(25))) { var win = GetWindow <AssetGraphEditorWindow>(); win.OpenGraph("Assets/AssetGraph/BResourceAssetBundleConfig.asset"); } GUILayout.Space(5); //遍历支持平台 foreach (var platform in BApplication.SupportPlatform) { GUILayout.BeginHorizontal(); { GUILayout.Label(BApplication.GetPlatformPath(platform), GUILayout.Width(80)); GUILayout.Space(20); GUI.color = Color.green; if (GUILayout.Button("Build", GUILayout.Width(80))) { var ret = EditorUtility.DisplayDialog("提示", "是否要构建AssetBundle? \n平台:" + BApplication.GetPlatformPath(platform), "Ok", "Cancel"); if (ret) { //开始打包 BuildAssetBundle(BApplication.DevOpsPublishAssetsPath, platform); } } if (GUILayout.Button("混淆AB", GUILayout.Width(80))) { var ret = EditorUtility.DisplayDialog("提示", "是否要混淆AssetBundle? \n平台:" + BApplication.GetPlatformPath(platform), "Ok", "Cancel"); if (ret) { AssetBundleEditorToolsV2.MixAssetBundle(BApplication.DevOpsPublishAssetsPath, platform); } } GUI.color = GUI.backgroundColor; } GUILayout.EndHorizontal(); } GUILayout.Space(10); //(); GUILayout.Label("资源验证:", EditorGUIHelper.LabelH4); //加载ab GUILayout.BeginHorizontal(); { GUILayout.Label("AssetBundle验证: DevOps目录", EditorGUIHelper.GetFontStyle(Color.white, 12)); if (GUILayout.Button("Play", GUILayout.Width(50), GUILayout.Height(20))) { AssetBundleEditorToolsV2CheckAssetbundle.TestLoadAssetbundleRuntime(); } } GUILayout.EndHorizontal(); // GUILayout.Space(5); //(); // //加载ab异步 // GUILayout.BeginHorizontal(); // { // GUILayout.Label("AssetBundle验证: 加载所有-Async (DevOps目录)", EditorGUIHelper.GetFontStyle(Color.white, 12)); // if (GUILayout.Button("Play", GUILayout.Width(50), GUILayout.Height(20))) // { // AssetBundleEditorToolsV2CheckAssetbundle.TestLoadAssetbundleRuntimeAsync(); // } // } // GUILayout.EndHorizontal(); } GUILayout.EndVertical(); }
/// <summary> /// 生成BuildInfo信息 /// </summary> public bool GenBuildInfo() { //初始化数据 this.AssetTypeList = new List <string>(); this.BuildAssetsInfo = new BuildAssetsInfo(); this.RuntimeAssetsList = GetRuntimeAssetsInfo(); // var sw = new Stopwatch(); sw.Start(); BuildAssetsInfo.Time = DateTime.Now.ToShortDateString(); int id = 0; //搜集所有的依赖 foreach (var mainAsset in this.RuntimeAssetsList) { //这里会包含主资源 var dependAssetPathList = GetDependAssetList(mainAsset.importFrom); //获取依赖信息 并加入buildinfo foreach (var dependPath in dependAssetPathList) { //防止重复 if (BuildAssetsInfo.AssetDataMaps.ContainsKey(dependPath)) { continue; } //判断资源类型 var type = AssetBundleEditorToolsV2.GetMainAssetTypeAtPath(dependPath); if (type == null) { Debug.LogError("获取资源类型失败:" + dependPath); continue; } //构建资源类型 var assetData = new BuildAssetsInfo.BuildAssetData(); assetData.Id = id; assetData.Hash = this.GetHashFromAssets(dependPath); assetData.ABName = dependPath; var idx = AssetTypeList.FindIndex((a) => a == type.FullName); if (idx == -1) { AssetTypeList.Add(type.FullName); idx = AssetTypeList.Count - 1; } assetData.Type = idx; //获取依赖 var dependeAssetList = this.GetDependAssetList(dependPath); assetData.DependAssetList.AddRange(dependeAssetList); //添加 BuildAssetsInfo.AssetDataMaps[dependPath] = assetData; id++; } } //TODO AB依赖关系纠正 /// 已知Unity,bug/设计缺陷: /// 1.依赖接口,中会携带自己 /// 2.如若a.png、b.png 依赖 c.atlas,则abc依赖都会是:a.png 、b.png 、 a.atlas foreach (var asset in BuildAssetsInfo.AssetDataMaps) { //依赖中不包含自己 asset.Value.DependAssetList.Remove(asset.Value.ABName); } //获取依赖 this.DependAssetList = this.GetDependAssetsinfo(); //---------------------------------------end--------------------------------------------------------- //检查 foreach (var ar in this.RuntimeAssetsList) { if (!BuildAssetsInfo.AssetDataMaps.ContainsKey(ar.importFrom)) { Debug.LogError("AssetDataMaps遗漏资源:" + ar.importFrom); } } Debug.LogFormat("【GenBuildInfo】耗时:{0}ms.", sw.ElapsedMilliseconds); //检测构造的数据 var count = this.RuntimeAssetsList.Count + this.DependAssetList.Count; if (BuildAssetsInfo.AssetDataMaps.Count != count) { Debug.LogErrorFormat("【初始化框架资源环境】出错! buildinfo:{0} output:{1}", BuildAssetsInfo.AssetDataMaps.Count, count); var tmpBuildAssetsInfo = BuildAssetsInfo.Clone(); foreach (var ra in this.RuntimeAssetsList) { tmpBuildAssetsInfo.AssetDataMaps.Remove(ra.importFrom); } foreach (var drf in this.DependAssetList) { tmpBuildAssetsInfo.AssetDataMaps.Remove(drf.importFrom); } Debug.Log(JsonMapper.ToJson(tmpBuildAssetsInfo.AssetDataMaps, true)); return(false); } return(true); }