public static void AddAsset(string filePath, AssetGroup asset_) { //if (m_path2asset.ContainsKey(filePath)) //{ // Debug.Assert(asset_ != m_path2asset[filePath]); // return; //} m_path2asset[filePath] = asset_; }
//-------∽-★-∽------∽-★-∽--------∽-★-∽公共打包项∽-★-∽--------∽-★-∽------∽-★-∽--------// static AssetGroup GetShareAsset_Font() { AssetGroup assetInfo = new AssetGroup(); assetInfo.name = "font"; //"font.j"; assetInfo.bundleId = BUNDLE_ID.FONT; assetInfo.files = new List <string> { "Assets/Resources/GUI/cn/font/DroidSansFallback.ttf", "Assets/Resources/GUI/cn/font/FontMaterial.mat" }; return(assetInfo); }
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); }
static AssetGroup GetShareAsset_Shader() { var allShaderFiles = new List <string>(); string[] suffixs = new[] { ".shader" }; BundleUtility.CollectPathWithSuffix("Assets/RawData/Shaders/", suffixs, allShaderFiles, null); BundleUtility.CollectPathWithSuffix("Assets/Resources/Start/", suffixs, allShaderFiles, null); BundleUtility.CollectPathWithSuffix("Assets/ShaderForGame/", suffixs, allShaderFiles, null); AssetGroup assetInfo = new AssetGroup(); assetInfo.name = "shader";//"shader.j"; assetInfo.bundleId = BUNDLE_ID.SHADER; assetInfo.files = allShaderFiles; return(assetInfo); }
//-------∽-★-∽------∽-★-∽--------∽-★-∽数据管理∽-★-∽--------∽-★-∽------∽-★-∽--------// /// <summary> /// 目前只用于顶级文件 /// </summary> /// <param name="filePath"></param> /// <returns></returns> public static AssetGroup CreateAsset(string filePath) { if (m_path2asset.ContainsKey(filePath)) { return(m_path2asset[filePath]); } string name; string path; BundleUtility.AnalyzeBundlePath(filePath, out path, out name); //path->Resources/GUI/cn/Prefab, name->Canvas_Account AssetGroup assetInfo = new AssetGroup(); // Assets/Resources/GUI/cn/Prefab/Canvas_Account.prefab -> Resources/GUI/cn/Prefab/Canvas_Account assetInfo.name = BundleUtility.Utf8toAscii(String.Format("{0}/{1}", path, name)); //为什么要转为asc码? assetInfo.isPatch = false; assetInfo.files.Add(filePath); m_path2asset[filePath] = assetInfo; return(assetInfo); }
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; } }
/// <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); }
static public void Export(List <BundleGenInfo> genInfos_) { //收集bundle节点 var bdlNodes = new List <BdlNode>(); HashSet <string> assetNameSet = new HashSet <string>(); List <AssetGroup> assetInfos = new List <AssetGroup>(); //收集assetInfo foreach (BundleGenInfo genInfo in genInfos_) { //收集顶级文件 foreach (AssetGroup assetInfo in genInfo.assets) { if (!assetNameSet.Contains(assetInfo.name)) { assetNameSet.Add(assetInfo.name); assetInfos.Add(assetInfo); } } //收集依赖 foreach (AssetGroup assetInfo in genInfo.assets) { foreach (var dpAsset in assetInfo.dpAssets) { if (!assetNameSet.Contains(dpAsset.name)) { assetNameSet.Add(dpAsset.name); assetInfos.Add(dpAsset); } } } } //排序 assetInfos.Sort((a, c) => { if (a.isPatch && (!c.isPatch)) { return(1); } if ((!a.isPatch) && (c.isPatch)) { return(-1); } return(a.name.CompareTo(c.name)); }); //分配id int id = 0; for (int i = 0; i < assetInfos.Count; ++i) { if (assetInfos[i].bundleId == null) { assetInfos[i].bundleId = (id + BUNDLE_IDX_BEGIN).ToString(); ++id; } } //再按id排一次序 assetInfos.Sort((a, c) => { return(a.bundleId.CompareTo(c.bundleId)); }); BdlNode bdlNode; for (var i = 0; i < assetInfos.Count; ++i) { AssetGroup assetInfo = assetInfos[i]; bdlNode = new BdlNode(); bdlNode.bundleId = assetInfo.bundleId; bdlNode.bundleName = assetInfo.name.ToLower(); List <string> dependIds = new List <string>(); foreach (AssetGroup dpAsset in assetInfo.dpAssets) //收集依赖包的id { dependIds.Add(dpAsset.bundleId); } bdlNode.depends = dependIds.ToArray(); bdlNodes.Add(bdlNode); } //收集资源节点 var resNodes = new List <ResNode>(); ResNode resNode; foreach (var assetInfo in assetInfos) { if (assetInfo.isTopFile) { //是顶级文件 foreach (string path in assetInfo.files) { string resPath = RestoreResPath(path); //还原为游戏里用到的路径(asset下) if (!string.IsNullOrEmpty(resPath)) { resNode = new ResNode(); resNode.path = resPath; resNode.bundleId = assetInfo.bundleId; resNode.suffix = Path.GetExtension(path); resNodes.Add(resNode); if (resNode.suffix.ToLower() != ".unity") // 除场景其他小写 { resNode.path = resNode.path.ToLower(); } } } } } var nodeMap = new BdlNodeMap(); nodeMap.bdlNodes = bdlNodes.ToArray(); nodeMap.resNodes = resNodes.ToArray(); string exportPath = Path.Combine(BundleBuilder.PATH_RES_CFG, CFG_FILE_NAME); JsonUtil.WriteToJson(nodeMap, exportPath, true); Debug.Log("导出资源配置: " + exportPath); }