123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- using XGame.Framework.i18n;
- using XGame.Framework.Asset.Addressable;
- using XGame.Framework.Asset.Addressable.Data;
- using System.Collections.Generic;
- using System.IO;
- using System.Text.RegularExpressions;
- using UnityEditor;
- using UnityEngine;
- namespace XGame.Editor.Asset
- {
- public static partial class AddressableHelper
- {
- private static readonly AddressableClassify[] QueryOrder = { AddressableClassify.Product, AddressableClassify.BuiltIn, AddressableClassify.Tests };
- /// <summary>
- /// 检测AddressableId的唯一性
- /// </summary>
- //[MenuItem("Tools/AssetBundle/VerifyAddressableId")]
- public static void VerifyAddressableIdUniqueness()
- {
- var assetInfoMap = new Dictionary<long, AssetInfo>();
- VerifyAddressableIdUniqueness(LoadAssetManifest(AddressableClassify.BuiltIn), ref assetInfoMap);
- VerifyAddressableIdUniqueness(LoadAssetManifest(), ref assetInfoMap);
- VerifyAddressableIdUniqueness(LoadAssetManifest(AddressableClassify.Tests), ref assetInfoMap);
- Debug.Log("VerifyAddressableIdUniqueness completed.");
- }
- private static void VerifyAddressableIdUniqueness(AddressableInfosSo manifest, ref Dictionary<long, AssetInfo> assetInfoMap)
- {
- if (manifest == null || manifest.assetInfos == null)
- {
- //Debug.Log("Verify AddressableId Uniqueness. But can't find AddressableAssetInfoManifest.");
- return;
- }
- //key:addressableId
- ////var assetInfoMap = new Dictionary<long, AddressableAssetInfo>();
- foreach (var assetInfo in manifest.assetInfos)
- {
- if (assetInfoMap.ContainsKey(assetInfo.addressableId))
- {
- Debug.LogError($"AddressableId repeat. Last:{assetInfoMap[assetInfo.addressableId]} Next:{assetInfo}");
- }
- else
- {
- assetInfoMap.Add(assetInfo.addressableId, assetInfo);
- }
- }
- }
-
- /// <summary>
- /// 资源目录
- /// </summary>
- public static string[] GetAssetsRoots(AddressableClassify classify = AddressableClassify.Product)
- {
- var roots = new List<string>();
- switch (classify)
- {
- case AddressableClassify.BuiltIn:
- if (Directory.Exists(PathDefine.BuiltInResourcesRelative))
- {
- roots.Add(PathDefine.BuiltInResourcesRelative);
- }
- break;
- case AddressableClassify.Tests:
- if (Directory.Exists(PathDefine.TestsToAddressable))
- {
- roots.Add(PathDefine.TestsToAddressable);
- }
- if (Directory.Exists(PathDefine.BuiltInResTests))
- {
- roots.Add(PathDefine.BuiltInResTests);
- }
- break;
- default:
- return FileUtil.GetProductAssetsRoots(true);
- //{
- // if (Directory.Exists(PathDefine.ResAddressableRelative))
- // {
- // roots.Add(PathDefine.ResAddressableRelative);
- // }
- // if (Directory.Exists(PathDefine.ResourcesRelative))
- // {
- // roots.Add(PathDefine.ResourcesRelative);
- // }
- // if (Directory.Exists(PathDefine.I18nAssetsRelative))
- // {
- // var directorys = Directory.GetDirectories(PathDefine.I18nAssetsRelative, PathDefine.ResAddressableName, SearchOption.AllDirectories);
- // foreach(var directory in directorys)
- // {
- // roots.Add(directory.Replace("\\", "/"));
- // }
- // }
- //}
- //break;
- }
- return roots.ToArray();
- }
- public static bool TryAssetPathToClassify(string assetPath, out AddressableClassify classify)
- {
- classify = AddressableClassify.Product;
- if (string.IsNullOrEmpty(assetPath))
- {
- return false;
- }
- //assetPath = FileUtil.ToRelativePath(assetPath);
- if (IsValidPath(assetPath, AddressableClassify.Product))
- {
- //在 Res/Addressable || Resources 目录下
- classify = AddressableClassify.Product;
- return true;
- }
- if (IsValidPath(assetPath, AddressableClassify.BuiltIn))
- {
- //在Package Framework目录下
- classify = AddressableClassify.BuiltIn;
- return true;
- }
- if (IsValidPath(assetPath, AddressableClassify.Tests))
- {
- //在ResStatic目录下
- classify = AddressableClassify.Tests;
- return true;
- }
- return false;
- }
- public static LanguageType GetLanguageTypeByAssetPath(string assetPath)
- {
- if (string.IsNullOrEmpty(assetPath))
- {
- return LanguageType.NONE;
- }
- var match = i18nAssetRegex.Match(assetPath);
- if (match.Success)
- {
- var i18nName = match.Groups[1].Value;
- return LanguageUtils.ToLanguageType(i18nName);
- }
- return LanguageType.NONE;
- }
- /// <summary>
- /// i18n资源路径正则
- /// </summary>
- private static Regex i18nAssetRegex = new Regex(@"^Assets/i18n/(.+?)/Res/Addressable");
- /// <summary>
- /// 是否是国际化资源
- /// </summary>
- /// <param name="assetPath"></param>
- /// <returns></returns>
- public static bool IsI18nAssetPath(string assetPath)
- {
- if (string.IsNullOrEmpty(assetPath))
- {
- return false;
- }
- if (i18nAssetRegex.IsMatch(assetPath))
- {
- //在国际化资源目录下
- return true;
- }
- return false;
- }
- /// <summary>
- /// 检测路径是否是指定类型的资源路径
- /// </summary>
- /// <param name="assetPath"></param>
- /// <param name="classify"></param>
- /// <returns></returns>
- public static bool IsValidPath(string assetPath, AddressableClassify classify)
- {
- if (string.IsNullOrEmpty(assetPath))
- {
- return false;
- }
- switch (classify)
- {
- case AddressableClassify.BuiltIn:
- if (assetPath.StartsWith(PathDefine.XGamePackageRelative))
- {
- //在Package Framework目录下
- return true;
- }
- break;
- case AddressableClassify.Tests:
- if (assetPath.StartsWith(PathDefine.TestsToAddressable) || assetPath.StartsWith(PathDefine.BuiltInResTests))
- {
- //在ResStatic目录下 或者在 在Package ResStatic目录下
- return true;
- }
- break;
- default:
- if (assetPath.StartsWith(PathDefine.ResAddressableRelative))
- {
- //在Res/Addressable目录下
- return true;
- }
- if (assetPath.StartsWith(PathDefine.ResourcesRelative))
- {
- //在Resources目录下
- return true;
- }
- if (IsI18nAssetPath(assetPath))
- {
- //在国际化资源目录下
- return true;
- }
- break;
- }
- return false;
- }
- /// <summary>
- /// 检测一个资源路径是否有效
- /// 用于判断指定Asset是否可以生成AddressableName
- /// </summary>
- /// <param name="assetPath">使用Unity的相对路径</param>
- /// <returns></returns>
- public static bool IsValidPath(string assetPath)
- {
- if (string.IsNullOrEmpty(assetPath))
- {
- return false;
- }
- if (!File.Exists(assetPath))
- {
- Debug.LogWarning($"File can't find. Path:{assetPath}");
- return false;
- }
- assetPath = FileUtil.ToRelativePath(assetPath);
- if (IsValidPath(assetPath, AddressableClassify.Product))
- {
- //项目资源
- return true;
- }
- if (IsValidPath(assetPath, AddressableClassify.BuiltIn))
- {
- //在Package Framework目录下
- return true;
- }
- if (IsValidPath(assetPath, AddressableClassify.Tests))
- {
- //在ResStatic目录下
- return true;
- }
- return false;
- }
- public static bool IsInResources(string assetPath, out string relativePath)
- {
- relativePath = string.Empty;
- if (string.IsNullOrEmpty(assetPath))
- {
- return false;
- }
- if (!File.Exists(assetPath))
- {
- Debug.LogWarning($"File can't find. Path:{assetPath}");
- return false;
- }
- //项目资源
- assetPath = FileUtil.ToRelativePath(assetPath);
- var resRelative = $"{PathDefine.ResourcesRelative}/";
- if (assetPath.StartsWith(resRelative))
- {
- //在Resources目录下
- relativePath = assetPath.Substring(resRelative.Length);
- relativePath = relativePath.Replace(Path.GetExtension(relativePath), "");
- //Debug.LogWarning($"assetPath: {assetPath} GetDirectoryName: {Path.GetDirectoryName(assetPath)} ResourcesPath: {resRelative} file:{Path.GetFileNameWithoutExtension(assetPath)} relativePath:{relativePath}");
- return true;
- }
- //包内资源
- var pkgResources = $"{PathDefine.BuiltInResourcesRelative}/";
- if (assetPath.StartsWith(pkgResources))
- {
- //在Resources目录下
- relativePath = assetPath.Substring(pkgResources.Length);
- relativePath = relativePath.Replace(Path.GetExtension(relativePath), "");
- //Debug.LogWarning($"assetPath: {assetPath} GetDirectoryName: {Path.GetDirectoryName(assetPath)} ResourcesPath: {pkgResources} file:{Path.GetFileNameWithoutExtension(assetPath)} relativePath:{relativePath}");
- return true;
- }
- return false;
- }
- /// <summary>
- /// 取文件名
- /// 若名字为数字,则加上父文件夹名
- /// </summary>
- /// <param name="assetPath"></param>
- /// <returns></returns>
- public static string GetFileName(string assetPath)
- {
- if (string.IsNullOrEmpty(assetPath))
- return string.Empty;
- var fileName = Path.GetFileNameWithoutExtension(assetPath);
- // ReSharper disable once UnusedVariable
- if (FileUtil.IsNumberic(fileName, out var result))
- {
- fileName = $"{FileUtil.GetDirectoryName(assetPath, false)}_{fileName}";
- }
- //删除空格,将'.'转为'_',然后转小写
- fileName = fileName.Replace(" ", "").Replace('.', '_').ToLower();
- return fileName;
- }
- public static string GetFileNameIncludeI18n(string assetPath)
- {
- var i18nName = string.Empty;
- var langFlag = GetLanguageTypeByAssetPath(assetPath);
- if (langFlag != LanguageType.NONE)
- {
- i18nName = langFlag.ToString().ToLower();
- }
- var fileName = GetFileName(assetPath);
- if (langFlag != LanguageType.NONE)
- {
- fileName = $"{i18nName}_{fileName}";
- }
- return fileName;
- }
- /// <summary>
- /// GUID转AddressableName
- /// </summary>
- /// <param name="guid"></param>
- /// <returns></returns>
- public static string GUIDToName(string guid)
- {
- var addressableName = string.Empty;
- if (!string.IsNullOrEmpty(guid))
- {
- var assetPath = AssetDatabase.GUIDToAssetPath(guid);
- if (TryAssetPathToClassify(assetPath, out var classify))
- {
- LoadAssetManifest(classify)?.TryGetAddressableNameByGUID(guid, out addressableName);
- }
- }
- return addressableName;
- }
- /// <summary>
- /// AssetPath转AddressableName
- /// </summary>
- /// <param name="assetPath"></param>
- /// <returns></returns>
- public static string AssetPathToName(string assetPath)
- {
- var addressableName = string.Empty;
- if (TryAssetPathToClassify(assetPath, out var classify))
- {
- var guid = AssetDatabase.AssetPathToGUID(assetPath);
- LoadAssetManifest(classify)?.TryGetAddressableNameByGUID(guid, out addressableName);
- }
- return addressableName;
- }
- /// <summary>
- /// AddressableName转AssetPath
- /// </summary>
- /// <param name="addressableName"></param>
- /// <returns></returns>
- public static string NameToAssetPath(string addressableName)
- {
- if (!string.IsNullOrEmpty(addressableName))
- addressableName = addressableName.ToLower();
- for (int i = 0; i < QueryOrder.Length; i++)
- {
- var assetPath = NameToAssetPath(addressableName, QueryOrder[i]);
- if (!string.IsNullOrEmpty(assetPath))
- {
- return assetPath;
- }
- }
- return string.Empty;
- }
- private static string NameToAssetPath(string addressableName, AddressableClassify classify)
- {
- var manifest = LoadAssetManifest(classify);
- if (manifest != null && manifest.assetInfos != null)
- {
- var assetInfos = manifest.assetInfos;
- var index = System.Array.FindIndex(assetInfos, (info => info.addressableName.Equals(addressableName.ToLower())));
- if (index >= 0)
- {
- return assetInfos[index].GetAssetPath();
- }
- }
- return string.Empty;
- }
- /// <summary>
- /// LoadAsset
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="addressableName"></param>
- /// <returns></returns>
- public static T LoadAssetByName<T>(string addressableName) where T : Object
- {
- var assetPath = NameToAssetPath(addressableName);
- if (string.IsNullOrEmpty(assetPath))
- {
- return default(T);
- }
- return AssetDatabase.LoadAssetAtPath<T>(assetPath);
- }
- /// <summary>
- /// 加载源资源(非克隆对象)
- /// </summary>
- /// <param name="addressableName"></param>
- /// <typeparam name="T"></typeparam>
- /// <returns></returns>
- public static T LoadMainAssetByName<T>(string addressableName) where T : Object
- {
- var assetPath = NameToAssetPath(addressableName);
- if (string.IsNullOrEmpty(assetPath))
- {
- return default(T);
- }
- return AssetDatabase.LoadMainAssetAtPath(assetPath) as T;
- }
- public static bool IsValidName(string addressableName)
- {
- var assetPath = NameToAssetPath(addressableName);
- return string.IsNullOrEmpty(assetPath) == false;
- }
- /// <summary>
- /// 根据assetPath判断是否LOD资源
- /// </summary>
- /// <param name="assetPath"></param>
- /// <returns></returns>
- internal static bool IsLodAssetPath(string assetPath)
- {
- if (string.IsNullOrEmpty(assetPath)) return false;
- var fileName = Path.GetFileNameWithoutExtension(assetPath).ToLower();
- return fileName.EndsWith(PathDefine.LodHigh) || fileName.EndsWith(PathDefine.LodLow);
- }
- }
- }
|