using XGame.Editor.Asset; using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; namespace XGame.Editor.Build.AssetBundles { public class DepBundleCollector { private BundleContext _context; public DepBundleCollector(BundleContext context) { _context = context; } /// /// 收集ResStatic目录下被引用的资源 /// /// /// public void Collect() { //> var refBundlesMap = new Dictionary>(); foreach (var item in _context.bundleDataMap) { if (item.Value.isTextureBundle) { continue; } var assetDependencies = _context.GetDependencies(item.Value.bundleId, item.Value.assetNames); foreach (var dependency in assetDependencies) { if (_context.bundleAssetPaths.Contains(dependency) || Asset.FileUtil.IsFileIgnore(dependency)) continue; if (dependency.Contains(_context.ResStaticDir)) { if (dependency.Contains(_context.UiPrefabDir) && dependency.EndsWith(_context.PrefabExt, StringComparison.OrdinalIgnoreCase)) { //忽略ResStatic下的UI预制 continue; } } else if (dependency.StartsWith(PathDefine.PackageRelative, StringComparison.OrdinalIgnoreCase)) { if (dependency.EndsWith(_context.PrefabExt, StringComparison.OrdinalIgnoreCase)) { //忽略预制体文件 //Debug.LogWarning($"Bundle:{build.Value.bundleId} 依赖了Editor或者Resources资源. dependency:{dependency}"); continue; } } else { //if (dependency.StartsWith(PathDefine.ResourcesRelative, StringComparison.OrdinalIgnoreCase)) //{ // Debug.LogWarning($"Bundle:{build.Value.bundleId} 依赖了Resources资源. dependency:{dependency}"); //} continue; } if (!refBundlesMap.TryGetValue(dependency, out var bundles)) { bundles = new HashSet(); refBundlesMap.Add(dependency, bundles); } if (!bundles.Contains(item.Value.bundleId)) { bundles.Add(item.Value.bundleId); } //Debug.Log($"ResStatic dependency :{dependency} dir:{dir}"); } } // var bundleNamesMap = new Dictionary(); foreach (var item in refBundlesMap) { //只被单个bundle引用的,不需要单独打包bundle if (item.Value.Count < 2) continue; var str = string.Join('_', item.Value); bundleNamesMap.Add(item.Key, str); } var groups = bundleNamesMap.GroupBy(p => p.Value); //由于AssetBundle的资源重名只认资源文件名,因此这里做好重名检测 //用于排序,减少二次分包的次数 var assetPaths = new List(); var fileNames = new HashSet(); foreach (var group in groups) { //先取出所有assetPath foreach (var item in group) { assetPaths.Add(item.Key); } //排序 assetPaths.Sort((a, b) => string.Compare(a, b, StringComparison.OrdinalIgnoreCase)); //是否重名 var isNameSake = false; foreach (var assetPath in assetPaths) { var fileName = Path.GetFileName(assetPath); if (!fileNames.Add(fileName)) { //有重名 isNameSake = true; break; } } if (isNameSake) { var depAssetsMap = AssetsGroupByDependencies(assetPaths); //生成依赖包,bundleName由引用的BundleId拼接而成 var firstKey = "0"; var assets = depAssetsMap[firstKey]; var bundleBuild = _context.AddRawAssetBundle(assets, group.Key, true); //var bundleBuild = new AssetBundleData(group.Key, KCDefine.BUNDLE_VARIANT, assets, assets); //_context.bundleBuildMap.TryAdd(bundleBuild); depAssetsMap.Remove(firstKey); foreach (var item in depAssetsMap) { var bundleName = $"{bundleBuild.bundleId}_{item.Key}"; assets = item.Value; _context.AddRawAssetBundle(assets, bundleName, true); //var depBundle = new AssetBundleData(bundleName, KCDefine.BUNDLE_VARIANT, assets, assets); //_context.bundleBuildMap.TryAdd(depBundle); } } else { //生成依赖包,bundleName由引用的BundleId拼接而成 var assets = assetPaths.ToArray(); _context.AddRawAssetBundle(assets, group.Key, true); //var bundleBuild = new AssetBundleData(group.Key, KCDefine.BUNDLE_VARIANT, assets, assets); //_context.bundleBuildMap.TryAdd(bundleBuild); } assetPaths.Clear(); fileNames.Clear(); } } /// /// Assets根据依赖关系二次分组 /// 返回 /// /// /// private Dictionary AssetsGroupByDependencies(List assetPaths) { var result = new Dictionary(); var assetDependencies = new Dictionary(); var dependencies = new List(); //先收集assetPaths之间的依赖信息 foreach (var assetPath in assetPaths) { var deps = AssetDatabase.GetDependencies(assetPath, true); foreach (var dep in deps) { if (!assetPath.Equals(dep) && assetPaths.Contains(dep)) { dependencies.Add(dep); } } assetDependencies.Add(assetPath, dependencies.ToArray()); dependencies.Clear(); } var assetGroupMap = new Dictionary(); var pendingPaths = new List(); foreach (var item in assetDependencies) { if (item.Value.Length > 0) { pendingPaths.Add(item.Key); } else { //没有依赖的资源,直接保存到assetGroupMap assetGroupMap.Add(item.Key, 0); } } var groupId = 1; var tempPaths = new List(); var fileNames = new HashSet(); //递归查询 while (pendingPaths.Count > 0) { foreach (var assetPath in pendingPaths) { var isTrue = true; foreach (var dep in assetDependencies[assetPath]) { if (!assetGroupMap.ContainsKey(dep)) { isTrue = false; break; } } if (isTrue) { //assetPath依赖的资源都有分组记录 tempPaths.Add(assetPath); } } if (tempPaths.Count > 0) { //新增分组 foreach (var assetPath in tempPaths) { pendingPaths.Remove(assetPath); assetGroupMap.Add(assetPath, groupId); } groupId += 1; tempPaths.Clear(); } else { //有互相依赖的资源,直接归为同一个分组,这里面可能有重名的资源 //由CheckAssetsNameRepeat方法统一报错处理 var isNameSake = false; foreach (var assetPath in pendingPaths) { var fileName = Path.GetFileName(assetPath); if (!fileNames.Add(fileName)) { //有重名资源 isNameSake = true; break; } } if (isNameSake) { BuildLog.Error($"DepAssetbundle Group Error: 存在同名文件且Asset之间互相依赖。"); foreach (var assetPath in pendingPaths) { BuildLog.Error($"Error File:{assetPath}"); } } result.Add(groupId.ToString(), pendingPaths.ToArray()); pendingPaths.Clear(); fileNames.Clear(); } } var groups = assetGroupMap.GroupBy(item => item.Value); foreach (var group in groups) { foreach (var item in group) { var assetPath = item.Key; var fileName = Path.GetFileName(assetPath); if (fileNames.Add(fileName)) { tempPaths.Add(assetPath); } else { //有同名文件,该文件直接单独打包 var guid = AssetDatabase.AssetPathToGUID(assetPath); result.Add($"{item.Value}_{guid}", new string[] { assetPath }); } } if (tempPaths.Count > 0) { result.Add(group.Key.ToString(), tempPaths.ToArray()); } else { BuildLog.Error($"DepAssetbundle Group Error: GroupId:{group.Key} Count:{group.Count()} 分组数据为空。"); } tempPaths.Clear(); fileNames.Clear(); } return result; } } }