using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace YooAsset.Editor { [Serializable] public class AssetBundleCollector { /// /// 收集路径 /// 注意:支持文件夹或单个资源文件 /// public string CollectPath = string.Empty; /// /// 收集器的GUID /// public string CollectorGUID = string.Empty; /// /// 收集器类型 /// public ECollectorType CollectorType = ECollectorType.MainAssetCollector; /// /// 寻址规则类名 /// public string AddressRuleName = nameof(AddressByFileName); /// /// 打包规则类名 /// public string PackRuleName = nameof(PackDirectory); /// /// 过滤规则类名 /// public string FilterRuleName = nameof(CollectAll); /// /// 资源分类标签 /// public string AssetTags = string.Empty; /// /// 收集器是否有效 /// public bool IsValid() { if (AssetDatabase.LoadAssetAtPath(CollectPath) == null) return false; if (CollectorType == ECollectorType.None) return false; if (AssetBundleCollectorSettingData.HasAddressRuleName(AddressRuleName) == false) return false; if (AssetBundleCollectorSettingData.HasPackRuleName(PackRuleName) == false) return false; if (AssetBundleCollectorSettingData.HasFilterRuleName(FilterRuleName) == false) return false; return true; } /// /// 检测配置错误 /// public void CheckConfigError() { string assetGUID = AssetDatabase.AssetPathToGUID(CollectPath); if (string.IsNullOrEmpty(assetGUID)) throw new Exception($"Invalid collect path : {CollectPath}"); if (CollectorType == ECollectorType.None) throw new Exception($"{nameof(ECollectorType)}.{ECollectorType.None} is invalid in collector : {CollectPath}"); if (AssetBundleCollectorSettingData.HasPackRuleName(PackRuleName) == false) throw new Exception($"Invalid {nameof(IPackRule)} class type : {PackRuleName} in collector : {CollectPath}"); if (AssetBundleCollectorSettingData.HasFilterRuleName(FilterRuleName) == false) throw new Exception($"Invalid {nameof(IFilterRule)} class type : {FilterRuleName} in collector : {CollectPath}"); if (AssetBundleCollectorSettingData.HasAddressRuleName(AddressRuleName) == false) throw new Exception($"Invalid {nameof(IAddressRule)} class type : {AddressRuleName} in collector : {CollectPath}"); } /// /// 修复配置错误 /// public bool FixConfigError() { bool isFixed = false; if (string.IsNullOrEmpty(CollectorGUID) == false) { string convertAssetPath = AssetDatabase.GUIDToAssetPath(CollectorGUID); if (string.IsNullOrEmpty(convertAssetPath)) { Debug.LogWarning($"Collector GUID {CollectorGUID} is invalid and has been auto removed !"); CollectorGUID = string.Empty; isFixed = true; } else { if (CollectPath != convertAssetPath) { CollectPath = convertAssetPath; isFixed = true; Debug.LogWarning($"Fix collect path : {CollectPath} -> {convertAssetPath}"); } } } /* string convertGUID = AssetDatabase.AssetPathToGUID(CollectPath); if(string.IsNullOrEmpty(convertGUID) == false) { CollectorGUID = convertGUID; } */ return isFixed; } /// /// 获取打包收集的资源文件 /// public List GetAllCollectAssets(EBuildMode buildMode, AssetBundleCollectorGroup group) { // 注意:模拟构建模式下只收集主资源 if (buildMode == EBuildMode.SimulateBuild) { if (CollectorType != ECollectorType.MainAssetCollector) return new List(); } Dictionary result = new Dictionary(1000); bool isRawAsset = PackRuleName == nameof(PackRawFile); // 检测原生资源包的收集器类型 if (isRawAsset && CollectorType != ECollectorType.MainAssetCollector) throw new Exception($"The raw file must be set to {nameof(ECollectorType)}.{ECollectorType.MainAssetCollector} : {CollectPath}"); if (string.IsNullOrEmpty(CollectPath)) throw new Exception($"The collect path is null or empty in group : {group.GroupName}"); // 收集打包资源 if (AssetDatabase.IsValidFolder(CollectPath)) { string collectDirectory = CollectPath; string[] findAssets = EditorTools.FindAssets(EAssetSearchType.All, collectDirectory); foreach (string assetPath in findAssets) { if (IsValidateAsset(assetPath) && IsCollectAsset(assetPath)) { if (result.ContainsKey(assetPath) == false) { var collectAssetInfo = CreateCollectAssetInfo(buildMode, group, assetPath, isRawAsset); result.Add(assetPath, collectAssetInfo); } else { throw new Exception($"The collecting asset file is existed : {assetPath} in collector : {CollectPath}"); } } } } else { string assetPath = CollectPath; if (IsValidateAsset(assetPath) && IsCollectAsset(assetPath)) { var collectAssetInfo = CreateCollectAssetInfo(buildMode, group, assetPath, isRawAsset); result.Add(assetPath, collectAssetInfo); } else { throw new Exception($"The collecting single asset file is invalid : {assetPath} in collector : {CollectPath}"); } } // 检测可寻址地址是否重复 if (AssetBundleCollectorSettingData.Setting.EnableAddressable) { HashSet adressTemper = new HashSet(); foreach (var collectInfoPair in result) { if (collectInfoPair.Value.CollectorType == ECollectorType.MainAssetCollector) { string address = collectInfoPair.Value.Address; if (adressTemper.Contains(address) == false) adressTemper.Add(address); else throw new Exception($"The address is existed : {address} in collector : {CollectPath}"); } } } // 返回列表 return result.Values.ToList(); } private CollectAssetInfo CreateCollectAssetInfo(EBuildMode buildMode, AssetBundleCollectorGroup group, string assetPath, bool isRawAsset) { string address = GetAddress(group, assetPath); string bundleName = GetBundleName(group, assetPath); List assetTags = GetAssetTags(group); CollectAssetInfo collectAssetInfo = new CollectAssetInfo(CollectorType, bundleName, address, assetPath, assetTags, isRawAsset); // 注意:模拟构建模式下不需要收集依赖资源 if (buildMode == EBuildMode.SimulateBuild) collectAssetInfo.DependAssets = new List(); else collectAssetInfo.DependAssets = GetAllDependencies(assetPath); return collectAssetInfo; } private bool IsValidateAsset(string assetPath) { if (assetPath.StartsWith("Assets/") == false && assetPath.StartsWith("Packages/") == false) { UnityEngine.Debug.LogError($"Invalid asset path : {assetPath}"); return false; } // 忽略文件夹 if (AssetDatabase.IsValidFolder(assetPath)) return false; // 忽略编辑器下的类型资源 Type type = AssetDatabase.GetMainAssetTypeAtPath(assetPath); if (type == typeof(LightingDataAsset)) return false; // 忽略Unity无法识别的无效文件 /* if (type == typeof(UnityEditor.DefaultAsset)) { UnityEngine.Debug.LogWarning($"Cannot pack default asset : {assetPath}"); return false; } */ string fileExtension = System.IO.Path.GetExtension(assetPath); if (IsIgnoreFile(fileExtension)) return false; return true; } private bool IsIgnoreFile(string fileExtension) { foreach (var extension in YooAssetSettings.IgnoreFileExtensions) { if (extension == fileExtension) return true; } return false; } private bool IsCollectAsset(string assetPath) { Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath); if (assetType == typeof(UnityEngine.Shader)) return true; // 根据规则设置过滤资源文件 IFilterRule filterRuleInstance = AssetBundleCollectorSettingData.GetFilterRuleInstance(FilterRuleName); return filterRuleInstance.IsCollectAsset(new FilterRuleData(assetPath)); } private string GetAddress(AssetBundleCollectorGroup group, string assetPath) { if (CollectorType != ECollectorType.MainAssetCollector) return string.Empty; IAddressRule addressRuleInstance = AssetBundleCollectorSettingData.GetAddressRuleInstance(AddressRuleName); string adressValue = addressRuleInstance.GetAssetAddress(new AddressRuleData(assetPath, CollectPath, group.GroupName)); return adressValue; } private string GetBundleName(AssetBundleCollectorGroup group, string assetPath) { System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath); if (assetType == typeof(UnityEngine.Shader)) { return EditorTools.GetRegularPath(YooAssetSettings.UnityShadersBundleName).ToLower(); } // 根据规则设置获取资源包名称 IPackRule packRuleInstance = AssetBundleCollectorSettingData.GetPackRuleInstance(PackRuleName); string bundleName = packRuleInstance.GetBundleName(new PackRuleData(assetPath, CollectPath, group.GroupName)); return EditorTools.GetRegularPath(bundleName).ToLower(); } private List GetAssetTags(AssetBundleCollectorGroup group) { List tags = StringUtility.StringToStringList(group.AssetTags, ';'); List temper = StringUtility.StringToStringList(AssetTags, ';'); tags.AddRange(temper); return tags; } private List GetAllDependencies(string mainAssetPath) { List result = new List(); string[] depends = AssetDatabase.GetDependencies(mainAssetPath, true); foreach (string assetPath in depends) { if (IsValidateAsset(assetPath)) { // 注意:排除主资源对象 if (assetPath != mainAssetPath) result.Add(assetPath); } } return result; } } }