// static void BuildBundles(List <BundleGenInfo> genInfos_, bool needBuild) { AssetBundleBuild[] shareBuilds = null; BundleGenInfo shareGenInfo = GetShareBundle(); if (shareGenInfo != null) { //公共打包项, 这里仅为拿到AssetBundleBuild列表 shareBuilds = BuildBundle(shareGenInfo, null, false).ToArray(); } foreach (BundleGenInfo genInfo in genInfos_) { BuildBundle(genInfo, shareBuilds, needBuild); } if (shareGenInfo != null) { //把公共项添加到总表 genInfos_.Add(shareGenInfo); } BundleLog.MakeLog(genInfos_); //生成日志 AbsResExport.Export(genInfos_); //生辰资源配置 }
public static void MakeLog(List <BundleGenInfo> genInfos_) { if (!Directory.Exists(log_folder_path)) { Directory.CreateDirectory(log_folder_path); //创建文件夹 } string filePath = log_folder_path + "/Log_abs " + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".txt"; StringBuilder strBundler = new StringBuilder(); for (int i = 0; i < genInfos_.Count; ++i) { BundleGenInfo genInfo = genInfos_[i]; //包名 strBundler.AppendLine("bundle_name:" + genInfo.bundleName); string tab = TAB; //依赖 strBundler.AppendLine(tab + "bundle_depend:"); LogAsset(genInfo.dpAssets, strBundler, tab); //资源列表 LogAsset(genInfo.assets, strBundler, ""); } File.WriteAllBytes(filePath, Encoding.UTF8.GetBytes(strBundler.ToString())); Debug.Log("导出打包日志: " + filePath); }
public static void Collect_s(BundleGenInfo info_) { BdlCollect collect = null; switch (info_.packType) { case PACK_TYPE.LD_FST: collect = new BdlCollectLdFst(); break; } if (collect != null) { collect.Collect(info_); } }
static BundleGenInfo GetShareBundle() { AssetGroup[] assetInfos = new AssetGroup[] { GetShareAsset_Font(), GetShareAsset_Shader(), }; BundleGenInfo genInfo = new BundleGenInfo(); genInfo.bundleName = "share"; genInfo.combineNum = 1; genInfo.assets.AddRange(assetInfos); return(genInfo); }
public virtual void Collect(BundleGenInfo info_) { }
/// <summary> /// 根据manifest重置单个GenInfo的依赖关系 /// (主要是补全顶级文件依赖顶级文件这种情况) /// </summary> /// <param name="manifest"></param> /// <param name="genInfo_"></param> public static void ResetDependsByManifest(AssetBundleManifest manifest, BundleGenInfo genInfo_) { string[] bundlePaths = manifest.GetAllAssetBundles(); foreach (string bundlePath in bundlePaths) { string bundleName = GetBdlNameByExportPath(bundlePath); foreach (AssetGroup assetInfo in genInfo_.assets) { if (assetInfo.name.ToLower() != bundleName) { continue; } List <AssetGroup> newDpAssets = new List <AssetGroup>(); string[] dpPaths = manifest.GetDirectDependencies(bundlePath); //获取实际用到的依赖路径 foreach (var dpPath in dpPaths) { var dpBundleName = GetBdlNameByExportPath(dpPath); bool areadyIn = false; foreach (AssetGroup a in newDpAssets) { if (a.name.ToLower() == dpBundleName) { areadyIn = true; break; } } if (!areadyIn) { bool found = false; foreach (AssetGroup assetInfo2 in genInfo_.assets) { if (assetInfo2.name.ToLower() == dpBundleName) { found = true; newDpAssets.Add(assetInfo2); } foreach (AssetGroup dpAsset in assetInfo2.dpAssets) { if (dpAsset.name.ToLower() == dpBundleName) { newDpAssets.Add(dpAsset); found = true; } } if (found) { break; } } } } List <AssetGroup> oldDpAssets = assetInfo.dpAssets; assetInfo.dpAssets = newDpAssets; //打印修正信息 //增加 foreach (AssetGroup a1 in newDpAssets) { if (!oldDpAssets.Contains(a1)) { Log.Debug(string.Format("依赖修正: {0} {1} 增加: {2}", genInfo_.bundleName, assetInfo.name, a1.name)); } } //删除 foreach (AssetGroup a1 in oldDpAssets) { if (!newDpAssets.Contains(a1)) { Log.Debug(string.Format("依赖修正: {0} {1} 删除: {2}", genInfo_.bundleName, assetInfo.name, a1.name)); } } } } }
public override void Collect(BundleGenInfo genInfo_) { string bundleName = genInfo_.bundleName; HashSet <string> topFileHash = new HashSet <string>(); foreach (PathInfo pathInfo in genInfo_.pathInfos) { //顶级文件列表 List <string> topFiles = new List <string>(); //递归收集所有文件 BundleUtility.CollectPathWithSuffix(pathInfo.path, pathInfo.suffixs, topFiles, pathInfo.exclude); //按名称排序 topFiles.Sort(); int bundleId = 0; int curIdx = 0; while (curIdx < topFiles.Count) { string name = bundleName + "_" + bundleId; //gui_0 AssetGroup asset = new AssetGroup(); asset.name = name; asset.isPatch = false; asset.isTopFile = true; //合并 for (int i = 0; i < pathInfo.combineNum && curIdx < topFiles.Count; ++i, ++curIdx) { asset.files.Add(topFiles[curIdx]); topFileHash.Add(topFiles[curIdx]); BundleBuilder.AddAsset(topFiles[curIdx], asset); //关联文件路径与BundleAsset } bundleId++; genInfo_.assets.Add(asset); } } //收集依赖 var asset2dpPathSet = new Dictionary <AssetGroup, HashSet <string> >(); //A的dpPathSet与B的此时可能会有重复依赖项 foreach (AssetGroup asset in genInfo_.assets) { var __dpPathSet = new HashSet <string>(); asset2dpPathSet[asset] = __dpPathSet; foreach (string path in asset.files) { BundleUtility.CollectDepends(path, __dpPathSet); } //排除顶级依赖 List <string> excludes = new List <string>(); foreach (string path in __dpPathSet) { if (topFileHash.Contains(path)) //这个依赖是顶级文件 { excludes.Add(path); //如果有依赖项是顶级文件,便直接添加到asset的depends中, 这些文件不视为外部依赖 AssetGroup topAsset = BundleBuilder.CreateAsset(path); if (!asset.dpAssets.Contains(topAsset)) { asset.dpAssets.Add(topAsset); } } } foreach (string path in excludes) //从pathHash移除掉 { __dpPathSet.Remove(path); } } // 统计一个dependPath被包含到多少个Asset里 var dpPath2assetSet = new Dictionary <string, HashSet <AssetGroup> >(); foreach (var kvp in asset2dpPathSet) { AssetGroup asset = kvp.Key; HashSet <string> __dpPathSet = kvp.Value; foreach (string dpPath in __dpPathSet) { HashSet <AssetGroup> assetHash; if (!dpPath2assetSet.TryGetValue(dpPath, out assetHash)) { assetHash = new HashSet <AssetGroup>(); dpPath2assetSet[dpPath] = assetHash; } assetHash.Add(asset); //dpPath -> {AssetGroup, ...} } } List <string> toDelPaths = new List <string>(); //只被一个文件依赖, 和此文件一起打包 foreach (var kvp in dpPath2assetSet) { string dpPath = kvp.Key; HashSet <AssetGroup> assetHash = kvp.Value; if (assetHash.Count <= 1) //排查出只有一个引用的依赖 { AssetGroup parentAsset = assetHash.First(); HashSet <string> __dpPathSet = asset2dpPathSet[parentAsset]; __dpPathSet.Remove(dpPath); //从引用者的dpPathHash里面移除, 从而不属于公共依赖 toDelPaths.Add(dpPath); } } //dpPath2assetSet里只保留引用数>1的路径 foreach (string path in toDelPaths) { dpPath2assetSet.Remove(path); } //对所有依赖进行排序 List <string> mainDpPaths = new List <string>(); mainDpPaths.AddRange(dpPath2assetSet.Keys); mainDpPaths.Sort((r, l) => { if (dpPath2assetSet[r].Count != dpPath2assetSet[l].Count) { //优先按依赖数量排序 return(dpPath2assetSet[l].Count - dpPath2assetSet[r].Count); } return(l.CompareTo(r)); }); //依赖总表 HashSet <string> mainDpPathSet = new HashSet <string>(); foreach (string path in mainDpPaths) { mainDpPathSet.Add(path); } HashSet <string> tmpDpPathSet = new HashSet <string>(); List <string> tmpDpPaths = new List <string>(); int index = 0; while (index < mainDpPaths.Count) { if (!mainDpPathSet.Contains(mainDpPaths[index])) { //已经处理过 index++; continue; } //50个依赖文件->dpPaths int combineCnt = 50; //一个包的依赖文件个数 List <string> dpFiles = new List <string>(); while (dpFiles.Count < combineCnt && index < mainDpPaths.Count) { string path = mainDpPaths[index]; if (mainDpPathSet.Contains(path)) { //还没处理过 mainDpPathSet.Remove(path); dpFiles.Add(path); } index++; } //此时dpFiles有50个依赖文件 bool hasMore = true; while (hasMore) { hasMore = false; tmpDpPaths.Clear(); tmpDpPaths.AddRange(dpFiles); tmpDpPathSet.Clear(); BundleUtility.CollectDepends(tmpDpPaths.ToArray(), tmpDpPathSet); //收集这个依赖列表的所有依赖项 foreach (string path in tmpDpPathSet) { if (mainDpPathSet.Contains(path)) { //次依赖在总表里,收集到本次列表里 mainDpPathSet.Remove(path); dpFiles.Add(path); hasMore = true; } } //查找剩下全部依赖 tmpDpPaths.Clear(); tmpDpPaths.AddRange(mainDpPathSet); foreach (string path in tmpDpPaths) { if (mainDpPathSet.Contains(path)) { tmpDpPathSet.Clear(); BundleUtility.CollectDepends(tmpDpPaths.ToArray(), tmpDpPathSet); //收集剩下的全部依赖的所有依赖项 bool packed = false; foreach (string dpPath in tmpDpPathSet) { if (dpFiles.Contains(dpPath)) { //如果这些依赖在这次的收集列表里, 也收集进来 dpFiles.Add(path); mainDpPathSet.Remove(path); hasMore = true; packed = true; break; } } if (packed) { foreach (var dpPath in tmpDpPathSet) { if (mainDpPathSet.Contains(dpPath)) { mainDpPathSet.Remove(dpPath); //所有依赖里面在总表的都添加到这次的收集 dpFiles.Add(dpPath); } } } } } } if (dpFiles.Count > 0) { } } }
public override void Collect(BundleGenInfo genInfo_) { string bundleName = genInfo_.bundleName; //顶级文件列表 List <string> topFiles = new List <string>(); foreach (var pathInfo in genInfo_.pathInfos) { BundleUtility.CollectPathWithSuffix(pathInfo.path, pathInfo.suffixs, topFiles, pathInfo.exclude); //递归收集所有文件 } //按名称排序 topFiles.Sort(); foreach (string path in topFiles) { AssetGroup assetInfo = BundleBuilder.CreateAsset(path); assetInfo.isTopFile = true; //是顶级文件 genInfo_.assets.Add(assetInfo); } //收集依赖 var asset2dpPathSet = new Dictionary <AssetGroup, HashSet <string> >(); foreach (AssetGroup asset in genInfo_.assets) { HashSet <string> dpPathSet = new HashSet <string>(); asset2dpPathSet[asset] = dpPathSet; foreach (string filePath in asset.files) //到这里的files应该只有一个文件,之后会添加 { BundleUtility.CollectDepends(filePath, dpPathSet); } //排除顶级依赖 List <string> excludes = new List <string>(); foreach (string path in dpPathSet) { if (topFiles.Contains(path)) //这个依赖是顶级文件 { excludes.Add(path); //如果有依赖项是顶级文件,便直接添加到asset的depends中, 这些文件不视为外部依赖 asset.dpAssets.Add(BundleBuilder.CreateAsset(path)); } } foreach (string path in excludes) //从pathHash移除掉 { dpPathSet.Remove(path); } } // 统计一个dependPath被包含到多少个Asset里 var dpPath2assetSet = new Dictionary <string, HashSet <AssetGroup> >(); foreach (var kvp in asset2dpPathSet) { AssetGroup asset = kvp.Key; HashSet <string> dpPathSet = kvp.Value; foreach (string dpPath in dpPathSet) { HashSet <AssetGroup> assetHash; if (!dpPath2assetSet.TryGetValue(dpPath, out assetHash)) { assetHash = new HashSet <AssetGroup>(); dpPath2assetSet[dpPath] = assetHash; } assetHash.Add(asset); //dpPath -> {assetInfo, ...} } } List <string> toDelPath = new List <string>(); // 只被一个文件依赖的 就和这个文件打包在一起,不再区分 foreach (var kvp in dpPath2assetSet) { string dpPath = kvp.Key; HashSet <AssetGroup> assetHash = kvp.Value; if (assetHash.Count <= 1) //排查出只有一个引用的依赖 { AssetGroup parentAsset = assetHash.First(); HashSet <string> dpPathSet = asset2dpPathSet[parentAsset]; dpPathSet.Remove(dpPath); //从引用者的dpPathHash里面移除, 从而不属于公共依赖 toDelPath.Add(dpPath); } } //dpPath2assetSet里只保留引用数>1的路径 foreach (string path in toDelPath) { dpPath2assetSet.Remove(path); } //建立无向图(所有依赖的连通图) { {A(a,b,c), B(a,d,e) } -> { a(b,c,d,e), b(c,a), c(a,b), d(a,e), e(a,d) } var path2node = new Dictionary <string, GraphNode>(); foreach (var kvp in asset2dpPathSet) { AssetGroup asset = kvp.Key; HashSet <string> dpPathSet = kvp.Value; //被同一个资源引用的依赖列表 foreach (string dpPath in dpPathSet) { GraphNode dpNode; if (!path2node.TryGetValue(dpPath, out dpNode)) //为所有依赖创建节点 { dpNode = new GraphNode(); dpNode.path = dpPath; path2node[dpPath] = dpNode; } foreach (string dpPath2 in dpPathSet) { if (dpPath2 == dpPath) { continue; } GraphNode dpNode2; if (path2node.TryGetValue(dpPath2, out dpNode2)) { //互相把自己添加到对方的edges dpNode2.edges.Add(dpNode); dpNode.edges.Add(dpNode2); } } } } //创建节点块(把所有连通的依赖独立出来一块) { (a,b,c,d,e), (...) } var nodeSets = new List <HashSet <GraphNode> >(); foreach (var kvp in path2node) { GraphNode node = kvp.Value; if (!node.visited) { HashSet <GraphNode> nodeSet = new HashSet <GraphNode>(); VisitNode(node, nodeSet); nodeSets.Add(nodeSet); } } int idx = 0; foreach (var nodeSet in nodeSets) { AssetGroup dpAsset = new AssetGroup(); string dpBdlName = Path.Combine("depends", string.Format("{0}_{1}", bundleName, idx)); //depends/gui_0 dpAsset.name = FileUtility.FomatPath(dpBdlName); dpAsset.isTopFile = false; foreach (GraphNode node in nodeSet) { dpAsset.files.Add(node.path); } //如果顶级文件的依赖项里有依赖这个块的其中一个文件,便把这个块的包作为顶级文件的依赖项 foreach (var kvp in asset2dpPathSet) { AssetGroup asset = kvp.Key; HashSet <string> dpPathSet = kvp.Value; foreach (string dpPath in dpAsset.files) { if (dpPathSet.Contains(dpPath)) { asset.dpAssets.Add(dpAsset); break; } } } genInfo_.dpAssets.Add(dpAsset); //添加到总依赖列表 ++idx; } }
static void ReadExcelConfig(FileStream stream, string sheetName, List <BundleGenInfo> genInfos) { Dictionary <string, BundleGenInfo> name2genInfo = new Dictionary <string, BundleGenInfo>(); /// 读取excel配置 IWorkbook workbook = new HSSFWorkbook(stream); ISheet sheet = workbook.GetSheet(sheetName); int rowCount = sheet.LastRowNum; for (int i = 1; i <= rowCount; ++i) //数据从第二行开始 { IRow row = sheet.GetRow(i); //获取行数据 if (row == null) { break; } ICell pathCell = row.GetCell((int)ExcelRID.Path); if (pathCell == null || string.IsNullOrEmpty(pathCell.StringCellValue)) { break; } //打包路径 string path = pathCell.StringCellValue; ICell bundleNameCell = row.GetCell((int)ExcelRID.BundleName); if (bundleNameCell == null || string.IsNullOrEmpty(bundleNameCell.StringCellValue)) { break; } //包名 string bundleName = bundleNameCell.StringCellValue; BundleGenInfo genInfo; if (name2genInfo.ContainsKey(bundleName)) { genInfo = name2genInfo[bundleName]; //同一包名的项收集在一个genInfo里 } else { genInfo = new BundleGenInfo(); //包名 genInfo.bundleName = bundleName; //打包方式 genInfo.packType = row.GetCell((int)ExcelRID.PackType).StringCellValue; //因此同一个包名的打包方式应该一致 //分包数 genInfo.combineNum = 1; //作为场景打包 //var isSceneCell = row.GetCell((int)ExcelRID.AsScene); //if (isSceneCell != null && !string.IsNullOrEmpty(isSceneCell.StringCellValue)) //{ // genInfo.isScene = true; //} name2genInfo[bundleName] = genInfo; } BundleGenInfo.PathInfo pathInfo = new BundleGenInfo.PathInfo(); genInfo.pathInfos.Add(pathInfo); pathInfo.path = path.Trim(); //ID(并没有作用) string idStr = row.GetCell((int)ExcelRID.ID).StringCellValue; if (!string.IsNullOrEmpty(idStr)) { pathInfo.id = idStr.Trim(); //去掉边缘空格 } var combineNumCell = row.GetCell((int)ExcelRID.CombineNum); if (combineNumCell != null && !string.IsNullOrEmpty(combineNumCell.StringCellValue)) { pathInfo.combineNum = int.Parse(combineNumCell.StringCellValue); pathInfo.combineNum = Math.Max(pathInfo.combineNum, 1); genInfo.combineNum = pathInfo.combineNum; } //打包的后缀名 string suffixStr = row.GetCell((int)ExcelRID.Suffixs).StringCellValue; if (!string.IsNullOrEmpty(suffixStr)) { pathInfo.suffixs = suffixStr.Split(','); for (int j = 0; j < pathInfo.suffixs.Length; ++j) { pathInfo.suffixs[j] = pathInfo.suffixs[j].Trim(); //去掉边缘空格 } } //排除的文件/目录名称 string exclueStr = row.GetCell((int)ExcelRID.Exclude).StringCellValue; if (!string.IsNullOrEmpty(exclueStr)) { pathInfo.exclude = exclueStr.Split(','); for (int j = 0; j < pathInfo.exclude.Length; ++j) { pathInfo.exclude[j] = pathInfo.exclude[j].Trim(); //去掉边缘空格 } } if (!genInfos.Contains(genInfo)) //保证只有一个 { genInfos.Add(genInfo); } } workbook.Close(); }
/// <summary> /// 根据单个生成信息打包 /// </summary> /// <param name="genInfo"></param> /// <param name="shareBuilds_">公共打包项</param> /// <param name="needBuild">是否需要build</param> /// <returns></returns> static List <AssetBundleBuild> BuildBundle(BundleGenInfo genInfo, AssetBundleBuild[] shareBuilds_, bool needBuild) { //先排个序 genInfo.assets.Sort((l, r) => { return(l.name.CompareTo(r.name)); }); List <AssetBundleBuild> bundleBuilds = new List <AssetBundleBuild>(); if (shareBuilds_ != null) //公共打包项 { bundleBuilds.AddRange(shareBuilds_); } //建立依赖的BundleBuild foreach (var dpAsset in genInfo.dpAssets) { dpAsset.name = BundleUtility.GetBundleOutputName(dpAsset.name); //重新定义包名 AssetBundleBuild build = new AssetBundleBuild(); build.assetBundleName = dpAsset.name; build.assetNames = dpAsset.files.ToArray(); //每个依赖列表打成一个包 bundleBuilds.Add(build); } int bdlIdx = 0; int combineNum = genInfo.combineNum; List <AssetGroup> newAssets = new List <AssetGroup>(); List <AssetGroup> assetInfos = genInfo.assets; while (bdlIdx < assetInfos.Count) { AssetGroup assetInfo = assetInfos[bdlIdx]; AssetBundleBuild build = new AssetBundleBuild(); var dpAssetSet = new HashSet <AssetGroup>(); //按分包数收集包含的所有files List <string> files = new List <string>(); for (int i = 0; i < combineNum && bdlIdx < assetInfos.Count; ++i) { files.AddRange(assetInfos[bdlIdx].files); //添加文件路径 List <AssetGroup> dpAssets = assetInfos[bdlIdx].dpAssets; foreach (AssetGroup dpAsset in dpAssets) { dpAssetSet.Add(dpAsset); } ++bdlIdx; } //重定义名称 if (combineNum > 1 && !string.IsNullOrEmpty(genInfo.bundleName)) //需要分包 { var name = assetInfo.name.Replace("\\", "/"); //只取第一个asset的话,感觉会有问题, 如果里面掺杂了其他remainName,会单独打出一个包了 var lastIdx = name.LastIndexOf("/"); var remainPath = name.Substring(0, lastIdx); //Resources\GUI\cn\Prefab var remainName = Path.GetFileName(remainPath); //Prefab 其实就是文件夹名称 remainPath = remainPath.Substring(0, remainPath.Length - remainName.Length - 1); //Resources\GUI\cn int idxSuffix = 0; if (!genInfo.curCombineIdx.ContainsKey(remainName)) //这里有点繁琐,可以优化一下 { genInfo.curCombineIdx[remainName] = 0; } idxSuffix = genInfo.curCombineIdx[remainName]; genInfo.curCombineIdx[remainName] = idxSuffix + 1; assetInfo.name = BundleUtility.GetBundleOutputName(remainPath + "/" + remainName + "_" + idxSuffix.ToString()); build.assetBundleName = assetInfo.name; } else { assetInfo.name = BundleUtility.GetBundleOutputName(assetInfo.name); //格式化成输出用的名称 build.assetBundleName = assetInfo.name; } assetInfo.files = files; //覆盖为新的文件列表 newAssets.Add(assetInfo); //添加到新的asset列表 //覆盖为新的依赖列表 List <AssetGroup> newDpAssets = new List <AssetGroup>(); foreach (AssetGroup dpAsset in dpAssetSet) { newDpAssets.Add(dpAsset); } assetInfo.dpAssets = newDpAssets; build.assetNames = files.ToArray(); bundleBuilds.Add(build); } //覆盖为新的Asset列表 genInfo.assets = newAssets; if (needBuild) { //执行打包 AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(PATH_BUNDLE_ROOT, bundleBuilds.ToArray(), CurrentBuildAssetOpts, m_buildTarget); //根据manifest矫正一下依赖情况 BundleUtility.ResetDependsByManifest(manifest, genInfo); } return(bundleBuilds); }