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;
}
}
}