mirror of
https://github.com/tuyoogame/YooAsset.git
synced 2026-06-18 18:03:42 +00:00
Compare commits
12 Commits
3.0.2-beta
...
3.0.3-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfb166ad91 | ||
|
|
f31e24bf59 | ||
|
|
868a186199 | ||
|
|
092af257d7 | ||
|
|
d39d9b59ef | ||
|
|
31c09d53bb | ||
|
|
a6299268d3 | ||
|
|
89b516942e | ||
|
|
8c49c27264 | ||
|
|
51c8c00604 | ||
|
|
cb96767f2d | ||
|
|
55d98dde02 |
@@ -2,6 +2,43 @@
|
||||
|
||||
All notable changes to this package will be documented in this file.
|
||||
|
||||
## [3.0.3-beta] - 2026-06-18
|
||||
|
||||
本版本重点优化资源清单二进制结构,显著减小清单体积、同时降低运行时内存占用并提升资源定位查询效率。。
|
||||
|
||||
### Added
|
||||
|
||||
- 新增 `AssetInfo.Tags` 资源标签访问接口
|
||||
|
||||
支持通过 `AssetInfo` 直接访问资源标签集合,便于在资源查询结果中判断和遍历标签。
|
||||
|
||||
- 新增 Bundle Collector 可编辑 Inspector 扩展
|
||||
|
||||
支持在 Inspector 面板中扩展和编辑 Bundle Collector 相关配置,方便自定义收集器配置界面。
|
||||
|
||||
### Changed
|
||||
|
||||
- 优化资源清单二进制结构
|
||||
|
||||
减小资源清单文件体积,降低运行时内存占用,并提升资源定位查询效率。
|
||||
|
||||
小型清单体积约减少 20%,重度项目预计可减少 35%~50%。
|
||||
|
||||
- 重构 `AssetReference`
|
||||
|
||||
调整资源引用相关实现,提升接口一致性和维护性,新增对 `Texture2D` 的支持。
|
||||
|
||||
- 调整编辑器缓存目录位置
|
||||
|
||||
编辑器模式下的缓存根目录调整到项目 `Library` 目录内,避免缓存文件直接生成在项目根目录,减少对工程目录的污染。
|
||||
|
||||
### Fixed
|
||||
|
||||
- (#752) 修复小游戏扩展类解密器枚举命名错误。
|
||||
|
||||
- (#660) 修复了UniTask扩展类里 IL2CPP 逆变委托崩溃。
|
||||
|
||||
|
||||
## [3.0.2-beta] - 2026-05-28
|
||||
|
||||
### Added
|
||||
|
||||
@@ -216,19 +216,19 @@ namespace YooAsset.Editor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建PackageBundle类
|
||||
/// 创建BuildBundle类
|
||||
/// </summary>
|
||||
internal PackageBundle CreatePackageBundle()
|
||||
internal BuildBundle CreateBuildBundle()
|
||||
{
|
||||
PackageBundle packageBundle = new PackageBundle();
|
||||
packageBundle.BundleName = BundleName;
|
||||
packageBundle.UnityCrc = PackageUnityCRC;
|
||||
packageBundle.FileHash = PackageFileHash;
|
||||
packageBundle.FileCrc = PackageFileCRC;
|
||||
packageBundle.FileSize = PackageFileSize;
|
||||
packageBundle.IsEncrypted = Encrypted;
|
||||
packageBundle.DependentBundleIDs = Array.Empty<int>();
|
||||
return packageBundle;
|
||||
BuildBundle buildBundle = new BuildBundle();
|
||||
buildBundle.BundleName = BundleName;
|
||||
buildBundle.UnityCrc = PackageUnityCRC;
|
||||
buildBundle.FileHash = PackageFileHash;
|
||||
buildBundle.FileCrc = PackageFileCRC;
|
||||
buildBundle.FileSize = PackageFileSize;
|
||||
buildBundle.IsEncrypted = Encrypted;
|
||||
buildBundle.DependentBundleIDs = Array.Empty<int>();
|
||||
return buildBundle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,8 +68,7 @@ namespace YooAsset.Editor
|
||||
{
|
||||
string bundleName = bundleInfo.BundleName;
|
||||
string fileHash = bundleInfo.PackageFileHash;
|
||||
string fileExtension = PackageManifestHelper.GetRemoteBundleFileExtension(bundleName);
|
||||
string fileName = PackageManifestHelper.GetRemoteBundleFileName(outputNameStyle, bundleName, fileExtension, fileHash);
|
||||
string fileName = BundleFileNaming.GetBundleFileName(outputNameStyle, bundleName, fileHash);
|
||||
bundleInfo.PackageDestFilePath = $"{packageOutputDirectory}/{fileName}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace YooAsset.Editor
|
||||
/// <summary>
|
||||
/// 拷贝首包资源文件
|
||||
/// </summary>
|
||||
internal void CopyBundledFilesToStreaming(BuildParametersContext buildParametersContext, PackageManifest manifest)
|
||||
internal void CopyBundledFilesToStreaming(BuildParametersContext buildParametersContext, BuildManifest manifest)
|
||||
{
|
||||
EBundledCopyOption copyOption = buildParametersContext.Parameters.BundledCopyOption;
|
||||
string packageOutputDirectory = buildParametersContext.GetPackageOutputDirectory();
|
||||
@@ -67,10 +67,10 @@ namespace YooAsset.Editor
|
||||
// 拷贝文件列表(所有文件)
|
||||
if (copyOption == EBundledCopyOption.ClearAndCopyAll || copyOption == EBundledCopyOption.OnlyCopyAll)
|
||||
{
|
||||
foreach (var packageBundle in manifest.BundleList)
|
||||
foreach (var buildBundle in manifest.BundleList)
|
||||
{
|
||||
string sourcePath = $"{packageOutputDirectory}/{packageBundle.GetFileName()}";
|
||||
string destPath = $"{bundledRootDirectory}/{packageBundle.GetFileName()}";
|
||||
string sourcePath = $"{packageOutputDirectory}/{buildBundle.GetFileName(manifest.OutputNameStyle)}";
|
||||
string destPath = $"{bundledRootDirectory}/{buildBundle.GetFileName(manifest.OutputNameStyle)}";
|
||||
EditorFileUtility.CopyFile(sourcePath, destPath, true);
|
||||
}
|
||||
}
|
||||
@@ -85,12 +85,12 @@ namespace YooAsset.Editor
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
string[] tags = copyParams.Split(';');
|
||||
foreach (var packageBundle in manifest.BundleList)
|
||||
foreach (var buildBundle in manifest.BundleList)
|
||||
{
|
||||
if (packageBundle.HasAnyTag(tags) == false)
|
||||
if (buildBundle.HasAnyTag(tags) == false)
|
||||
continue;
|
||||
string sourcePath = $"{packageOutputDirectory}/{packageBundle.GetFileName()}";
|
||||
string destPath = $"{bundledRootDirectory}/{packageBundle.GetFileName()}";
|
||||
string sourcePath = $"{packageOutputDirectory}/{buildBundle.GetFileName(manifest.OutputNameStyle)}";
|
||||
string destPath = $"{bundledRootDirectory}/{buildBundle.GetFileName(manifest.OutputNameStyle)}";
|
||||
EditorFileUtility.CopyFile(sourcePath, destPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace YooAsset.Editor
|
||||
[ContextObject]
|
||||
public class ManifestContext
|
||||
{
|
||||
internal PackageManifest Manifest;
|
||||
internal BuildManifest Manifest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,7 +44,7 @@ namespace YooAsset.Editor
|
||||
CheckBundleHashConflict(buildMapContext);
|
||||
|
||||
// 创建新补丁清单
|
||||
PackageManifest manifest = new PackageManifest();
|
||||
BuildManifest manifest = new BuildManifest();
|
||||
manifest.FileVersion = PackageManifestConsts.FileVersion;
|
||||
manifest.EnableAddressable = buildMapContext.Command.EnableAddressable;
|
||||
manifest.SupportExtensionless = buildMapContext.Command.SupportExtensionless;
|
||||
@@ -74,20 +74,15 @@ namespace YooAsset.Editor
|
||||
// 4. 处理首包资源包
|
||||
if (processBundleDepends)
|
||||
{
|
||||
// 注意:初始化资源清单建立引用关系
|
||||
manifest.Initialize();
|
||||
// 注意:建立资源包引用关系图
|
||||
manifest.BuildReferrerGraph();
|
||||
|
||||
ProcessBuiltinBundleDependency(context, manifest);
|
||||
}
|
||||
|
||||
|
||||
// 创建资源清单文本文件
|
||||
{
|
||||
string fileName = YooAssetConfiguration.GetManifestJsonFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
||||
string filePath = $"{packageOutputDirectory}/{fileName}";
|
||||
PackageManifestHelper.SerializeManifestToJson(filePath, manifest);
|
||||
BuildLogger.Log($"Create package manifest file: '{filePath}'.");
|
||||
}
|
||||
// 5. 构建全局标签表与目录表
|
||||
manifest.BuildTagTable();
|
||||
manifest.BuildDirectoryTable();
|
||||
|
||||
// 创建资源清单二进制文件
|
||||
string packageHash;
|
||||
@@ -119,8 +114,7 @@ namespace YooAsset.Editor
|
||||
// 填充上下文
|
||||
{
|
||||
ManifestContext manifestContext = new ManifestContext();
|
||||
byte[] bytesData = FileUtility.ReadAllBytes(packagePath);
|
||||
manifestContext.Manifest = PackageManifestHelper.DeserializeManifestFromBinary(bytesData, buildParameters.ManifestDecryptor);
|
||||
manifestContext.Manifest = manifest;
|
||||
context.SetContextObject(manifestContext);
|
||||
}
|
||||
}
|
||||
@@ -158,21 +152,21 @@ namespace YooAsset.Editor
|
||||
/// <summary>
|
||||
/// 创建资源对象列表
|
||||
/// </summary>
|
||||
private List<PackageAsset> CreatePackageAssetList(BuildMapContext buildMapContext)
|
||||
private List<BuildAsset> CreatePackageAssetList(BuildMapContext buildMapContext)
|
||||
{
|
||||
List<PackageAsset> result = new List<PackageAsset>(1000);
|
||||
List<BuildAsset> result = new List<BuildAsset>(1000);
|
||||
foreach (var bundleInfo in buildMapContext.Collection)
|
||||
{
|
||||
var assetInfos = bundleInfo.GetAllManifestAssetInfos();
|
||||
foreach (var assetInfo in assetInfos)
|
||||
{
|
||||
PackageAsset packageAsset = new PackageAsset();
|
||||
packageAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty;
|
||||
packageAsset.AssetPath = assetInfo.AssetInfo.AssetPath;
|
||||
packageAsset.AssetGuid = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetInfo.AssetGUID : string.Empty;
|
||||
packageAsset.AssetTags = assetInfo.AssetTags.ToArray();
|
||||
packageAsset.EditorUserData = assetInfo;
|
||||
result.Add(packageAsset);
|
||||
BuildAsset buildAsset = new BuildAsset();
|
||||
buildAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty;
|
||||
buildAsset.AssetPath = assetInfo.AssetInfo.AssetPath;
|
||||
buildAsset.AssetGuid = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetInfo.AssetGUID : string.Empty;
|
||||
buildAsset.Tags = assetInfo.AssetTags.ToArray();
|
||||
buildAsset.EditorUserData = assetInfo;
|
||||
result.Add(buildAsset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,13 +178,13 @@ namespace YooAsset.Editor
|
||||
/// <summary>
|
||||
/// 创建资源包列表
|
||||
/// </summary>
|
||||
private List<PackageBundle> CreatePackageBundleList(BuildMapContext buildMapContext)
|
||||
private List<BuildBundle> CreatePackageBundleList(BuildMapContext buildMapContext)
|
||||
{
|
||||
List<PackageBundle> result = new List<PackageBundle>(1000);
|
||||
List<BuildBundle> result = new List<BuildBundle>(1000);
|
||||
foreach (var bundleInfo in buildMapContext.Collection)
|
||||
{
|
||||
var packageBundle = bundleInfo.CreatePackageBundle();
|
||||
result.Add(packageBundle);
|
||||
var buildBundle = bundleInfo.CreateBuildBundle();
|
||||
result.Add(buildBundle);
|
||||
}
|
||||
|
||||
// 按照BundleName排序
|
||||
@@ -201,7 +195,7 @@ namespace YooAsset.Editor
|
||||
/// <summary>
|
||||
/// 处理资源清单的资源对象列表
|
||||
/// </summary>
|
||||
private void ProcessPackageAsset(PackageManifest manifest)
|
||||
private void ProcessPackageAsset(BuildManifest manifest)
|
||||
{
|
||||
// 注意:优先缓存资源包索引
|
||||
for (int index = 0; index < manifest.BundleList.Count; index++)
|
||||
@@ -211,31 +205,31 @@ namespace YooAsset.Editor
|
||||
}
|
||||
|
||||
// 记录资源对象所属的资源包ID
|
||||
foreach (var packageAsset in manifest.AssetList)
|
||||
foreach (var buildAsset in manifest.AssetList)
|
||||
{
|
||||
var assetInfo = packageAsset.EditorUserData as BuildAssetInfo;
|
||||
packageAsset.BundleID = GetCachedBundleIndexID(assetInfo.BundleName);
|
||||
var assetInfo = buildAsset.EditorUserData as BuildAssetInfo;
|
||||
buildAsset.BundleID = GetCachedBundleIndexID(assetInfo.BundleName);
|
||||
}
|
||||
|
||||
// 记录资源对象依赖的资源包ID集合
|
||||
// 注意:依赖关系非引擎构建结果里查询!
|
||||
foreach (var packageAsset in manifest.AssetList)
|
||||
foreach (var buildAsset in manifest.AssetList)
|
||||
{
|
||||
var mainAssetInfo = packageAsset.EditorUserData as BuildAssetInfo;
|
||||
packageAsset.DependentBundleIDs = GetAssetDependBundleIDs(mainAssetInfo);
|
||||
var mainAssetInfo = buildAsset.EditorUserData as BuildAssetInfo;
|
||||
buildAsset.DependentBundleIDs = GetAssetDependBundleIDs(mainAssetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理资源包的依赖集合
|
||||
/// </summary>
|
||||
private void ProcessBundleDepends(BuildContext context, PackageManifest manifest)
|
||||
private void ProcessBundleDepends(BuildContext context, BuildManifest manifest)
|
||||
{
|
||||
// 查询引擎生成的资源包依赖关系,然后记录到清单
|
||||
foreach (var packageBundle in manifest.BundleList)
|
||||
foreach (var buildBundle in manifest.BundleList)
|
||||
{
|
||||
int mainBundleID = GetCachedBundleIndexID(packageBundle.BundleName);
|
||||
string[] dependNames = GetBundleDepends(context, packageBundle.BundleName);
|
||||
int mainBundleID = GetCachedBundleIndexID(buildBundle.BundleName);
|
||||
string[] dependNames = GetBundleDepends(context, buildBundle.BundleName);
|
||||
List<int> dependIDs = new List<int>(dependNames.Length);
|
||||
foreach (var dependName in dependNames)
|
||||
{
|
||||
@@ -246,29 +240,29 @@ namespace YooAsset.Editor
|
||||
|
||||
// 排序并填充数据
|
||||
dependIDs.Sort();
|
||||
packageBundle.DependentBundleIDs = dependIDs.ToArray();
|
||||
buildBundle.DependentBundleIDs = dependIDs.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理资源包的标签集合
|
||||
/// </summary>
|
||||
private void ProcessBundleTags(PackageManifest manifest)
|
||||
private void ProcessBundleTags(BuildManifest manifest)
|
||||
{
|
||||
foreach (var packageBundle in manifest.BundleList)
|
||||
foreach (var buildBundle in manifest.BundleList)
|
||||
{
|
||||
packageBundle.Tags = Array.Empty<string>();
|
||||
buildBundle.Tags = Array.Empty<string>();
|
||||
}
|
||||
|
||||
// 将主资源的标签信息传染给其依赖的资源包集合
|
||||
foreach (var packageAsset in manifest.AssetList)
|
||||
foreach (var buildAsset in manifest.AssetList)
|
||||
{
|
||||
var assetTags = packageAsset.AssetTags;
|
||||
int bundleID = packageAsset.BundleID;
|
||||
var assetTags = buildAsset.Tags;
|
||||
int bundleID = buildAsset.BundleID;
|
||||
CacheBundleTags(bundleID, assetTags);
|
||||
if (packageAsset.DependentBundleIDs != null)
|
||||
if (buildAsset.DependentBundleIDs != null)
|
||||
{
|
||||
foreach (var dependBundleID in packageAsset.DependentBundleIDs)
|
||||
foreach (var dependBundleID in buildAsset.DependentBundleIDs)
|
||||
{
|
||||
CacheBundleTags(dependBundleID, assetTags);
|
||||
}
|
||||
@@ -278,15 +272,15 @@ namespace YooAsset.Editor
|
||||
// 将缓存的资源标签赋值给资源包
|
||||
for (int index = 0; index < manifest.BundleList.Count; index++)
|
||||
{
|
||||
var packageBundle = manifest.BundleList[index];
|
||||
var buildBundle = manifest.BundleList[index];
|
||||
if (_cacheBundleTags.TryGetValue(index, out var value))
|
||||
{
|
||||
packageBundle.Tags = value.ToArray();
|
||||
buildBundle.Tags = value.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 注意:SBP构建管线会自动剔除一些冗余资源的引用关系,导致游离资源包没有被任何主资源包引用。
|
||||
string warning = BuildLogger.GetErrorMessage(ErrorCode.FoundStrayBundle, $"Found stray bundle. Bundle ID: {index}, bundle name: '{packageBundle.BundleName}'.");
|
||||
string warning = BuildLogger.GetErrorMessage(ErrorCode.FoundStrayBundle, $"Found stray bundle. Bundle ID: {index}, bundle name: '{buildBundle.BundleName}'.");
|
||||
BuildLogger.Warning(warning);
|
||||
}
|
||||
}
|
||||
@@ -324,7 +318,7 @@ namespace YooAsset.Editor
|
||||
}
|
||||
|
||||
#region YOOASSET_LEGACY_DEPENDENCY
|
||||
private void ProcessBuiltinBundleDependency(BuildContext context, PackageManifest manifest)
|
||||
private void ProcessBuiltinBundleDependency(BuildContext context, BuildManifest manifest)
|
||||
{
|
||||
// 注意:如果是可编程构建管线,需要补充首包资源包
|
||||
// 注意:该步骤依赖前面的操作!
|
||||
@@ -356,7 +350,7 @@ namespace YooAsset.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
private void ProcessBuiltinBundleReference(PackageManifest manifest, string builtinBundleName)
|
||||
private void ProcessBuiltinBundleReference(BuildManifest manifest, string builtinBundleName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(builtinBundleName))
|
||||
return;
|
||||
@@ -367,23 +361,23 @@ namespace YooAsset.Editor
|
||||
|
||||
// 获取首包资源包
|
||||
int builtinBundleID = GetCachedBundleIndexID(builtinBundleName);
|
||||
var builtinPackageBundle = manifest.BundleList[builtinBundleID];
|
||||
var builtinBuildBundle = manifest.BundleList[builtinBundleID];
|
||||
|
||||
// 更新依赖资源包ID集合
|
||||
HashSet<int> cacheBundleIDs = new HashSet<int>(builtinPackageBundle.ReferrerBundleIDs);
|
||||
HashSet<int> cacheBundleIDs = new HashSet<int>(builtinBuildBundle.ReferrerBundleIDs);
|
||||
HashSet<string> tempTags = new HashSet<string>();
|
||||
foreach (var packageAsset in manifest.AssetList)
|
||||
foreach (var buildAsset in manifest.AssetList)
|
||||
{
|
||||
if (cacheBundleIDs.Contains(packageAsset.BundleID))
|
||||
if (cacheBundleIDs.Contains(buildAsset.BundleID))
|
||||
{
|
||||
if (packageAsset.DependentBundleIDs.Contains(builtinBundleID) == false)
|
||||
if (buildAsset.DependentBundleIDs.Contains(builtinBundleID) == false)
|
||||
{
|
||||
var tempBundleIDs = new List<int>(packageAsset.DependentBundleIDs);
|
||||
var tempBundleIDs = new List<int>(buildAsset.DependentBundleIDs);
|
||||
tempBundleIDs.Add(builtinBundleID);
|
||||
packageAsset.DependentBundleIDs = tempBundleIDs.ToArray();
|
||||
buildAsset.DependentBundleIDs = tempBundleIDs.ToArray();
|
||||
}
|
||||
|
||||
foreach (var tag in packageAsset.AssetTags)
|
||||
foreach (var tag in buildAsset.Tags)
|
||||
{
|
||||
if (tempTags.Contains(tag) == false)
|
||||
tempTags.Add(tag);
|
||||
@@ -392,12 +386,12 @@ namespace YooAsset.Editor
|
||||
}
|
||||
|
||||
// 更新首包资源包的标签集合
|
||||
foreach (var tag in builtinPackageBundle.Tags)
|
||||
foreach (var tag in builtinBuildBundle.Tags)
|
||||
{
|
||||
if (tempTags.Contains(tag) == false)
|
||||
tempTags.Add(tag);
|
||||
}
|
||||
builtinPackageBundle.Tags = tempTags.ToArray();
|
||||
builtinBuildBundle.Tags = tempTags.ToArray();
|
||||
}
|
||||
private int[] GetAssetDependBundleIDs(BuildAssetInfo mainAssetInfo)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace YooAsset.Editor
|
||||
var buildParameters = buildParametersContext.Parameters;
|
||||
|
||||
string packageOutputDirectory = buildParametersContext.GetPackageOutputDirectory();
|
||||
PackageManifest manifest = manifestContext.Manifest;
|
||||
BuildManifest manifest = manifestContext.Manifest;
|
||||
BuildReport buildReport = new BuildReport();
|
||||
|
||||
// 概述信息
|
||||
@@ -89,36 +89,36 @@ namespace YooAsset.Editor
|
||||
|
||||
// 资源对象列表
|
||||
buildReport.AssetInfos = new List<ReportAssetInfo>(manifest.AssetList.Count);
|
||||
foreach (var packageAsset in manifest.AssetList)
|
||||
foreach (var buildAsset in manifest.AssetList)
|
||||
{
|
||||
var mainBundle = manifest.BundleList[packageAsset.BundleID];
|
||||
var mainBundle = manifest.BundleList[buildAsset.BundleID];
|
||||
ReportAssetInfo reportAssetInfo = new ReportAssetInfo();
|
||||
reportAssetInfo.Address = packageAsset.Address;
|
||||
reportAssetInfo.AssetPath = packageAsset.AssetPath;
|
||||
reportAssetInfo.AssetTags = packageAsset.AssetTags;
|
||||
reportAssetInfo.AssetGuid = AssetDatabase.AssetPathToGUID(packageAsset.AssetPath);
|
||||
reportAssetInfo.Address = buildAsset.Address;
|
||||
reportAssetInfo.AssetPath = buildAsset.AssetPath;
|
||||
reportAssetInfo.AssetTags = buildAsset.Tags;
|
||||
reportAssetInfo.AssetGuid = AssetDatabase.AssetPathToGUID(buildAsset.AssetPath);
|
||||
reportAssetInfo.MainBundleName = mainBundle.BundleName;
|
||||
reportAssetInfo.MainBundleSize = mainBundle.FileSize;
|
||||
reportAssetInfo.DependAssets = GetAssetDependAssets(buildMapContext, mainBundle.BundleName, packageAsset.AssetPath);
|
||||
reportAssetInfo.DependBundles = GetAssetDependBundles(manifest, packageAsset);
|
||||
reportAssetInfo.DependAssets = GetAssetDependAssets(buildMapContext, mainBundle.BundleName, buildAsset.AssetPath);
|
||||
reportAssetInfo.DependBundles = GetAssetDependBundles(manifest, buildAsset);
|
||||
buildReport.AssetInfos.Add(reportAssetInfo);
|
||||
}
|
||||
|
||||
// 资源包列表
|
||||
buildReport.BundleInfos = new List<ReportBundleInfo>(manifest.BundleList.Count);
|
||||
foreach (var packageBundle in manifest.BundleList)
|
||||
foreach (var buildBundle in manifest.BundleList)
|
||||
{
|
||||
ReportBundleInfo reportBundleInfo = new ReportBundleInfo();
|
||||
reportBundleInfo.BundleName = packageBundle.BundleName;
|
||||
reportBundleInfo.FileName = packageBundle.GetFileName();
|
||||
reportBundleInfo.FileHash = packageBundle.FileHash;
|
||||
reportBundleInfo.FileCrc = packageBundle.FileCrc;
|
||||
reportBundleInfo.FileSize = packageBundle.FileSize;
|
||||
reportBundleInfo.Encrypted = packageBundle.IsEncrypted;
|
||||
reportBundleInfo.Tags = packageBundle.Tags;
|
||||
reportBundleInfo.DependBundles = GetBundleDependBundles(manifest, packageBundle);
|
||||
reportBundleInfo.ReferenceBundles = GetBundleReferenceBundles(manifest, packageBundle);
|
||||
reportBundleInfo.BundleContents = GetBundleContents(buildMapContext, packageBundle.BundleName);
|
||||
reportBundleInfo.BundleName = buildBundle.BundleName;
|
||||
reportBundleInfo.FileName = buildBundle.GetFileName(manifest.OutputNameStyle);
|
||||
reportBundleInfo.FileHash = buildBundle.FileHash;
|
||||
reportBundleInfo.FileCrc = buildBundle.FileCrc;
|
||||
reportBundleInfo.FileSize = buildBundle.FileSize;
|
||||
reportBundleInfo.Encrypted = buildBundle.IsEncrypted;
|
||||
reportBundleInfo.Tags = buildBundle.Tags;
|
||||
reportBundleInfo.DependBundles = GetBundleDependBundles(manifest, buildBundle);
|
||||
reportBundleInfo.ReferenceBundles = GetBundleReferenceBundles(manifest, buildBundle);
|
||||
reportBundleInfo.BundleContents = GetBundleContents(buildMapContext, buildBundle.BundleName);
|
||||
buildReport.BundleInfos.Add(reportBundleInfo);
|
||||
}
|
||||
|
||||
@@ -151,10 +151,10 @@ namespace YooAsset.Editor
|
||||
/// <summary>
|
||||
/// 获取资源对象依赖的资源包集合
|
||||
/// </summary>
|
||||
private List<string> GetAssetDependBundles(PackageManifest manifest, PackageAsset packageAsset)
|
||||
private List<string> GetAssetDependBundles(BuildManifest manifest, BuildAsset buildAsset)
|
||||
{
|
||||
List<string> dependBundles = new List<string>(packageAsset.DependentBundleIDs.Length);
|
||||
foreach (int index in packageAsset.DependentBundleIDs)
|
||||
List<string> dependBundles = new List<string>(buildAsset.DependentBundleIDs.Length);
|
||||
foreach (int index in buildAsset.DependentBundleIDs)
|
||||
{
|
||||
string dependBundleName = manifest.BundleList[index].BundleName;
|
||||
dependBundles.Add(dependBundleName);
|
||||
@@ -166,10 +166,10 @@ namespace YooAsset.Editor
|
||||
/// <summary>
|
||||
/// 获取资源包依赖的资源包集合
|
||||
/// </summary>
|
||||
private List<string> GetBundleDependBundles(PackageManifest manifest, PackageBundle packageBundle)
|
||||
private List<string> GetBundleDependBundles(BuildManifest manifest, BuildBundle buildBundle)
|
||||
{
|
||||
List<string> dependBundles = new List<string>(packageBundle.DependentBundleIDs.Length);
|
||||
foreach (int index in packageBundle.DependentBundleIDs)
|
||||
List<string> dependBundles = new List<string>(buildBundle.DependentBundleIDs.Length);
|
||||
foreach (int index in buildBundle.DependentBundleIDs)
|
||||
{
|
||||
string dependBundleName = manifest.BundleList[index].BundleName;
|
||||
dependBundles.Add(dependBundleName);
|
||||
@@ -181,10 +181,10 @@ namespace YooAsset.Editor
|
||||
/// <summary>
|
||||
/// 获取引用该资源包的资源包集合
|
||||
/// </summary>
|
||||
private List<string> GetBundleReferenceBundles(PackageManifest manifest, PackageBundle packageBundle)
|
||||
private List<string> GetBundleReferenceBundles(BuildManifest manifest, BuildBundle buildBundle)
|
||||
{
|
||||
List<string> referenceBundles = new List<string>(packageBundle.ReferrerBundleIDs.Count);
|
||||
foreach (int index in packageBundle.ReferrerBundleIDs)
|
||||
List<string> referenceBundles = new List<string>(buildBundle.ReferrerBundleIDs.Count);
|
||||
foreach (int index in buildBundle.ReferrerBundleIDs)
|
||||
{
|
||||
string dependBundleName = manifest.BundleList[index].BundleName;
|
||||
referenceBundles.Add(dependBundleName);
|
||||
@@ -204,15 +204,15 @@ namespace YooAsset.Editor
|
||||
return result;
|
||||
}
|
||||
|
||||
private int GetMainAssetCount(PackageManifest manifest)
|
||||
private int GetMainAssetCount(BuildManifest manifest)
|
||||
{
|
||||
return manifest.AssetList.Count;
|
||||
}
|
||||
private int GetAllBundleCount(PackageManifest manifest)
|
||||
private int GetAllBundleCount(BuildManifest manifest)
|
||||
{
|
||||
return manifest.BundleList.Count;
|
||||
}
|
||||
private long GetAllBundleSize(PackageManifest manifest)
|
||||
private long GetAllBundleSize(BuildManifest manifest)
|
||||
{
|
||||
long fileBytes = 0;
|
||||
foreach (var packageBundle in manifest.BundleList)
|
||||
@@ -221,7 +221,7 @@ namespace YooAsset.Editor
|
||||
}
|
||||
return fileBytes;
|
||||
}
|
||||
private int GetEncryptedBundleCount(PackageManifest manifest)
|
||||
private int GetEncryptedBundleCount(BuildManifest manifest)
|
||||
{
|
||||
int fileCount = 0;
|
||||
foreach (var packageBundle in manifest.BundleList)
|
||||
@@ -231,7 +231,7 @@ namespace YooAsset.Editor
|
||||
}
|
||||
return fileCount;
|
||||
}
|
||||
private long GetEncryptedBundleSize(PackageManifest manifest)
|
||||
private long GetEncryptedBundleSize(BuildManifest manifest)
|
||||
{
|
||||
long fileBytes = 0;
|
||||
foreach (var packageBundle in manifest.BundleList)
|
||||
|
||||
@@ -61,8 +61,7 @@ namespace YooAsset.Editor
|
||||
{
|
||||
string bundleName = bundleInfo.BundleName;
|
||||
string fileHash = bundleInfo.PackageFileHash;
|
||||
string fileExtension = PackageManifestHelper.GetRemoteBundleFileExtension(bundleName);
|
||||
string fileName = PackageManifestHelper.GetRemoteBundleFileName(outputNameStyle, bundleName, fileExtension, fileHash);
|
||||
string fileName = BundleFileNaming.GetBundleFileName(outputNameStyle, bundleName, fileHash);
|
||||
bundleInfo.PackageDestFilePath = $"{packageOutputDirectory}/{fileName}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +175,17 @@ namespace YooAsset.Editor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 工程中是否已存在收集器配置文件
|
||||
/// </summary>
|
||||
/// <returns>存在返回 true</returns>
|
||||
public static bool HasSettingAsset()
|
||||
{
|
||||
string typeName = nameof(BundleCollectorSetting);
|
||||
var guids = AssetDatabase.FindAssets($"t:{typeName}");
|
||||
return guids != null && guids.Length > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存储配置文件
|
||||
/// </summary>
|
||||
|
||||
@@ -19,10 +19,30 @@ namespace YooAsset.Editor
|
||||
/// </summary>
|
||||
[MenuItem("YooAsset/Bundle Collector", false, 101)]
|
||||
public static void OpenWindow()
|
||||
{
|
||||
OpenWindowInternal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开资源收集器窗口并定位到指定的收集器
|
||||
/// </summary>
|
||||
/// <param name="packageName">包裹名称</param>
|
||||
/// <param name="groupName">分组名称</param>
|
||||
/// <param name="collectPath">收集路径</param>
|
||||
public static void OpenWindow(string packageName, string groupName, string collectPath)
|
||||
{
|
||||
BundleCollectorWindow window = OpenWindowInternal();
|
||||
window.SetFocusCollector(packageName, groupName, collectPath);
|
||||
window.RefreshWindow();
|
||||
window.Focus();
|
||||
}
|
||||
|
||||
private static BundleCollectorWindow OpenWindowInternal()
|
||||
{
|
||||
Type[] dockedTypes = EditorWindowDefine.GetDockedWindowTypes();
|
||||
BundleCollectorWindow window = GetWindow<BundleCollectorWindow>("Bundle Collector", true, dockedTypes);
|
||||
window.minSize = new Vector2(800, 600);
|
||||
return window;
|
||||
}
|
||||
|
||||
private const string PlaceholderClass = "search-placeholder";
|
||||
@@ -77,6 +97,7 @@ namespace YooAsset.Editor
|
||||
private int _highlightCollectorIndex = -1;
|
||||
private int _lastModifyPackageIndex = 0;
|
||||
private int _lastModifyGroupIndex = 0;
|
||||
private bool _hasFocusCollector = false;
|
||||
private bool _showGlobalSettings = false;
|
||||
private bool _showPackageSettings = false;
|
||||
|
||||
@@ -471,7 +492,8 @@ namespace YooAsset.Editor
|
||||
private void RefreshWindow()
|
||||
{
|
||||
_highlightAssetPath = null;
|
||||
_highlightCollectorIndex = -1;
|
||||
if (_hasFocusCollector == false)
|
||||
_highlightCollectorIndex = -1;
|
||||
_groupContainer.visible = false;
|
||||
_collectorContainer.visible = false;
|
||||
|
||||
@@ -508,6 +530,7 @@ namespace YooAsset.Editor
|
||||
{
|
||||
_highlightAssetPath = null;
|
||||
_highlightCollectorIndex = -1;
|
||||
_hasFocusCollector = false;
|
||||
FillCollectorViewData();
|
||||
|
||||
string searchInput = GetSearchInput();
|
||||
@@ -577,6 +600,46 @@ namespace YooAsset.Editor
|
||||
return ruleDisplayName.ClassName;
|
||||
}
|
||||
|
||||
// 焦点相关
|
||||
private void SetFocusCollector(string packageName, string groupName, string collectPath)
|
||||
{
|
||||
var packages = BundleCollectorSettingData.Setting.Packages;
|
||||
int packageIndex = packages.FindIndex(item => item.PackageName == packageName);
|
||||
if (packageIndex < 0)
|
||||
{
|
||||
Debug.LogWarning($"Package not found: '{packageName}'.");
|
||||
_highlightCollectorIndex = -1;
|
||||
_hasFocusCollector = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var package = packages[packageIndex];
|
||||
int groupIndex = package.Groups.FindIndex(item => item.GroupName == groupName);
|
||||
if (groupIndex < 0)
|
||||
{
|
||||
Debug.LogWarning($"Group not found: '{groupName}' in package '{packageName}'.");
|
||||
_highlightCollectorIndex = -1;
|
||||
_hasFocusCollector = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var group = package.Groups[groupIndex];
|
||||
int collectorIndex = group.Collectors.FindIndex(item => string.Equals(item.CollectPath, collectPath, StringComparison.OrdinalIgnoreCase));
|
||||
if (collectorIndex < 0)
|
||||
{
|
||||
Debug.LogWarning($"Collector not found: '{collectPath}'.");
|
||||
_highlightCollectorIndex = -1;
|
||||
_hasFocusCollector = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_highlightAssetPath = null;
|
||||
_highlightCollectorIndex = collectorIndex;
|
||||
_lastModifyPackageIndex = packageIndex;
|
||||
_lastModifyGroupIndex = groupIndex;
|
||||
_hasFocusCollector = true;
|
||||
}
|
||||
|
||||
// 搜索栏相关
|
||||
private void ShowSearchResult(string message, Color color)
|
||||
{
|
||||
@@ -898,6 +961,7 @@ namespace YooAsset.Editor
|
||||
if (foldout != null)
|
||||
foldout.value = true;
|
||||
_highlightCollectorIndex = -1;
|
||||
_hasFocusCollector = false;
|
||||
}
|
||||
}
|
||||
private VisualElement MakeCollectorListViewItem()
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace YooAsset
|
||||
{
|
||||
if (options.Manifest.TryGetPackageBundleByBundleGuid(entry.BundleGuid, out PackageBundle bundle))
|
||||
{
|
||||
if (bundle.HasAnyTag(tags))
|
||||
if (bundle.Tags.HasAnyTag(tags))
|
||||
bundleGuids.Add(bundle.BundleGuid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,44 +29,14 @@ namespace YooAsset
|
||||
public bool IsEncrypted => _bundle.IsEncrypted;
|
||||
|
||||
/// <summary>
|
||||
/// 分类标签数量
|
||||
/// 分类标签集合
|
||||
/// </summary>
|
||||
public int TagCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _bundle.Tags == null ? 0 : _bundle.Tags.Length;
|
||||
}
|
||||
}
|
||||
public PackageTags Tags => _bundle.Tags;
|
||||
|
||||
internal BundleUnpackInfo(PackageBundle bundle)
|
||||
{
|
||||
_bundle = bundle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定索引的分类标签
|
||||
/// </summary>
|
||||
public string GetTag(int index)
|
||||
{
|
||||
return _bundle.Tags[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定的单个标签
|
||||
/// </summary>
|
||||
public bool HasTag(string tag)
|
||||
{
|
||||
return _bundle.HasTag(tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定标签数组中的任意一个
|
||||
/// </summary>
|
||||
public bool HasAnyTag(string[] tags)
|
||||
{
|
||||
return _bundle.HasAnyTag(tags);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -125,6 +125,19 @@ namespace YooAsset
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源标签集合
|
||||
/// </summary>
|
||||
public PackageTags Tags
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_packageAsset == null)
|
||||
return PackageTags.Empty;
|
||||
return _packageAsset.Tags;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建有效的资源信息实例
|
||||
/// </summary>
|
||||
|
||||
8
Assets/YooAsset/Runtime/ResourcePackage/BuildModel.meta
Normal file
8
Assets/YooAsset/Runtime/ResourcePackage/BuildModel.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b097205d69d7e8348968898bc0067ee6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建期专用的资源描述
|
||||
/// </summary>
|
||||
internal class BuildAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 可寻址地址
|
||||
/// </summary>
|
||||
public string Address;
|
||||
|
||||
/// <summary>
|
||||
/// 资源路径
|
||||
/// </summary>
|
||||
public string AssetPath;
|
||||
|
||||
/// <summary>
|
||||
/// 资源GUID
|
||||
/// </summary>
|
||||
public string AssetGuid;
|
||||
|
||||
/// <summary>
|
||||
/// 所属资源包ID
|
||||
/// </summary>
|
||||
public int BundleID;
|
||||
|
||||
/// <summary>
|
||||
/// 依赖的资源包ID集合
|
||||
/// 说明:框架层收集查询结果
|
||||
/// </summary>
|
||||
public int[] DependentBundleIDs;
|
||||
|
||||
/// <summary>
|
||||
/// 资源的分类标签
|
||||
/// </summary>
|
||||
public string[] Tags;
|
||||
|
||||
/// <summary>
|
||||
/// 临时数据对象
|
||||
/// </summary>
|
||||
public object EditorUserData;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53eb285fc3a7b614089a000f8c9c738c
|
||||
guid: ecfb4d29b9f6c534191ca4d6e3de242a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建期专用的资源包描述
|
||||
/// </summary>
|
||||
internal class BuildBundle
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包名称
|
||||
/// </summary>
|
||||
public string BundleName;
|
||||
|
||||
/// <summary>
|
||||
/// Unity引擎计算的内容校验码
|
||||
/// </summary>
|
||||
public uint UnityCrc;
|
||||
|
||||
/// <summary>
|
||||
/// 文件哈希值
|
||||
/// </summary>
|
||||
public string FileHash;
|
||||
|
||||
/// <summary>
|
||||
/// 文件校验码
|
||||
/// </summary>
|
||||
public uint FileCrc;
|
||||
|
||||
/// <summary>
|
||||
/// 文件大小(字节数)
|
||||
/// </summary>
|
||||
public long FileSize;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为加密文件
|
||||
/// </summary>
|
||||
public bool IsEncrypted;
|
||||
|
||||
/// <summary>
|
||||
/// 依赖的资源包ID集合
|
||||
/// 注意:引擎层构建查询结果
|
||||
/// </summary>
|
||||
public int[] DependentBundleIDs;
|
||||
|
||||
/// <summary>
|
||||
/// 资源包的分类标签
|
||||
/// </summary>
|
||||
public string[] Tags;
|
||||
|
||||
/// <summary>
|
||||
/// 引用该资源包的资源包ID集合
|
||||
/// </summary>
|
||||
public readonly List<int> ReferrerBundleIDs = new List<int>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源包文件名称
|
||||
/// </summary>
|
||||
/// <param name="outputNameStyle">文件名称样式</param>
|
||||
/// <returns>返回根据命名样式生成的远端文件名</returns>
|
||||
public string GetFileName(int outputNameStyle)
|
||||
{
|
||||
return BundleFileNaming.GetBundleFileName(outputNameStyle, BundleName, FileHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定标签数组中的任意一个
|
||||
/// </summary>
|
||||
public bool HasAnyTag(string[] tags)
|
||||
{
|
||||
if (tags == null || tags.Length == 0 || Tags == null || Tags.Length == 0)
|
||||
return false;
|
||||
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (Tags.Contains(tag))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15552077c0d6ff441a4cd62af62b7d5a
|
||||
guid: 9109a09c01fd3d3448f0bfba85d8759f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,259 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建期专用的清单模型
|
||||
/// </summary>
|
||||
internal class BuildManifest
|
||||
{
|
||||
private bool _referrerGraphBuilt;
|
||||
|
||||
// 全局标签表
|
||||
private bool _tagTableBuilt;
|
||||
private string[] _tagTable;
|
||||
private Dictionary<string, int> _tagToIndex;
|
||||
|
||||
// 全局目录表
|
||||
private bool _directoryTableBuilt;
|
||||
private string[] _directoryTable;
|
||||
private Dictionary<string, int> _directoryToIndex;
|
||||
|
||||
/// <summary>
|
||||
/// 文件版本
|
||||
/// </summary>
|
||||
public int FileVersion;
|
||||
|
||||
/// <summary>
|
||||
/// 启用可寻址资源定位
|
||||
/// </summary>
|
||||
public bool EnableAddressable;
|
||||
|
||||
/// <summary>
|
||||
/// 支持无后缀名的资源定位地址
|
||||
/// </summary>
|
||||
public bool SupportExtensionless;
|
||||
|
||||
/// <summary>
|
||||
/// 资源定位地址大小写不敏感
|
||||
/// </summary>
|
||||
public bool LocationToLower;
|
||||
|
||||
/// <summary>
|
||||
/// 包含资源GUID数据
|
||||
/// </summary>
|
||||
public bool IncludeAssetGuid;
|
||||
|
||||
/// <summary>
|
||||
/// 使用可寻址地址代替资源路径
|
||||
/// </summary>
|
||||
public bool ReplaceAssetPathWithAddress;
|
||||
|
||||
/// <summary>
|
||||
/// 文件名称样式
|
||||
/// </summary>
|
||||
public int OutputNameStyle;
|
||||
|
||||
/// <summary>
|
||||
/// 构建资源包类型
|
||||
/// </summary>
|
||||
public int BuildBundleType;
|
||||
|
||||
/// <summary>
|
||||
/// 构建管线名称
|
||||
/// </summary>
|
||||
public string BuildPipeline;
|
||||
|
||||
/// <summary>
|
||||
/// 资源包裹名称
|
||||
/// </summary>
|
||||
public string PackageName;
|
||||
|
||||
/// <summary>
|
||||
/// 资源包裹的版本信息
|
||||
/// </summary>
|
||||
public string PackageVersion;
|
||||
|
||||
/// <summary>
|
||||
/// 资源包裹的备注信息
|
||||
/// </summary>
|
||||
public string PackageNote;
|
||||
|
||||
/// <summary>
|
||||
/// 资源列表
|
||||
/// </summary>
|
||||
public List<BuildAsset> AssetList = new List<BuildAsset>();
|
||||
|
||||
/// <summary>
|
||||
/// 资源包列表
|
||||
/// </summary>
|
||||
public List<BuildBundle> BundleList = new List<BuildBundle>();
|
||||
|
||||
/// <summary>
|
||||
/// 全局标签表(去重后的标签数组)
|
||||
/// </summary>
|
||||
public string[] TagTable
|
||||
{
|
||||
get { return _tagTable; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局目录表(去重后的目录数组)
|
||||
/// </summary>
|
||||
public string[] DirectoryTable
|
||||
{
|
||||
get { return _directoryTable; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 构建资源包的引用关系图
|
||||
/// 说明:根据每个资源包的依赖列表,反向填充被依赖方的引用者集合。
|
||||
/// </summary>
|
||||
public void BuildReferrerGraph()
|
||||
{
|
||||
if (_referrerGraphBuilt)
|
||||
throw new YooInternalException("BuildReferrerGraph has already been called.");
|
||||
_referrerGraphBuilt = true;
|
||||
|
||||
for (int index = 0; index < BundleList.Count; index++)
|
||||
{
|
||||
var sourceBundle = BundleList[index];
|
||||
if (sourceBundle.DependentBundleIDs == null)
|
||||
continue;
|
||||
|
||||
foreach (int dependIndex in sourceBundle.DependentBundleIDs)
|
||||
{
|
||||
if (dependIndex >= 0 && dependIndex < BundleList.Count)
|
||||
{
|
||||
var dependBundle = BundleList[dependIndex];
|
||||
if (dependBundle.ReferrerBundleIDs.Contains(index))
|
||||
throw new YooInternalException($"Duplicate referrer bundle ID detected: referrer {index} -> bundle {dependIndex}.");
|
||||
dependBundle.ReferrerBundleIDs.Add(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.ArgumentOutOfRangeException($"Invalid dependent bundle index: {dependIndex}. Valid range is 0 to {BundleList.Count - 1}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建去重后的全局标签表,并填充标签到索引的映射
|
||||
/// </summary>
|
||||
public void BuildTagTable()
|
||||
{
|
||||
if (_tagTableBuilt)
|
||||
throw new YooInternalException("BuildTagTable has already been called.");
|
||||
_tagTableBuilt = true;
|
||||
|
||||
HashSet<string> tagSet = new HashSet<string>();
|
||||
foreach (var buildAsset in AssetList)
|
||||
{
|
||||
if (buildAsset.Tags == null)
|
||||
continue;
|
||||
foreach (var tag in buildAsset.Tags)
|
||||
{
|
||||
tagSet.Add(tag);
|
||||
}
|
||||
}
|
||||
foreach (var buildBundle in BundleList)
|
||||
{
|
||||
if (buildBundle.Tags == null)
|
||||
continue;
|
||||
foreach (var tag in buildBundle.Tags)
|
||||
{
|
||||
tagSet.Add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
var tagTable = tagSet.ToList();
|
||||
tagTable.Sort(StringComparer.Ordinal); //注意:排序增加序列化稳定性
|
||||
|
||||
if (tagTable.Count > ushort.MaxValue)
|
||||
throw new YooInternalException($"Tag count exceeds the maximum value of {ushort.MaxValue}.");
|
||||
|
||||
_tagTable = tagTable.ToArray();
|
||||
_tagToIndex = new Dictionary<string, int>(_tagTable.Length);
|
||||
for (int index = 0; index < _tagTable.Length; index++)
|
||||
{
|
||||
_tagToIndex.Add(_tagTable[index], index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将标签字符串数组转换为全局标签表的索引数组
|
||||
/// </summary>
|
||||
public ushort[] ConvertTagsToIndices(string[] tags)
|
||||
{
|
||||
if (tags == null || tags.Length == 0)
|
||||
return Array.Empty<ushort>();
|
||||
|
||||
ushort[] indices = new ushort[tags.Length];
|
||||
for (int i = 0; i < tags.Length; i++)
|
||||
{
|
||||
indices[i] = (ushort)_tagToIndex[tags[i]];
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建去重后的全局目录表,并填充目录到索引的映射
|
||||
/// </summary>
|
||||
public void BuildDirectoryTable()
|
||||
{
|
||||
if (_directoryTableBuilt)
|
||||
throw new YooInternalException("BuildDirectoryTable has already been called.");
|
||||
_directoryTableBuilt = true;
|
||||
|
||||
HashSet<string> dirSet = new HashSet<string>();
|
||||
foreach (var buildAsset in AssetList)
|
||||
{
|
||||
SplitAssetPath(buildAsset.AssetPath, out string directory, out string fileName);
|
||||
dirSet.Add(directory);
|
||||
}
|
||||
|
||||
var dirTable = dirSet.ToList();
|
||||
dirTable.Sort(StringComparer.Ordinal); //注意:排序增加序列化稳定性
|
||||
|
||||
if (dirTable.Count > ushort.MaxValue)
|
||||
throw new YooInternalException($"Directory count exceeds the maximum value of {ushort.MaxValue}.");
|
||||
|
||||
_directoryTable = dirTable.ToArray();
|
||||
_directoryToIndex = new Dictionary<string, int>(_directoryTable.Length);
|
||||
for (int index = 0; index < _directoryTable.Length; index++)
|
||||
{
|
||||
_directoryToIndex.Add(_directoryTable[index], index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源父目录在全局目录表中的索引
|
||||
/// </summary>
|
||||
public ushort ConvertDirectoryToIndex(string directory)
|
||||
{
|
||||
return (ushort)_directoryToIndex[directory];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拆分资源路径为「父目录」与「文件名」
|
||||
/// </summary>
|
||||
public static void SplitAssetPath(string assetPath, out string directory, out string fileName)
|
||||
{
|
||||
int slash = assetPath.LastIndexOf('/');
|
||||
if (slash < 0)
|
||||
{
|
||||
directory = string.Empty;
|
||||
fileName = assetPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
directory = assetPath.Substring(0, slash);
|
||||
fileName = assetPath.Substring(slash + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f77081497483ffd44b94a78d4e95203c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
50
Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs
Normal file
50
Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.IO;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包文件命名工具
|
||||
/// </summary>
|
||||
internal static class BundleFileNaming
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取资源包的文件名
|
||||
/// </summary>
|
||||
/// <param name="nameStyle">文件名称样式</param>
|
||||
/// <param name="bundleName">资源包名称</param>
|
||||
/// <param name="fileHash">文件哈希值</param>
|
||||
/// <returns>返回根据命名样式生成的远端文件名</returns>
|
||||
public static string GetBundleFileName(int nameStyle, string bundleName, string fileHash)
|
||||
{
|
||||
EFileNameStyle style = (EFileNameStyle)nameStyle;
|
||||
switch (style)
|
||||
{
|
||||
case EFileNameStyle.HashName:
|
||||
{
|
||||
string fileExtension = Path.GetExtension(bundleName);
|
||||
return StringUtility.Format("{0}{1}", fileHash, fileExtension);
|
||||
}
|
||||
|
||||
case EFileNameStyle.BundleName:
|
||||
return bundleName;
|
||||
|
||||
case EFileNameStyle.BundleName_HashName:
|
||||
{
|
||||
string fileExtension = Path.GetExtension(bundleName);
|
||||
if (string.IsNullOrEmpty(fileExtension))
|
||||
{
|
||||
return StringUtility.Format("{0}_{1}", bundleName, fileHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
string fileName = bundleName.Remove(bundleName.LastIndexOf('.'));
|
||||
return StringUtility.Format("{0}_{1}{2}", fileName, fileHash, fileExtension);
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
throw new System.NotImplementedException($"Invalid name style: {nameStyle}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8eb74dcfa1fb85948ad1da5f188a6677
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -421,14 +421,14 @@ namespace YooAsset
|
||||
if (predicate(fileSystem, packageBundle))
|
||||
{
|
||||
// 注意:未标记的资源包视为公共依赖,始终包含在下载列表中
|
||||
if (packageBundle.IsTagged() == false)
|
||||
if (packageBundle.Tags.IsTagged == false)
|
||||
{
|
||||
var bundleInfo = new BundleInfo(fileSystem, packageBundle);
|
||||
result.Add(bundleInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packageBundle.HasAnyTag(tags))
|
||||
if (packageBundle.Tags.HasAnyTag(tags))
|
||||
{
|
||||
var bundleInfo = new BundleInfo(fileSystem, packageBundle);
|
||||
result.Add(bundleInfo);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace YooAsset
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
RestoreFileData,
|
||||
DecryptManifest,
|
||||
DeserializeFileHeader,
|
||||
PrepareAssetList,
|
||||
DeserializeAssetList,
|
||||
@@ -26,6 +26,8 @@ namespace YooAsset
|
||||
private readonly IManifestDecryptor _decryptor;
|
||||
private byte[] _sourceData;
|
||||
private BufferReader _buffer;
|
||||
private string[] _tagTable;
|
||||
private string[] _directoryTable;
|
||||
private int _packageAssetCount;
|
||||
private int _packageBundleCount;
|
||||
private int _progressTotalValue;
|
||||
@@ -48,14 +50,14 @@ namespace YooAsset
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.RestoreFileData;
|
||||
_steps = ESteps.DecryptManifest;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.RestoreFileData)
|
||||
if (_steps == ESteps.DecryptManifest)
|
||||
{
|
||||
if (_decryptor != null)
|
||||
{
|
||||
@@ -110,6 +112,12 @@ namespace YooAsset
|
||||
Manifest.PackageVersion = _buffer.ReadString();
|
||||
Manifest.PackageNote = _buffer.ReadString();
|
||||
|
||||
// 读取全局标签表
|
||||
_tagTable = _buffer.ReadStringArray();
|
||||
|
||||
// 读取全局目录表
|
||||
_directoryTable = _buffer.ReadStringArray();
|
||||
|
||||
// 检测配置
|
||||
if (Manifest.EnableAddressable && Manifest.LocationToLower)
|
||||
throw new YooManifestInvalidException("Addressable mode does not support converting locations to lowercase.");
|
||||
@@ -138,18 +146,35 @@ namespace YooAsset
|
||||
while (_packageAssetCount > 0)
|
||||
{
|
||||
var packageAsset = new PackageAsset();
|
||||
packageAsset.Address = _buffer.ReadString();
|
||||
|
||||
// Address
|
||||
if (Manifest.EnableAddressable)
|
||||
packageAsset.Address = _buffer.ReadString();
|
||||
else
|
||||
packageAsset.Address = string.Empty;
|
||||
|
||||
// AssetPath
|
||||
ushort dirIndex = _buffer.ReadUInt16();
|
||||
if (replaceAssetPath)
|
||||
{
|
||||
packageAsset.AssetPath = packageAsset.Address;
|
||||
_buffer.SkipString(); //跳过解析AssetPath
|
||||
_buffer.SkipString(); //跳过解析文件名
|
||||
}
|
||||
else
|
||||
{
|
||||
packageAsset.AssetPath = _buffer.ReadString();
|
||||
packageAsset.AssetPath = ResolvePath(dirIndex);
|
||||
}
|
||||
packageAsset.AssetGuid = _buffer.ReadString();
|
||||
packageAsset.AssetTags = _buffer.ReadStringArray();
|
||||
|
||||
// AssetGuid
|
||||
if (Manifest.IncludeAssetGuid)
|
||||
packageAsset.AssetGuid = _buffer.ReadHash16();
|
||||
else
|
||||
packageAsset.AssetGuid = string.Empty;
|
||||
|
||||
// Tags
|
||||
var tagIndices = _buffer.ReadUInt16Array();
|
||||
packageAsset.Tags = ResolveTags(tagIndices);
|
||||
|
||||
packageAsset.BundleID = _buffer.ReadInt32();
|
||||
packageAsset.DependentBundleIDs = _buffer.ReadInt32Array();
|
||||
FillAssetCollection(Manifest, packageAsset, replaceAssetPath);
|
||||
@@ -180,12 +205,13 @@ namespace YooAsset
|
||||
var packageBundle = new PackageBundle();
|
||||
packageBundle.BundleName = _buffer.ReadString();
|
||||
packageBundle.UnityCrc = _buffer.ReadUInt32();
|
||||
packageBundle.FileHash = _buffer.ReadString();
|
||||
packageBundle.FileHash = _buffer.ReadHash16();
|
||||
packageBundle.FileCrc = _buffer.ReadUInt32();
|
||||
packageBundle.FileSize = _buffer.ReadInt64();
|
||||
packageBundle.IsEncrypted = _buffer.ReadBoolean();
|
||||
packageBundle.Tags = _buffer.ReadStringArray();
|
||||
packageBundle.Tags = ResolveTags(_buffer.ReadUInt16Array());
|
||||
packageBundle.DependentBundleIDs = _buffer.ReadInt32Array();
|
||||
packageBundle.Initialize(Manifest);
|
||||
FillBundleCollection(Manifest, packageBundle);
|
||||
|
||||
_packageBundleCount--;
|
||||
@@ -202,7 +228,9 @@ namespace YooAsset
|
||||
|
||||
if (_steps == ESteps.InitManifest)
|
||||
{
|
||||
Manifest.Initialize();
|
||||
FillBundleMainAssets(Manifest);
|
||||
FillBundleReferrerBundleIDs(Manifest);
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
}
|
||||
@@ -212,51 +240,77 @@ namespace YooAsset
|
||||
ExecuteBatch();
|
||||
}
|
||||
|
||||
private PackageTags ResolveTags(ushort[] tagIndices)
|
||||
{
|
||||
if (tagIndices.Length == 0)
|
||||
return new PackageTags(Array.Empty<string>());
|
||||
|
||||
string[] tags = new string[tagIndices.Length];
|
||||
for (int i = 0; i < tagIndices.Length; i++)
|
||||
{
|
||||
ushort tagIndex = tagIndices[i];
|
||||
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
if (tagIndex >= _tagTable.Length)
|
||||
throw new YooManifestInvalidException($"Invalid tag index: {tagIndex}. Valid range is 0 to {_tagTable.Length - 1}.");
|
||||
#endif
|
||||
|
||||
tags[i] = _tagTable[tagIndex];
|
||||
}
|
||||
|
||||
return new PackageTags(tags);
|
||||
}
|
||||
private string ResolvePath(ushort dirIndex)
|
||||
{
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
if (dirIndex >= _directoryTable.Length)
|
||||
throw new YooManifestInvalidException($"Invalid directory index: {dirIndex}. Valid range is 0 to {_directoryTable.Length - 1}.");
|
||||
#endif
|
||||
|
||||
string fileName = _buffer.ReadString();
|
||||
string directory = _directoryTable[dirIndex];
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
return fileName;
|
||||
else
|
||||
return string.Concat(directory, "/", fileName);
|
||||
}
|
||||
|
||||
private void CreateAssetCollection(PackageManifest manifest, int assetCount)
|
||||
{
|
||||
manifest.AssetList = new List<PackageAsset>(assetCount);
|
||||
manifest.AssetDictionary = new Dictionary<string, PackageAsset>(assetCount);
|
||||
|
||||
if (manifest.EnableAddressable)
|
||||
{
|
||||
manifest.AssetPathsByLocation = new Dictionary<string, string>(assetCount * 3);
|
||||
manifest.AssetsByLocation = new Dictionary<string, PackageAsset>(assetCount * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (manifest.LocationToLower)
|
||||
manifest.AssetPathsByLocation = new Dictionary<string, string>(assetCount * 2, StringComparer.OrdinalIgnoreCase);
|
||||
manifest.AssetsByLocation = new Dictionary<string, PackageAsset>(assetCount * 2, StringComparer.OrdinalIgnoreCase);
|
||||
else
|
||||
manifest.AssetPathsByLocation = new Dictionary<string, string>(assetCount * 2);
|
||||
manifest.AssetsByLocation = new Dictionary<string, PackageAsset>(assetCount * 2);
|
||||
}
|
||||
|
||||
if (manifest.IncludeAssetGuid)
|
||||
manifest.AssetPathsByGuid = new Dictionary<string, string>(assetCount);
|
||||
manifest.AssetsByGuid = new Dictionary<string, PackageAsset>(assetCount);
|
||||
else
|
||||
manifest.AssetPathsByGuid = new Dictionary<string, string>();
|
||||
manifest.AssetsByGuid = new Dictionary<string, PackageAsset>();
|
||||
}
|
||||
private void FillAssetCollection(PackageManifest manifest, PackageAsset packageAsset, bool replaceAssetPath)
|
||||
{
|
||||
// 添加到列表集合
|
||||
manifest.AssetList.Add(packageAsset);
|
||||
|
||||
// 注意:我们不允许原始路径存在重名
|
||||
string assetPath = packageAsset.AssetPath;
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
if (manifest.AssetDictionary.ContainsKey(assetPath))
|
||||
throw new YooManifestInvalidException($"Asset path already exists: '{assetPath}'.");
|
||||
#endif
|
||||
manifest.AssetDictionary.Add(assetPath, packageAsset);
|
||||
|
||||
// 填充AssetPathMapping1
|
||||
// 填充AssetsByLocation
|
||||
{
|
||||
string location = packageAsset.AssetPath;
|
||||
|
||||
// 添加原生路径的映射
|
||||
// 添加原生路径的映射(注意:我们不允许原始路径存在重名)
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
if (manifest.AssetPathsByLocation.ContainsKey(location))
|
||||
throw new YooManifestInvalidException($"Location already exists: '{location}'.");
|
||||
if (manifest.AssetsByLocation.ContainsKey(location))
|
||||
throw new YooManifestInvalidException($"Asset path already exists: '{location}'.");
|
||||
#endif
|
||||
manifest.AssetPathsByLocation.Add(location, packageAsset.AssetPath);
|
||||
manifest.AssetsByLocation.Add(location, packageAsset);
|
||||
|
||||
// 添加无后缀名路径的映射
|
||||
if (manifest.SupportExtensionless)
|
||||
@@ -264,22 +318,22 @@ namespace YooAsset
|
||||
string locationWithoutExtension = Path.ChangeExtension(location, null);
|
||||
if (ReferenceEquals(location, locationWithoutExtension) == false)
|
||||
{
|
||||
if (manifest.AssetPathsByLocation.ContainsKey(locationWithoutExtension))
|
||||
if (manifest.AssetsByLocation.ContainsKey(locationWithoutExtension))
|
||||
YooLogger.LogWarning($"Location already exists: '{locationWithoutExtension}'.");
|
||||
else
|
||||
manifest.AssetPathsByLocation.Add(locationWithoutExtension, packageAsset.AssetPath);
|
||||
manifest.AssetsByLocation.Add(locationWithoutExtension, packageAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 填充AssetPathMapping2
|
||||
// 填充AssetsByGuid
|
||||
if (manifest.IncludeAssetGuid)
|
||||
{
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
if (manifest.AssetPathsByGuid.ContainsKey(packageAsset.AssetGuid))
|
||||
if (manifest.AssetsByGuid.ContainsKey(packageAsset.AssetGuid))
|
||||
throw new YooManifestInvalidException($"Asset GUID already exists: '{packageAsset.AssetGuid}'.");
|
||||
#endif
|
||||
manifest.AssetPathsByGuid.Add(packageAsset.AssetGuid, packageAsset.AssetPath);
|
||||
manifest.AssetsByGuid.Add(packageAsset.AssetGuid, packageAsset);
|
||||
}
|
||||
|
||||
// 添加可寻址地址
|
||||
@@ -289,10 +343,10 @@ namespace YooAsset
|
||||
if (string.IsNullOrEmpty(location) == false)
|
||||
{
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
if (manifest.AssetPathsByLocation.ContainsKey(location))
|
||||
if (manifest.AssetsByLocation.ContainsKey(location))
|
||||
throw new YooManifestInvalidException($"Location already exists: '{location}'.");
|
||||
#endif
|
||||
manifest.AssetPathsByLocation.Add(location, packageAsset.AssetPath);
|
||||
manifest.AssetsByLocation.Add(location, packageAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,9 +360,6 @@ namespace YooAsset
|
||||
}
|
||||
private void FillBundleCollection(PackageManifest manifest, PackageBundle packageBundle)
|
||||
{
|
||||
// 初始化资源包
|
||||
packageBundle.Initialize(manifest);
|
||||
|
||||
// 添加到列表集合
|
||||
manifest.BundleList.Add(packageBundle);
|
||||
|
||||
@@ -316,5 +367,72 @@ namespace YooAsset
|
||||
manifest.BundlesByFileName.Add(packageBundle.GetFileName(), packageBundle);
|
||||
manifest.BundlesByGuid.Add(packageBundle.BundleGuid, packageBundle);
|
||||
}
|
||||
|
||||
private void FillBundleMainAssets(PackageManifest manifest)
|
||||
{
|
||||
int bundleCount = manifest.BundleList.Count;
|
||||
|
||||
// 1. 统计每个资源包的主资源数量
|
||||
int[] mainAssetCounts = new int[bundleCount];
|
||||
foreach (var packageAsset in manifest.AssetList)
|
||||
{
|
||||
int bundleID = packageAsset.BundleID;
|
||||
if (bundleID < 0 || bundleID >= bundleCount)
|
||||
throw new ArgumentOutOfRangeException($"Invalid bundle ID: {bundleID}. Valid range is 0 to {bundleCount - 1}.");
|
||||
|
||||
mainAssetCounts[bundleID]++;
|
||||
}
|
||||
|
||||
// 2. 创建列表
|
||||
for (int index = 0; index < bundleCount; index++)
|
||||
{
|
||||
int capacity = mainAssetCounts[index];
|
||||
manifest.BundleList[index].MainAssets = new List<PackageAsset>(capacity);
|
||||
}
|
||||
|
||||
// 3. 填充数据
|
||||
foreach (var packageAsset in manifest.AssetList)
|
||||
{
|
||||
manifest.BundleList[packageAsset.BundleID].MainAssets.Add(packageAsset);
|
||||
}
|
||||
}
|
||||
private void FillBundleReferrerBundleIDs(PackageManifest manifest)
|
||||
{
|
||||
int bundleCount = manifest.BundleList.Count;
|
||||
|
||||
// 1. 统计每个资源包被引用的次数
|
||||
int[] referrerCounts = new int[bundleCount];
|
||||
for (int index = 0; index < bundleCount; index++)
|
||||
{
|
||||
foreach (int dependIndex in manifest.BundleList[index].DependentBundleIDs)
|
||||
{
|
||||
if (dependIndex < 0 || dependIndex >= bundleCount)
|
||||
throw new ArgumentOutOfRangeException($"Invalid dependent bundle index: {dependIndex}. Valid range is 0 to {bundleCount - 1}.");
|
||||
|
||||
referrerCounts[dependIndex]++;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 创建列表
|
||||
for (int index = 0; index < bundleCount; index++)
|
||||
{
|
||||
int capacity = referrerCounts[index];
|
||||
manifest.BundleList[index].ReferrerBundleIDs = new List<int>(capacity);
|
||||
}
|
||||
|
||||
// 3. 填充数据
|
||||
for (int index = 0; index < bundleCount; index++)
|
||||
{
|
||||
foreach (int dependIndex in manifest.BundleList[index].DependentBundleIDs)
|
||||
{
|
||||
var dependBundle = manifest.BundleList[dependIndex];
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
if (dependBundle.ReferrerBundleIDs.Contains(index))
|
||||
throw new YooManifestInvalidException($"Duplicate referrer bundle ID detected: referrer {index} -> bundle {dependIndex}.");
|
||||
#endif
|
||||
dependBundle.ReferrerBundleIDs.Add(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 序列化清单文件操作
|
||||
/// </summary>
|
||||
internal class SerializeManifestOperation : AsyncOperationBase
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
SerializeFileHeader,
|
||||
SerializeAssetList,
|
||||
SerializeBundleList,
|
||||
EncryptManifest,
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly BuildManifest _manifest;
|
||||
private readonly IManifestEncryptor _encryptor;
|
||||
private BufferWriter _buffer;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
/// <summary>
|
||||
/// 序列化后的二进制数据
|
||||
/// </summary>
|
||||
public byte[] FileData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建序列化清单文件操作实例
|
||||
/// </summary>
|
||||
/// <param name="manifest">清单对象</param>
|
||||
/// <param name="encryptor">清单加密器,为null时不加密</param>
|
||||
public SerializeManifestOperation(BuildManifest manifest, IManifestEncryptor encryptor)
|
||||
{
|
||||
_manifest = manifest;
|
||||
_encryptor = encryptor;
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.SerializeFileHeader;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.SerializeFileHeader)
|
||||
{
|
||||
// 创建缓存器
|
||||
_buffer = new BufferWriter(PackageManifestConsts.MaxFileSize);
|
||||
|
||||
// 写入文件标记
|
||||
_buffer.WriteUInt32(PackageManifestConsts.FileMagic);
|
||||
|
||||
// 写入文件版本
|
||||
_buffer.WriteInt32(_manifest.FileVersion);
|
||||
|
||||
// 写入文件头信息
|
||||
_buffer.WriteBoolean(_manifest.EnableAddressable);
|
||||
_buffer.WriteBoolean(_manifest.SupportExtensionless);
|
||||
_buffer.WriteBoolean(_manifest.LocationToLower);
|
||||
_buffer.WriteBoolean(_manifest.IncludeAssetGuid);
|
||||
_buffer.WriteBoolean(_manifest.ReplaceAssetPathWithAddress);
|
||||
_buffer.WriteInt32(_manifest.OutputNameStyle);
|
||||
_buffer.WriteInt32(_manifest.BuildBundleType);
|
||||
_buffer.WriteString(_manifest.BuildPipeline);
|
||||
_buffer.WriteString(_manifest.PackageName);
|
||||
_buffer.WriteString(_manifest.PackageVersion);
|
||||
_buffer.WriteString(_manifest.PackageNote);
|
||||
|
||||
// 写入全局标签表
|
||||
_buffer.WriteStringArray(_manifest.TagTable);
|
||||
|
||||
// 写入全局目录表
|
||||
_buffer.WriteStringArray(_manifest.DirectoryTable);
|
||||
|
||||
_steps = ESteps.SerializeAssetList;
|
||||
}
|
||||
|
||||
if (_steps == ESteps.SerializeAssetList)
|
||||
{
|
||||
_buffer.WriteInt32(_manifest.AssetList.Count);
|
||||
for (int i = 0; i < _manifest.AssetList.Count; i++)
|
||||
{
|
||||
var buildAsset = _manifest.AssetList[i];
|
||||
|
||||
// Address
|
||||
if (_manifest.EnableAddressable)
|
||||
_buffer.WriteString(buildAsset.Address);
|
||||
|
||||
// AssetPath
|
||||
BuildManifest.SplitAssetPath(buildAsset.AssetPath, out string directory, out string fileName);
|
||||
ushort directoryIndex = _manifest.ConvertDirectoryToIndex(directory);
|
||||
_buffer.WriteUInt16(directoryIndex);
|
||||
_buffer.WriteString(fileName);
|
||||
|
||||
// AssetGuid
|
||||
if (_manifest.IncludeAssetGuid)
|
||||
_buffer.WriteHash16(buildAsset.AssetGuid);
|
||||
|
||||
// Tags
|
||||
ushort[] tagIndices = _manifest.ConvertTagsToIndices(buildAsset.Tags);
|
||||
_buffer.WriteUInt16Array(tagIndices);
|
||||
|
||||
_buffer.WriteInt32(buildAsset.BundleID);
|
||||
_buffer.WriteInt32Array(buildAsset.DependentBundleIDs);
|
||||
}
|
||||
|
||||
_steps = ESteps.SerializeBundleList;
|
||||
}
|
||||
|
||||
if (_steps == ESteps.SerializeBundleList)
|
||||
{
|
||||
_buffer.WriteInt32(_manifest.BundleList.Count);
|
||||
for (int i = 0; i < _manifest.BundleList.Count; i++)
|
||||
{
|
||||
var buildBundle = _manifest.BundleList[i];
|
||||
ushort[] tagIndices = _manifest.ConvertTagsToIndices(buildBundle.Tags);
|
||||
_buffer.WriteString(buildBundle.BundleName);
|
||||
_buffer.WriteUInt32(buildBundle.UnityCrc);
|
||||
_buffer.WriteHash16(buildBundle.FileHash);
|
||||
_buffer.WriteUInt32(buildBundle.FileCrc);
|
||||
_buffer.WriteInt64(buildBundle.FileSize);
|
||||
_buffer.WriteBoolean(buildBundle.IsEncrypted);
|
||||
_buffer.WriteUInt16Array(tagIndices);
|
||||
_buffer.WriteInt32Array(buildBundle.DependentBundleIDs);
|
||||
}
|
||||
|
||||
_steps = ESteps.EncryptManifest;
|
||||
}
|
||||
|
||||
if (_steps == ESteps.EncryptManifest)
|
||||
{
|
||||
if (_encryptor != null)
|
||||
{
|
||||
var tempBytes = _buffer.ToArray();
|
||||
FileData = _encryptor.Encrypt(tempBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileData = _buffer.ToArray();
|
||||
}
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
}
|
||||
}
|
||||
protected override void InternalWaitForCompletion()
|
||||
{
|
||||
ExecuteBatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd920db2febc59c48a34b2899bd5c973
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
@@ -24,11 +23,6 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public string AssetGuid;
|
||||
|
||||
/// <summary>
|
||||
/// 资源的分类标签
|
||||
/// </summary>
|
||||
public string[] AssetTags;
|
||||
|
||||
/// <summary>
|
||||
/// 所属资源包ID
|
||||
/// </summary>
|
||||
@@ -41,29 +35,9 @@ namespace YooAsset
|
||||
public int[] DependentBundleIDs;
|
||||
|
||||
/// <summary>
|
||||
/// 临时数据对象(仅编辑器有效)
|
||||
/// 资源的分类标签
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public object EditorUserData;
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定的标签
|
||||
/// </summary>
|
||||
/// <param name="tags">要检查的标签数组</param>
|
||||
/// <returns>如果包含任意一个标签返回true,否则返回false。</returns>
|
||||
public bool HasAnyTag(string[] tags)
|
||||
{
|
||||
if (tags == null || tags.Length == 0)
|
||||
return false;
|
||||
if (AssetTags == null || AssetTags.Length == 0)
|
||||
return false;
|
||||
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (AssetTags.Contains(tag))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public PackageTags Tags;
|
||||
}
|
||||
}
|
||||
@@ -39,11 +39,6 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public bool IsEncrypted;
|
||||
|
||||
/// <summary>
|
||||
/// 资源包的分类标签
|
||||
/// </summary>
|
||||
public string[] Tags;
|
||||
|
||||
/// <summary>
|
||||
/// 依赖的资源包ID集合
|
||||
/// 注意:引擎层构建查询结果
|
||||
@@ -59,28 +54,28 @@ namespace YooAsset
|
||||
get { return FileHash; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源包的分类标签
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public PackageTags Tags;
|
||||
|
||||
/// <summary>
|
||||
/// 包含的主资源集合
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public readonly List<PackageAsset> MainAssets = new List<PackageAsset>(10);
|
||||
public List<PackageAsset> MainAssets;
|
||||
|
||||
/// <summary>
|
||||
/// 引用该资源包的资源包列表
|
||||
/// 引用该资源包的资源包ID列表
|
||||
/// 说明:谁引用了该资源包
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public readonly List<int> ReferrerBundleIDs = new List<int>(10);
|
||||
[NonSerialized]
|
||||
private readonly HashSet<int> _referrerBundleIDs = new HashSet<int>();
|
||||
public List<int> ReferrerBundleIDs;
|
||||
|
||||
[NonSerialized]
|
||||
private PackageManifest _manifest;
|
||||
[NonSerialized]
|
||||
private bool _isInitialized;
|
||||
[NonSerialized]
|
||||
private int _bundleType;
|
||||
[NonSerialized]
|
||||
private string _fileName;
|
||||
|
||||
|
||||
@@ -94,14 +89,10 @@ namespace YooAsset
|
||||
/// <summary>
|
||||
/// 初始化资源包
|
||||
/// </summary>
|
||||
/// <param name="manifest">所属的资源清单</param>
|
||||
public void Initialize(PackageManifest manifest)
|
||||
{
|
||||
_isInitialized = true;
|
||||
_manifest = manifest;
|
||||
_bundleType = manifest.BuildBundleType;
|
||||
string fileExtension = PackageManifestHelper.GetRemoteBundleFileExtension(BundleName);
|
||||
_fileName = PackageManifestHelper.GetRemoteBundleFileName(manifest.OutputNameStyle, BundleName, fileExtension, FileHash);
|
||||
_fileName = BundleFileNaming.GetBundleFileName(manifest.OutputNameStyle, BundleName, FileHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,9 +101,9 @@ namespace YooAsset
|
||||
/// <returns>返回资源包类型</returns>
|
||||
public int GetBundleType()
|
||||
{
|
||||
if (_isInitialized == false)
|
||||
if (_manifest == null)
|
||||
throw new YooInternalException("PackageBundle is not initialized.");
|
||||
return _bundleType;
|
||||
return _manifest.BuildBundleType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -121,68 +112,11 @@ namespace YooAsset
|
||||
/// <returns>返回资源包文件名称</returns>
|
||||
public string GetFileName()
|
||||
{
|
||||
if (_isInitialized == false)
|
||||
if (_manifest == null)
|
||||
throw new YooInternalException("PackageBundle is not initialized.");
|
||||
return _fileName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加引用该资源包的资源包ID
|
||||
/// </summary>
|
||||
/// <param name="bundleID">引用该资源包的资源包ID</param>
|
||||
/// <remarks>记录谁引用了该资源包</remarks>
|
||||
public void AddReferrerBundleID(int bundleID)
|
||||
{
|
||||
if (_referrerBundleIDs.Contains(bundleID) == false)
|
||||
{
|
||||
_referrerBundleIDs.Add(bundleID);
|
||||
ReferrerBundleIDs.Add(bundleID);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定的单个标签
|
||||
/// </summary>
|
||||
public bool HasTag(string tag)
|
||||
{
|
||||
if (Tags == null || Tags.Length == 0)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < Tags.Length; i++)
|
||||
{
|
||||
if (Tags[i] == tag)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定标签数组中的任意一个
|
||||
/// </summary>
|
||||
public bool HasAnyTag(string[] tags)
|
||||
{
|
||||
if (tags == null || tags.Length == 0)
|
||||
return false;
|
||||
if (Tags == null || Tags.Length == 0)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < tags.Length; i++)
|
||||
{
|
||||
if (HasTag(tags[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含分类标签
|
||||
/// </summary>
|
||||
/// <returns>如果包含至少一个标签返回true,否则返回false。</returns>
|
||||
public bool IsTagged()
|
||||
{
|
||||
return Tags != null && Tags.Length > 0;
|
||||
}
|
||||
|
||||
#region 调试信息
|
||||
[NonSerialized]
|
||||
private List<string> _debugReferrerBundleNames;
|
||||
|
||||
@@ -2,78 +2,150 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包裹的详细信息,用于外部查询包裹配置。
|
||||
/// 资源包裹的详细信息
|
||||
/// </summary>
|
||||
public class PackageDetails
|
||||
{
|
||||
private readonly PackageManifest _manifest;
|
||||
|
||||
/// <summary>
|
||||
/// 文件版本
|
||||
/// </summary>
|
||||
public int FileVersion { get; internal set; }
|
||||
public int FileVersion
|
||||
{
|
||||
get { return _manifest.FileVersion; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用可寻址资源定位
|
||||
/// </summary>
|
||||
public bool EnableAddressable { get; internal set; }
|
||||
public bool EnableAddressable
|
||||
{
|
||||
get { return _manifest.EnableAddressable; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支持无后缀名的资源定位地址
|
||||
/// </summary>
|
||||
public bool SupportExtensionless { get; internal set; }
|
||||
public bool SupportExtensionless
|
||||
{
|
||||
get { return _manifest.SupportExtensionless; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源定位地址大小写不敏感
|
||||
/// </summary>
|
||||
public bool LocationToLower { get; internal set; }
|
||||
public bool LocationToLower
|
||||
{
|
||||
get { return _manifest.LocationToLower; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 包含资源GUID数据
|
||||
/// </summary>
|
||||
public bool IncludeAssetGuid { get; internal set; }
|
||||
public bool IncludeAssetGuid
|
||||
{
|
||||
get { return _manifest.IncludeAssetGuid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用可寻址地址代替资源路径
|
||||
/// </summary>
|
||||
public bool ReplaceAssetPathWithAddress { get; internal set; }
|
||||
public bool ReplaceAssetPathWithAddress
|
||||
{
|
||||
get { return _manifest.ReplaceAssetPathWithAddress; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文件名称样式
|
||||
/// </summary>
|
||||
public int OutputNameStyle { get; internal set; }
|
||||
public int OutputNameStyle
|
||||
{
|
||||
get { return _manifest.OutputNameStyle; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建资源包类型
|
||||
/// </summary>
|
||||
public int BuildBundleType { get; internal set; }
|
||||
public int BuildBundleType
|
||||
{
|
||||
get { return _manifest.BuildBundleType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建管线名称
|
||||
/// </summary>
|
||||
public string BuildPipeline { get; internal set; }
|
||||
public string BuildPipeline
|
||||
{
|
||||
get { return _manifest.BuildPipeline; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源包裹名称
|
||||
/// </summary>
|
||||
public string PackageName { get; internal set; }
|
||||
public string PackageName
|
||||
{
|
||||
get { return _manifest.PackageName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源包裹的版本信息
|
||||
/// </summary>
|
||||
public string PackageVersion { get; internal set; }
|
||||
public string PackageVersion
|
||||
{
|
||||
get { return _manifest.PackageVersion; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源包裹的备注信息
|
||||
/// </summary>
|
||||
public string PackageNote { get; internal set; }
|
||||
public string PackageNote
|
||||
{
|
||||
get { return _manifest.PackageNote; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 主资源文件总数
|
||||
/// </summary>
|
||||
public int AssetTotalCount { get; internal set; }
|
||||
public int AssetTotalCount
|
||||
{
|
||||
get { return _manifest.AssetList.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源包文件总数
|
||||
/// </summary>
|
||||
public int BundleTotalCount { get; internal set; }
|
||||
public int BundleTotalCount
|
||||
{
|
||||
get { return _manifest.BundleList.Count; }
|
||||
}
|
||||
|
||||
internal PackageDetails(PackageManifest manifest)
|
||||
{
|
||||
_manifest = manifest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回包裹详细信息的字符串描述
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine($"FileVersion : {FileVersion}");
|
||||
sb.AppendLine($"PackageName : {PackageName}");
|
||||
sb.AppendLine($"PackageVersion : {PackageVersion}");
|
||||
sb.AppendLine($"PackageNote : {PackageNote}");
|
||||
sb.AppendLine($"BuildPipeline : {BuildPipeline}");
|
||||
sb.AppendLine($"BuildBundleType : {BuildBundleType}");
|
||||
sb.AppendLine($"OutputNameStyle : {OutputNameStyle}");
|
||||
sb.AppendLine($"EnableAddressable : {EnableAddressable}");
|
||||
sb.AppendLine($"SupportExtensionless : {SupportExtensionless}");
|
||||
sb.AppendLine($"LocationToLower : {LocationToLower}");
|
||||
sb.AppendLine($"IncludeAssetGuid : {IncludeAssetGuid}");
|
||||
sb.AppendLine($"ReplaceAssetPathWithAddress : {ReplaceAssetPathWithAddress}");
|
||||
sb.AppendLine($"AssetTotalCount : {AssetTotalCount}");
|
||||
sb.AppendLine($"BundleTotalCount : {BundleTotalCount}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,22 +82,16 @@ namespace YooAsset
|
||||
public List<PackageBundle> BundleList = new List<PackageBundle>();
|
||||
|
||||
/// <summary>
|
||||
/// 资源映射集合(提供AssetPath获取PackageAsset)
|
||||
/// 资源映射集合(提供Location获取PackageAsset)
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public Dictionary<string, PackageAsset> AssetDictionary;
|
||||
public Dictionary<string, PackageAsset> AssetsByLocation;
|
||||
|
||||
/// <summary>
|
||||
/// 资源路径映射集合(提供Location获取AssetPath)
|
||||
/// 资源映射集合(提供AssetGUID获取PackageAsset)
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public Dictionary<string, string> AssetPathsByLocation;
|
||||
|
||||
/// <summary>
|
||||
/// 资源路径映射集合(提供AssetGUID获取AssetPath)
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public Dictionary<string, string> AssetPathsByGuid;
|
||||
public Dictionary<string, PackageAsset> AssetsByGuid;
|
||||
|
||||
/// <summary>
|
||||
/// 资源包集合(提供BundleName获取PackageBundle)
|
||||
@@ -118,67 +112,13 @@ namespace YooAsset
|
||||
public Dictionary<string, PackageBundle> BundlesByGuid;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化资源清单
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
// 填充资源包内包含的主资源列表
|
||||
foreach (var packageAsset in AssetList)
|
||||
{
|
||||
int bundleID = packageAsset.BundleID;
|
||||
if (bundleID >= 0 && bundleID < BundleList.Count)
|
||||
{
|
||||
var packageBundle = BundleList[bundleID];
|
||||
packageBundle.MainAssets.Add(packageAsset);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.ArgumentOutOfRangeException($"Invalid bundle ID: {bundleID}. Valid range is 0 to {BundleList.Count - 1}.");
|
||||
}
|
||||
}
|
||||
|
||||
// 填充资源包引用关系
|
||||
for (int index = 0; index < BundleList.Count; index++)
|
||||
{
|
||||
var sourceBundle = BundleList[index];
|
||||
foreach (int dependIndex in sourceBundle.DependentBundleIDs)
|
||||
{
|
||||
if (dependIndex >= 0 && dependIndex < BundleList.Count)
|
||||
{
|
||||
var dependBundle = BundleList[dependIndex];
|
||||
dependBundle.AddReferrerBundleID(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.ArgumentOutOfRangeException($"Invalid dependent bundle index: {dependIndex}. Valid range is 0 to {BundleList.Count - 1}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取包裹的详细信息
|
||||
/// </summary>
|
||||
/// <returns>返回包含包裹配置信息的详细信息对象</returns>
|
||||
public PackageDetails GetPackageDetails()
|
||||
{
|
||||
PackageDetails details = new PackageDetails();
|
||||
details.FileVersion = FileVersion;
|
||||
details.EnableAddressable = EnableAddressable;
|
||||
details.SupportExtensionless = SupportExtensionless;
|
||||
details.LocationToLower = LocationToLower;
|
||||
details.IncludeAssetGuid = IncludeAssetGuid;
|
||||
details.ReplaceAssetPathWithAddress = ReplaceAssetPathWithAddress;
|
||||
details.OutputNameStyle = OutputNameStyle;
|
||||
details.BuildBundleType = BuildBundleType;
|
||||
details.BuildPipeline = BuildPipeline;
|
||||
details.PackageName = PackageName;
|
||||
details.PackageVersion = PackageVersion;
|
||||
details.PackageNote = PackageNote;
|
||||
details.AssetTotalCount = AssetList.Count;
|
||||
details.BundleTotalCount = BundleList.Count;
|
||||
return details;
|
||||
return new PackageDetails(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,8 +131,8 @@ namespace YooAsset
|
||||
if (string.IsNullOrEmpty(location))
|
||||
return string.Empty;
|
||||
|
||||
if (AssetPathsByLocation.TryGetValue(location, out string assetPath))
|
||||
return assetPath;
|
||||
if (AssetsByLocation.TryGetValue(location, out PackageAsset packageAsset))
|
||||
return packageAsset.AssetPath;
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -271,7 +211,7 @@ namespace YooAsset
|
||||
/// <returns>如果找到返回true,否则返回false。</returns>
|
||||
public bool TryGetPackageAsset(string assetPath, out PackageAsset result)
|
||||
{
|
||||
return AssetDictionary.TryGetValue(assetPath, out result);
|
||||
return AssetsByLocation.TryGetValue(assetPath, out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -343,7 +283,7 @@ namespace YooAsset
|
||||
List<AssetInfo> result = new List<AssetInfo>(AssetList.Count);
|
||||
foreach (var packageAsset in AssetList)
|
||||
{
|
||||
if (packageAsset.HasAnyTag(tags))
|
||||
if (packageAsset.Tags.HasAnyTag(tags))
|
||||
{
|
||||
AssetInfo assetInfo = new AssetInfo(PackageName, packageAsset, null);
|
||||
result.Add(assetInfo);
|
||||
@@ -362,39 +302,20 @@ namespace YooAsset
|
||||
{
|
||||
DebugCheckLocation(location);
|
||||
|
||||
string assetPath = ResolveLocationToAssetPath(location);
|
||||
if (TryGetPackageAsset(assetPath, out PackageAsset packageAsset))
|
||||
{
|
||||
AssetInfo assetInfo = new AssetInfo(PackageName, packageAsset, assetType);
|
||||
return assetInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
string error;
|
||||
if (string.IsNullOrEmpty(location))
|
||||
error = "Location is null or empty.";
|
||||
else
|
||||
error = $"Location is invalid: '{location}'.";
|
||||
AssetInfo assetInfo = new AssetInfo(PackageName, error);
|
||||
return assetInfo;
|
||||
}
|
||||
}
|
||||
private string ResolveLocationToAssetPath(string location)
|
||||
{
|
||||
if (string.IsNullOrEmpty(location))
|
||||
{
|
||||
YooLogger.LogError("Failed to map location to asset path. Location is null or empty.");
|
||||
return string.Empty;
|
||||
return new AssetInfo(PackageName, "Location is null or empty.");
|
||||
}
|
||||
|
||||
if (AssetPathsByLocation.TryGetValue(location, out string assetPath))
|
||||
if (AssetsByLocation.TryGetValue(location, out PackageAsset packageAsset))
|
||||
{
|
||||
return assetPath;
|
||||
return new AssetInfo(PackageName, packageAsset, assetType);
|
||||
}
|
||||
else
|
||||
{
|
||||
YooLogger.LogWarning($"Failed to map location to asset path: '{location}'.");
|
||||
return string.Empty;
|
||||
return new AssetInfo(PackageName, $"Location is invalid: '{location}'.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,43 +330,23 @@ namespace YooAsset
|
||||
if (IncludeAssetGuid == false)
|
||||
{
|
||||
YooLogger.LogWarning("Package manifest does not include asset GUID. Please check asset bundle collector settings.");
|
||||
AssetInfo assetInfo = new AssetInfo(PackageName, "AssetGuid data is empty.");
|
||||
return assetInfo;
|
||||
return new AssetInfo(PackageName, "AssetGuid data is empty.");
|
||||
}
|
||||
|
||||
string assetPath = ResolveGuidToAssetPath(assetGuid);
|
||||
if (TryGetPackageAsset(assetPath, out PackageAsset packageAsset))
|
||||
{
|
||||
AssetInfo assetInfo = new AssetInfo(PackageName, packageAsset, assetType);
|
||||
return assetInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
string error;
|
||||
if (string.IsNullOrEmpty(assetGuid))
|
||||
error = "Asset GUID is null or empty.";
|
||||
else
|
||||
error = $"Asset GUID is invalid: '{assetGuid}'.";
|
||||
AssetInfo assetInfo = new AssetInfo(PackageName, error);
|
||||
return assetInfo;
|
||||
}
|
||||
}
|
||||
private string ResolveGuidToAssetPath(string assetGuid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(assetGuid))
|
||||
{
|
||||
YooLogger.LogError("Failed to map asset GUID to asset path. Asset GUID is null or empty.");
|
||||
return string.Empty;
|
||||
return new AssetInfo(PackageName, "Asset GUID is null or empty.");
|
||||
}
|
||||
|
||||
if (AssetPathsByGuid.TryGetValue(assetGuid, out string assetPath))
|
||||
if (AssetsByGuid.TryGetValue(assetGuid, out PackageAsset packageAsset))
|
||||
{
|
||||
return assetPath;
|
||||
return new AssetInfo(PackageName, packageAsset, assetType);
|
||||
}
|
||||
else
|
||||
{
|
||||
YooLogger.LogWarning($"Failed to map asset GUID to asset path: '{assetGuid}'.");
|
||||
return string.Empty;
|
||||
return new AssetInfo(PackageName, $"Asset GUID is invalid: '{assetGuid}'.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ namespace YooAsset
|
||||
/// <summary>
|
||||
/// 文件版本号
|
||||
/// </summary>
|
||||
public const int FileVersion = 1;
|
||||
public const int FileVersion = 2;
|
||||
|
||||
/// <summary>
|
||||
/// 文件最小合法大小(37字节)
|
||||
/// 文件最小合法大小(41字节)
|
||||
/// </summary>
|
||||
public const int MinFileSize = 37;
|
||||
public const int MinFileSize = 41;
|
||||
}
|
||||
}
|
||||
@@ -39,113 +39,18 @@ namespace YooAsset
|
||||
return fileHash == hashValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将清单文件序列化为JSON格式
|
||||
/// </summary>
|
||||
/// <param name="savePath">保存路径</param>
|
||||
/// <param name="manifest">清单对象</param>
|
||||
public static void SerializeManifestToJson(string savePath, PackageManifest manifest)
|
||||
{
|
||||
string json = JsonUtility.ToJson(manifest, true);
|
||||
FileUtility.WriteAllText(savePath, json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将清单文件序列化为二进制格式
|
||||
/// </summary>
|
||||
/// <param name="savePath">保存路径</param>
|
||||
/// <param name="manifest">清单对象</param>
|
||||
/// <param name="encryptor">清单加密(可为null)</param>
|
||||
public static void SerializeManifestToBinary(string savePath, PackageManifest manifest, IManifestEncryptor encryptor)
|
||||
public static void SerializeManifestToBinary(string savePath, BuildManifest manifest, IManifestEncryptor encryptor)
|
||||
{
|
||||
using (FileStream fs = new FileStream(savePath, FileMode.Create))
|
||||
{
|
||||
// 创建缓存器
|
||||
BufferWriter buffer = new BufferWriter(PackageManifestConsts.MaxFileSize);
|
||||
|
||||
// 写入文件标记
|
||||
buffer.WriteUInt32(PackageManifestConsts.FileMagic);
|
||||
|
||||
// 写入文件版本
|
||||
buffer.WriteInt32(manifest.FileVersion);
|
||||
|
||||
// 写入文件头信息
|
||||
buffer.WriteBoolean(manifest.EnableAddressable);
|
||||
buffer.WriteBoolean(manifest.SupportExtensionless);
|
||||
buffer.WriteBoolean(manifest.LocationToLower);
|
||||
buffer.WriteBoolean(manifest.IncludeAssetGuid);
|
||||
buffer.WriteBoolean(manifest.ReplaceAssetPathWithAddress);
|
||||
buffer.WriteInt32(manifest.OutputNameStyle);
|
||||
buffer.WriteInt32(manifest.BuildBundleType);
|
||||
buffer.WriteString(manifest.BuildPipeline);
|
||||
buffer.WriteString(manifest.PackageName);
|
||||
buffer.WriteString(manifest.PackageVersion);
|
||||
buffer.WriteString(manifest.PackageNote);
|
||||
|
||||
// 写入资源列表
|
||||
buffer.WriteInt32(manifest.AssetList.Count);
|
||||
for (int i = 0; i < manifest.AssetList.Count; i++)
|
||||
{
|
||||
var packageAsset = manifest.AssetList[i];
|
||||
buffer.WriteString(packageAsset.Address);
|
||||
buffer.WriteString(packageAsset.AssetPath);
|
||||
buffer.WriteString(packageAsset.AssetGuid);
|
||||
buffer.WriteStringArray(packageAsset.AssetTags);
|
||||
buffer.WriteInt32(packageAsset.BundleID);
|
||||
buffer.WriteInt32Array(packageAsset.DependentBundleIDs);
|
||||
}
|
||||
|
||||
// 写入资源包列表
|
||||
buffer.WriteInt32(manifest.BundleList.Count);
|
||||
for (int i = 0; i < manifest.BundleList.Count; i++)
|
||||
{
|
||||
var packageBundle = manifest.BundleList[i];
|
||||
buffer.WriteString(packageBundle.BundleName);
|
||||
buffer.WriteUInt32(packageBundle.UnityCrc);
|
||||
buffer.WriteString(packageBundle.FileHash);
|
||||
buffer.WriteUInt32(packageBundle.FileCrc);
|
||||
buffer.WriteInt64(packageBundle.FileSize);
|
||||
buffer.WriteBoolean(packageBundle.IsEncrypted);
|
||||
buffer.WriteStringArray(packageBundle.Tags);
|
||||
buffer.WriteInt32Array(packageBundle.DependentBundleIDs);
|
||||
}
|
||||
|
||||
// 清单处理操作
|
||||
if (encryptor != null)
|
||||
{
|
||||
var tempBytes = buffer.ToArray();
|
||||
var resultBytes = encryptor.Encrypt(tempBytes);
|
||||
fs.Write(resultBytes, 0, resultBytes.Length);
|
||||
fs.Flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 写入文件流
|
||||
buffer.WriteToStream(fs);
|
||||
fs.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JSON字符串反序列化清单文件
|
||||
/// </summary>
|
||||
/// <param name="jsonContent">JSON内容字符串</param>
|
||||
/// <returns>返回反序列化后的清单对象</returns>
|
||||
public static PackageManifest DeserializeManifestFromJson(string jsonContent)
|
||||
{
|
||||
var manifest = JsonUtility.FromJson<PackageManifest>(jsonContent);
|
||||
|
||||
// 初始化资源包
|
||||
for (int i = 0; i < manifest.BundleList.Count; i++)
|
||||
{
|
||||
var packageBundle = manifest.BundleList[i];
|
||||
packageBundle.Initialize(manifest);
|
||||
}
|
||||
|
||||
// 初始化资源清单
|
||||
manifest.Initialize();
|
||||
return manifest;
|
||||
SerializeManifestOperation operation = new SerializeManifestOperation(manifest, encryptor);
|
||||
operation.StartOperation();
|
||||
operation.WaitForCompletion();
|
||||
FileUtility.WriteAllBytes(savePath, operation.FileData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,51 +66,5 @@ namespace YooAsset
|
||||
operation.WaitForCompletion();
|
||||
return operation.Manifest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源文件的后缀名
|
||||
/// </summary>
|
||||
/// <param name="bundleName">资源包名称</param>
|
||||
/// <returns>返回文件后缀名(包含点号)</returns>
|
||||
public static string GetRemoteBundleFileExtension(string bundleName)
|
||||
{
|
||||
string fileExtension = Path.GetExtension(bundleName);
|
||||
return fileExtension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取远端的资源文件名
|
||||
/// </summary>
|
||||
/// <param name="nameStyle">文件名称样式</param>
|
||||
/// <param name="bundleName">资源包名称</param>
|
||||
/// <param name="fileExtension">文件后缀名</param>
|
||||
/// <param name="fileHash">文件哈希值</param>
|
||||
/// <returns>返回根据命名样式生成的远端文件名</returns>
|
||||
public static string GetRemoteBundleFileName(int nameStyle, string bundleName, string fileExtension, string fileHash)
|
||||
{
|
||||
EFileNameStyle style = (EFileNameStyle)nameStyle;
|
||||
switch (style)
|
||||
{
|
||||
case EFileNameStyle.HashName:
|
||||
return StringUtility.Format("{0}{1}", fileHash, fileExtension);
|
||||
|
||||
case EFileNameStyle.BundleName:
|
||||
return bundleName;
|
||||
|
||||
case EFileNameStyle.BundleName_HashName:
|
||||
if (string.IsNullOrEmpty(fileExtension))
|
||||
{
|
||||
return StringUtility.Format("{0}_{1}", bundleName, fileHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
string fileName = bundleName.Remove(bundleName.LastIndexOf('.'));
|
||||
return StringUtility.Format("{0}_{1}{2}", fileName, fileHash, fileExtension);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Invalid name style: {nameStyle}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs
Normal file
91
Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 标签集合
|
||||
/// </summary>
|
||||
public class PackageTags
|
||||
{
|
||||
/// <summary>
|
||||
/// 空标签集合
|
||||
/// </summary>
|
||||
public static readonly PackageTags Empty = new PackageTags(Array.Empty<string>());
|
||||
|
||||
private readonly string[] _tags;
|
||||
|
||||
/// <summary>
|
||||
/// 解析后的完整标签字符串数组
|
||||
/// </summary>
|
||||
internal string[] RawTags
|
||||
{
|
||||
get { return _tags; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签数量
|
||||
/// </summary>
|
||||
public int TagCount
|
||||
{
|
||||
get { return _tags.Length; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含分类标签
|
||||
/// </summary>
|
||||
public bool IsTagged
|
||||
{
|
||||
get { return _tags.Length > 0; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 创建标签集合实例
|
||||
/// </summary>
|
||||
/// <param name="tags">解析后的标签字符串数组</param>
|
||||
public PackageTags(string[] tags)
|
||||
{
|
||||
if (tags == null)
|
||||
_tags = Array.Empty<string>();
|
||||
else
|
||||
_tags = tags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定索引的标签
|
||||
/// </summary>
|
||||
public string GetTag(int index)
|
||||
{
|
||||
return _tags[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定的单个标签
|
||||
/// </summary>
|
||||
public bool HasTag(string tag)
|
||||
{
|
||||
for (int i = 0; i < _tags.Length; i++)
|
||||
{
|
||||
if (_tags[i] == tag)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定标签数组中的任意一个
|
||||
/// </summary>
|
||||
public bool HasAnyTag(string[] tags)
|
||||
{
|
||||
if (tags == null || tags.Length == 0 || _tags.Length == 0)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < tags.Length; i++)
|
||||
{
|
||||
if (HasTag(tags[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs.meta
Normal file
11
Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d3c7e71f91758244916b4f9d00c24a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -161,17 +161,18 @@ namespace YooAsset
|
||||
/// <returns>缓存文件根目录的绝对路径</returns>
|
||||
internal static string GetEditorCacheRoot()
|
||||
{
|
||||
// 注意:为了方便调试查看,编辑器下把存储目录放到项目根目录下。
|
||||
// 注意:为了方便调试查看,编辑器下把存储目录放到项目的 Library 目录下。
|
||||
string projectPath = Path.GetDirectoryName(Application.dataPath);
|
||||
if (string.IsNullOrEmpty(projectPath))
|
||||
throw new InvalidOperationException("Could not determine project root path from Application.dataPath.");
|
||||
projectPath = PathUtility.NormalizePath(projectPath);
|
||||
|
||||
string libraryPath = PathUtility.Combine(projectPath, "Library");
|
||||
var settings = GetSettings();
|
||||
if (string.IsNullOrEmpty(settings.YooFolderName))
|
||||
return projectPath;
|
||||
return libraryPath;
|
||||
else
|
||||
return PathUtility.Combine(projectPath, settings.YooFolderName);
|
||||
return PathUtility.Combine(libraryPath, settings.YooFolderName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
internal class BufferReader
|
||||
{
|
||||
private readonly char[] _hashChars = new char[32];
|
||||
private readonly byte[] _buffer;
|
||||
private int _position = 0;
|
||||
|
||||
@@ -163,6 +164,38 @@ namespace YooAsset
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取哈希值(16字节数据转换为32位哈希值)
|
||||
/// </summary>
|
||||
public string ReadHash16()
|
||||
{
|
||||
EnsureCapacity(16);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
byte b = _buffer[_position++];
|
||||
_hashChars[i * 2] = GetHexChar(b >> 4);
|
||||
_hashChars[i * 2 + 1] = GetHexChar(b & 0xF);
|
||||
}
|
||||
return new string(_hashChars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取16位无符号整数数组
|
||||
/// </summary>
|
||||
public ushort[] ReadUInt16Array()
|
||||
{
|
||||
ushort count = ReadUInt16();
|
||||
if (count == 0)
|
||||
return Array.Empty<ushort>();
|
||||
|
||||
ushort[] values = new ushort[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
values[i] = ReadUInt16();
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取32位有符号整数数组
|
||||
/// </summary>
|
||||
@@ -222,5 +255,11 @@ namespace YooAsset
|
||||
throw new InvalidOperationException("Insufficient buffer capacity.");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static char GetHexChar(int value)
|
||||
{
|
||||
return (char)(value < 10 ? '0' + value : 'a' + value - 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,6 +178,47 @@ namespace YooAsset
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入哈希值(32位哈希值转换为16字节数据)
|
||||
/// </summary>
|
||||
public void WriteHash16(string hashString)
|
||||
{
|
||||
if (hashString == null)
|
||||
throw new ArgumentNullException(nameof(hashString));
|
||||
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
if (hashString.Length != 32)
|
||||
throw new InvalidOperationException($"Hash string length must be 32, but got {hashString.Length}: '{hashString}'.");
|
||||
#endif
|
||||
|
||||
EnsureCapacity(16);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int high = GetHexValue(hashString[i * 2]);
|
||||
int low = GetHexValue(hashString[i * 2 + 1]);
|
||||
_buffer[_position++] = (byte)((high << 4) | low);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入16位无符号整数数组
|
||||
/// </summary>
|
||||
public void WriteUInt16Array(ushort[] values)
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
|
||||
int count = values.Length;
|
||||
if (count > ushort.MaxValue)
|
||||
throw new OverflowException($"Array length exceeds the maximum value of {ushort.MaxValue}.");
|
||||
|
||||
WriteUInt16(Convert.ToUInt16(count));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WriteUInt16(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入32位有符号整数数组
|
||||
/// </summary>
|
||||
@@ -243,5 +284,16 @@ namespace YooAsset
|
||||
throw new InvalidOperationException("Insufficient buffer capacity.");
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetHexValue(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
throw new FormatException($"Invalid hex character: '{c}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CustomPropertyDrawer(typeof(GameObjectReference))]
|
||||
public class GameObjectReferenceDrawer : PropertyDrawer
|
||||
[CustomPropertyDrawer(typeof(AssetReference), true)]
|
||||
public class AssetReferenceDrawer : PropertyDrawer
|
||||
{
|
||||
private const float LineSpacing = 2f;
|
||||
|
||||
private Type _assetType;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
SerializedProperty packageNameProp = property.FindPropertyRelative("_packageName");
|
||||
SerializedProperty assetGUIDProp = property.FindPropertyRelative("_assetGUID");
|
||||
|
||||
Type assetType = GetAssetType();
|
||||
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
{
|
||||
float lineHeight = EditorGUIUtility.singleLineHeight;
|
||||
@@ -19,19 +25,19 @@ public class GameObjectReferenceDrawer : PropertyDrawer
|
||||
// 绘制 PackageName
|
||||
packageNameProp.stringValue = EditorGUI.TextField(line, "Package Name", packageNameProp.stringValue);
|
||||
|
||||
// 加载 GameObject
|
||||
// 加载当前资源对象
|
||||
string assetGUID = assetGUIDProp.stringValue;
|
||||
GameObject current = null;
|
||||
UnityEngine.Object current = null;
|
||||
if (string.IsNullOrEmpty(assetGUID) == false)
|
||||
{
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(assetGUID);
|
||||
if (string.IsNullOrEmpty(assetPath) == false)
|
||||
current = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
|
||||
current = AssetDatabase.LoadAssetAtPath(assetPath, assetType);
|
||||
}
|
||||
|
||||
// 绘制 GameObject
|
||||
// 绘制资源对象字段
|
||||
line.y += lineHeight + LineSpacing;
|
||||
GameObject newAsset = (GameObject)EditorGUI.ObjectField(line, "Game Object", current, typeof(GameObject), false);
|
||||
UnityEngine.Object newAsset = EditorGUI.ObjectField(line, assetType.Name, current, assetType, false);
|
||||
if (newAsset != current)
|
||||
{
|
||||
if (newAsset == null)
|
||||
@@ -46,7 +52,7 @@ public class GameObjectReferenceDrawer : PropertyDrawer
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制 AssetGUID
|
||||
// 绘制 AssetGUID(只读)
|
||||
line.y += lineHeight + LineSpacing;
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUI.TextField(line, "Asset GUID", assetGUIDProp.stringValue);
|
||||
@@ -54,8 +60,31 @@ public class GameObjectReferenceDrawer : PropertyDrawer
|
||||
}
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
return EditorGUIUtility.singleLineHeight * 3 + LineSpacing * 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射获取字段对应子类的 AssetType
|
||||
/// </summary>
|
||||
private Type GetAssetType()
|
||||
{
|
||||
if (_assetType != null)
|
||||
return _assetType;
|
||||
|
||||
Type fieldType = fieldInfo.FieldType;
|
||||
|
||||
// 兼容数组
|
||||
if (fieldType.IsArray)
|
||||
fieldType = fieldType.GetElementType();
|
||||
// 兼容 List<T>
|
||||
else if (fieldType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(fieldType))
|
||||
fieldType = fieldType.GetGenericArguments()[0];
|
||||
|
||||
var instance = (AssetReference)Activator.CreateInstance(fieldType);
|
||||
_assetType = instance.AssetType;
|
||||
return _assetType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee2a8d68b06fc434c8d70a9e1d94caf6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd8a9e643275082438da0f2f5c4f7f68
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 扩展的 IMGUI 绘制辅助方法集合
|
||||
/// </summary>
|
||||
internal static class BundleCollectorGUIDraw
|
||||
{
|
||||
/// <summary>
|
||||
/// 绘制区块标题
|
||||
/// </summary>
|
||||
/// <param name="title">标题文本</param>
|
||||
public static void DrawSectionTitle(string title)
|
||||
{
|
||||
EditorGUILayout.LabelField(title, BundleCollectorGUIStyle.TitleStyle);
|
||||
EditorGUILayout.Space(2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制一个带字段名的只读文本字段
|
||||
/// </summary>
|
||||
/// <param name="label">字段名</param>
|
||||
/// <param name="value">字段值</param>
|
||||
/// <param name="disabled">是否置灰整行</param>
|
||||
public static void DrawLabelField(string label, string value, bool disabled = false)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(disabled))
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, BundleCollectorGUIStyle.FieldLabelStyle, GUILayout.Width(EditorGUIUtility.labelWidth));
|
||||
EditorGUILayout.LabelField(string.IsNullOrEmpty(value) ? "-" : value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制一个带字段名的延迟文本输入框
|
||||
/// </summary>
|
||||
/// <param name="label">字段名</param>
|
||||
/// <param name="value">当前文本值</param>
|
||||
/// <returns>新的文本值</returns>
|
||||
public static string DrawTextField(string label, string value)
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, BundleCollectorGUIStyle.FieldLabelStyle, GUILayout.Width(EditorGUIUtility.labelWidth));
|
||||
return EditorGUILayout.DelayedTextField(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制一个带字段名的下拉框
|
||||
/// </summary>
|
||||
/// <param name="label">字段名</param>
|
||||
/// <param name="index">当前选项索引</param>
|
||||
/// <param name="displayedOptions">下拉选项</param>
|
||||
/// <returns>新的选项索引</returns>
|
||||
public static int DrawPopupField(string label, int index, string[] displayedOptions)
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, BundleCollectorGUIStyle.FieldLabelStyle, GUILayout.Width(EditorGUIUtility.labelWidth));
|
||||
return EditorGUILayout.Popup(index, displayedOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制规则选择行
|
||||
/// </summary>
|
||||
/// <param name="label">字段标签</param>
|
||||
/// <param name="displayNames">显示名称列表</param>
|
||||
/// <param name="currentIndex">当前选中索引</param>
|
||||
/// <param name="newIndex">新选中的索引</param>
|
||||
/// <returns>规则索引发生变化返回 true</returns>
|
||||
public static bool TryDrawRuleSelection(string label, string[] displayNames, int currentIndex, out int newIndex)
|
||||
{
|
||||
newIndex = -1;
|
||||
if (displayNames == null || displayNames.Length == 0)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(true)) DrawPopupField(label, 0, new[] { "<None>" });
|
||||
return false;
|
||||
}
|
||||
|
||||
currentIndex = Mathf.Clamp(currentIndex, 0, displayNames.Length - 1);
|
||||
newIndex = DrawPopupField(label, currentIndex, displayNames);
|
||||
return newIndex != currentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb2a2401e7c68a64496c3194543aff6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 扩展的 IMGUI 样式集合
|
||||
/// </summary>
|
||||
internal static class BundleCollectorGUIStyle
|
||||
{
|
||||
// 注意:GUIStyle 依赖 GUI 皮肤,不能在静态构造里创建,需延迟到绘制时初始化。
|
||||
private static GUIStyle _titleStyle;
|
||||
private static GUIStyle _fieldLabelStyle;
|
||||
private static GUIStyle _sectionStyle;
|
||||
|
||||
/// <summary>
|
||||
/// 区块标题样式:保持正文字号,仅通过加粗和间距与内容区分。
|
||||
/// </summary>
|
||||
public static GUIStyle TitleStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_titleStyle == null)
|
||||
{
|
||||
_titleStyle = new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
fontStyle = FontStyle.Bold,
|
||||
margin = new RectOffset(0, 0, 2, 4),
|
||||
};
|
||||
}
|
||||
return _titleStyle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字段名样式:保持正文字号。
|
||||
/// </summary>
|
||||
public static GUIStyle FieldLabelStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_fieldLabelStyle == null)
|
||||
{
|
||||
_fieldLabelStyle = new GUIStyle(EditorStyles.label)
|
||||
{
|
||||
fontStyle = FontStyle.Normal,
|
||||
};
|
||||
}
|
||||
return _fieldLabelStyle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分组卡片样式:基于 helpBox 增加内边距,让内容不贴边。
|
||||
/// </summary>
|
||||
public static GUIStyle SectionStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_sectionStyle == null)
|
||||
{
|
||||
_sectionStyle = new GUIStyle(EditorStyles.helpBox)
|
||||
{
|
||||
padding = new RectOffset(6, 6, 5, 6),
|
||||
};
|
||||
}
|
||||
return _sectionStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f37650836ac1ca4ab53da11208bace4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,456 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源收集器的 Inspector 扩展
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
internal static class BundleCollectorInspector
|
||||
{
|
||||
private struct CollectorContext
|
||||
{
|
||||
public BundleCollectorPackage Package;
|
||||
public BundleCollectorGroup Group;
|
||||
public BundleCollector Collector;
|
||||
}
|
||||
|
||||
private static readonly string[] CollectorTypeNames =
|
||||
{
|
||||
nameof(ECollectorType.MainAssetCollector),
|
||||
nameof(ECollectorType.StaticAssetCollector),
|
||||
nameof(ECollectorType.DependAssetCollector),
|
||||
};
|
||||
|
||||
private static int _createPackageIndex;
|
||||
private static int _createGroupIndex;
|
||||
|
||||
static BundleCollectorInspector()
|
||||
{
|
||||
UnityEditor.Editor.finishedDefaultHeaderGUI -= OnPostHeaderGUI;
|
||||
UnityEditor.Editor.finishedDefaultHeaderGUI += OnPostHeaderGUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inspector 默认头部绘制完成后的回调
|
||||
/// </summary>
|
||||
/// <param name="editor">当前正在绘制的 Inspector 编辑器实例</param>
|
||||
private static void OnPostHeaderGUI(UnityEditor.Editor editor)
|
||||
{
|
||||
// 注意:多目标选择的时候不绘制
|
||||
if (editor.targets != null && editor.targets.Length > 1)
|
||||
return;
|
||||
|
||||
UnityEngine.Object target = editor.target;
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
// 检测选择的路径是否合法
|
||||
string assetPath = AssetDatabase.GetAssetPath(target);
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
return;
|
||||
if (assetPath == "Assets")
|
||||
return;
|
||||
if (assetPath.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase) == false)
|
||||
return;
|
||||
if (AssetDatabase.IsValidFolder(assetPath) == false)
|
||||
return;
|
||||
|
||||
// 检测配置文件是否存在
|
||||
if (BundleCollectorSettingData.HasSettingAsset() == false)
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("YooAsset", "BundleCollectorSetting.asset not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据当前文件夹是否已经配置,动态切换展示模式。
|
||||
EditorGUILayout.Space();
|
||||
using (new EditorGUILayout.VerticalScope(BundleCollectorGUIStyle.SectionStyle))
|
||||
{
|
||||
bool isFindCollector = TryGetCollector(assetPath, out CollectorContext collectorContext);
|
||||
if (isFindCollector)
|
||||
{
|
||||
DrawOpenCollectorHeader(collectorContext);
|
||||
DrawTargetCollectorContent(collectorContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawCreateCollectorHeader(assetPath);
|
||||
DrawCreateCollectorSelector();
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void DrawCreateCollectorHeader(string assetPath)
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
EditorGUILayout.LabelField("YooAsset", EditorStyles.boldLabel, GUILayout.Width(95));
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
bool hasCreateTarget = TryGetCreateCollectorTarget(out var package, out var group);
|
||||
using (new EditorGUI.DisabledScope(hasCreateTarget == false))
|
||||
{
|
||||
if (GUILayout.Button("Create Collector", GUILayout.Width(120)))
|
||||
{
|
||||
TryAddCollector(assetPath, package, group, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void DrawCreateCollectorSelector()
|
||||
{
|
||||
using (new EditorGUILayout.VerticalScope(BundleCollectorGUIStyle.SectionStyle))
|
||||
{
|
||||
BundleCollectorGUIDraw.DrawSectionTitle("Package & Group");
|
||||
|
||||
var setting = BundleCollectorSettingData.Setting;
|
||||
if (setting == null || setting.Packages.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Please create a Package in the Bundle Collector window first.", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
ClampCreateTargetIndices();
|
||||
string[] packageNames = setting.Packages.Select(item => item.PackageName).ToArray();
|
||||
int newPackageIndex = BundleCollectorGUIDraw.DrawPopupField("Package", _createPackageIndex, packageNames);
|
||||
if (newPackageIndex != _createPackageIndex)
|
||||
{
|
||||
_createPackageIndex = newPackageIndex;
|
||||
_createGroupIndex = 0;
|
||||
}
|
||||
|
||||
var package = setting.Packages[_createPackageIndex];
|
||||
if (package.Groups.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Please create a Group in the Bundle Collector window first.", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
string[] groupNames = package.Groups.Select(item => item.GroupName).ToArray();
|
||||
_createGroupIndex = BundleCollectorGUIDraw.DrawPopupField("Group", _createGroupIndex, groupNames);
|
||||
}
|
||||
}
|
||||
private static void DrawOpenCollectorHeader(CollectorContext collectorContext)
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
EditorGUILayout.LabelField("YooAsset", EditorStyles.boldLabel, GUILayout.Width(95));
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Open Collector", GUILayout.Width(120)))
|
||||
{
|
||||
BundleCollectorWindow.OpenWindow(collectorContext.Package.PackageName, collectorContext.Group.GroupName, collectorContext.Collector.CollectPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void DrawTargetCollectorContent(CollectorContext collectorContext)
|
||||
{
|
||||
var package = collectorContext.Package;
|
||||
var group = collectorContext.Group;
|
||||
var collector = collectorContext.Collector;
|
||||
var setting = BundleCollectorSettingData.Setting;
|
||||
bool showAlias = setting.ShowEditorAlias;
|
||||
|
||||
float oldLabelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth = 100;
|
||||
|
||||
try
|
||||
{
|
||||
// 区块:包裹与分组
|
||||
using (new EditorGUILayout.VerticalScope(BundleCollectorGUIStyle.SectionStyle))
|
||||
{
|
||||
BundleCollectorGUIDraw.DrawSectionTitle("Package & Group");
|
||||
|
||||
// 包裹
|
||||
var packageNames = setting.Packages.Select(p => p.PackageName).ToArray();
|
||||
int packageIndex = Mathf.Max(0, Array.IndexOf(packageNames, package.PackageName));
|
||||
int newPackageIndex = BundleCollectorGUIDraw.DrawPopupField("Package", packageIndex, packageNames);
|
||||
if (newPackageIndex != packageIndex && MoveCollectorToPackage(collector, group, setting.Packages[newPackageIndex]))
|
||||
return;
|
||||
|
||||
// 分组
|
||||
var groupNames = package.Groups.Select(g => g.GroupName).ToArray();
|
||||
int groupIndex = Mathf.Max(0, Array.IndexOf(groupNames, group.GroupName));
|
||||
int newGroupIndex = BundleCollectorGUIDraw.DrawPopupField("Group", groupIndex, groupNames);
|
||||
if (newGroupIndex != groupIndex)
|
||||
{
|
||||
MoveCollectorToGroup(collector, group, package.Groups[newGroupIndex]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 区块:收集器配置
|
||||
using (new EditorGUILayout.VerticalScope(BundleCollectorGUIStyle.SectionStyle))
|
||||
{
|
||||
BundleCollectorGUIDraw.DrawSectionTitle("Collector Settings");
|
||||
BundleCollectorGUIDraw.DrawLabelField("Collect Path", collector.CollectPath);
|
||||
|
||||
// 收集器类型
|
||||
int typeIndex = Mathf.Max(0, Array.IndexOf(CollectorTypeNames, collector.CollectorType.ToString()));
|
||||
int newTypeIndex = BundleCollectorGUIDraw.DrawPopupField("Collector Type", typeIndex, CollectorTypeNames);
|
||||
if (newTypeIndex != typeIndex)
|
||||
{
|
||||
RecordUndo("YooAsset.Inspector Modify CollectorType");
|
||||
collector.CollectorType = EditorStringUtility.ParseEnum<ECollectorType>(CollectorTypeNames[newTypeIndex]);
|
||||
CommitModify(group, collector);
|
||||
}
|
||||
|
||||
// 地址规则(仅对主资源收集器生效)
|
||||
bool isMainCollector = collector.CollectorType == ECollectorType.MainAssetCollector;
|
||||
using (new EditorGUI.DisabledScope(isMainCollector == false))
|
||||
{
|
||||
var addressRules = BundleCollectorSettingData.GetAddressRuleNames();
|
||||
if (BundleCollectorGUIDraw.TryDrawRuleSelection("Address Rule", GetRuleDisplayNames(addressRules, showAlias), GetRuleIndex(addressRules, collector.AddressRuleName), out int newAddressRuleIndex))
|
||||
{
|
||||
RecordUndo("YooAsset.Inspector Modify AddressRule");
|
||||
collector.AddressRuleName = addressRules[newAddressRuleIndex].ClassName;
|
||||
CommitModify(group, collector);
|
||||
}
|
||||
}
|
||||
|
||||
// 打包规则
|
||||
var packRules = BundleCollectorSettingData.GetBundlePackRuleNames();
|
||||
if (BundleCollectorGUIDraw.TryDrawRuleSelection("Pack Rule", GetRuleDisplayNames(packRules, showAlias), GetRuleIndex(packRules, collector.PackRuleName), out int newPackRuleIndex))
|
||||
{
|
||||
RecordUndo("YooAsset.Inspector Modify PackRule");
|
||||
collector.PackRuleName = packRules[newPackRuleIndex].ClassName;
|
||||
CommitModify(group, collector);
|
||||
}
|
||||
|
||||
// 过滤规则
|
||||
var filterRules = BundleCollectorSettingData.GetAssetFilterRuleNames();
|
||||
if (BundleCollectorGUIDraw.TryDrawRuleSelection("Filter Rule", GetRuleDisplayNames(filterRules, showAlias), GetRuleIndex(filterRules, collector.FilterRuleName), out int newFilterRuleIndex))
|
||||
{
|
||||
RecordUndo("YooAsset.Inspector Modify FilterRule");
|
||||
collector.FilterRuleName = filterRules[newFilterRuleIndex].ClassName;
|
||||
CommitModify(group, collector);
|
||||
}
|
||||
|
||||
// 用户数据(延迟提交,避免逐键写盘)
|
||||
string newUserData = BundleCollectorGUIDraw.DrawTextField("UserData", collector.UserData);
|
||||
if (newUserData != collector.UserData)
|
||||
{
|
||||
RecordUndo("YooAsset.Inspector Modify UserData");
|
||||
collector.UserData = newUserData;
|
||||
CommitModify(group, collector);
|
||||
}
|
||||
|
||||
// 资源标签(仅对主资源收集器生效)
|
||||
using (new EditorGUI.DisabledScope(isMainCollector == false))
|
||||
{
|
||||
string newTags = BundleCollectorGUIDraw.DrawTextField("Tags", collector.AssetTags);
|
||||
if (newTags != collector.AssetTags)
|
||||
{
|
||||
RecordUndo("YooAsset.Inspector Modify AssetTags");
|
||||
collector.AssetTags = newTags;
|
||||
CommitModify(group, collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorGUIUtility.labelWidth = oldLabelWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取规则列表用于下拉框展示的名称数组
|
||||
/// </summary>
|
||||
private static string[] GetRuleDisplayNames(List<RuleDisplayName> rules, bool showAlias)
|
||||
{
|
||||
if (rules == null || rules.Count == 0)
|
||||
return Array.Empty<string>();
|
||||
|
||||
return rules.Select(rule => showAlias ? rule.DisplayName : rule.ClassName).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据规则类名查找规则在列表中的索引
|
||||
/// </summary>
|
||||
private static int GetRuleIndex(List<RuleDisplayName> rules, string className)
|
||||
{
|
||||
if (rules == null || rules.Count == 0)
|
||||
return -1;
|
||||
|
||||
return Mathf.Max(0, rules.FindIndex(rule => rule.ClassName == className));
|
||||
}
|
||||
|
||||
#region 数据查找与编辑
|
||||
/// <summary>
|
||||
/// 获取当前创建收集器所选择的目标包裹和分组
|
||||
/// </summary>
|
||||
private static bool TryGetCreateCollectorTarget(out BundleCollectorPackage package, out BundleCollectorGroup group)
|
||||
{
|
||||
package = null;
|
||||
group = null;
|
||||
|
||||
var setting = BundleCollectorSettingData.Setting;
|
||||
if (setting == null || setting.Packages.Count == 0)
|
||||
return false;
|
||||
|
||||
ClampCreateTargetIndices();
|
||||
package = setting.Packages[_createPackageIndex];
|
||||
if (package.Groups.Count == 0)
|
||||
return false;
|
||||
|
||||
group = package.Groups[_createGroupIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将创建目标的包裹/分组索引收敛到当前配置的合法范围
|
||||
/// </summary>
|
||||
private static void ClampCreateTargetIndices()
|
||||
{
|
||||
var setting = BundleCollectorSettingData.Setting;
|
||||
if (setting == null || setting.Packages.Count == 0)
|
||||
{
|
||||
_createPackageIndex = 0;
|
||||
_createGroupIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_createPackageIndex = Mathf.Clamp(_createPackageIndex, 0, setting.Packages.Count - 1);
|
||||
var package = setting.Packages[_createPackageIndex];
|
||||
if (package.Groups.Count == 0)
|
||||
{
|
||||
_createGroupIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_createGroupIndex = Mathf.Clamp(_createGroupIndex, 0, package.Groups.Count - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按文件夹路径精确获取已配置的收集器信息
|
||||
/// </summary>
|
||||
private static bool TryGetCollector(string folderPath, out CollectorContext result)
|
||||
{
|
||||
result = default;
|
||||
|
||||
var setting = BundleCollectorSettingData.Setting;
|
||||
if (setting == null)
|
||||
return false;
|
||||
|
||||
foreach (var package in setting.Packages)
|
||||
{
|
||||
foreach (var group in package.Groups)
|
||||
{
|
||||
foreach (var collector in group.Collectors)
|
||||
{
|
||||
if (string.Equals(collector.CollectPath, folderPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result = new CollectorContext
|
||||
{
|
||||
Package = package,
|
||||
Group = group,
|
||||
Collector = collector,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为指定文件夹在目标包裹分组下创建收集器
|
||||
/// </summary>
|
||||
private static bool TryAddCollector(string folderPath, BundleCollectorPackage package, BundleCollectorGroup group, out CollectorContext result)
|
||||
{
|
||||
result = default;
|
||||
if (package == null || group == null)
|
||||
return false;
|
||||
|
||||
RecordUndo("YooAsset.Inspector Add Collector");
|
||||
var collector = new BundleCollector
|
||||
{
|
||||
CollectPath = folderPath,
|
||||
CollectorGUID = AssetDatabase.AssetPathToGUID(folderPath),
|
||||
};
|
||||
BundleCollectorSettingData.CreateCollector(group, collector);
|
||||
BundleCollectorSettingData.SaveFile();
|
||||
|
||||
result = new CollectorContext
|
||||
{
|
||||
Package = package,
|
||||
Group = group,
|
||||
Collector = collector,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将收集器移动到目标包裹的第一个分组
|
||||
/// </summary>
|
||||
/// <param name="collector">要移动的收集器实例</param>
|
||||
/// <param name="fromGroup">原分组</param>
|
||||
/// <param name="toPackage">目标包裹</param>
|
||||
/// <returns>目标包裹无分组或无需移动时返回 false</returns>
|
||||
private static bool MoveCollectorToPackage(BundleCollector collector, BundleCollectorGroup fromGroup, BundleCollectorPackage toPackage)
|
||||
{
|
||||
if (toPackage.Groups.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"Package '{toPackage.PackageName}' has no group. Please create a group first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var toGroup = toPackage.Groups[0];
|
||||
if (toGroup == fromGroup)
|
||||
return false;
|
||||
|
||||
RecordUndo("YooAsset.Inspector Move Collector Package");
|
||||
fromGroup.Collectors.Remove(collector);
|
||||
toGroup.Collectors.Add(collector);
|
||||
BundleCollectorSettingData.ModifyCollector(toGroup, collector);
|
||||
BundleCollectorSettingData.SaveFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将收集器在同一包裹内移动到目标分组
|
||||
/// </summary>
|
||||
/// <param name="collector">要移动的收集器实例</param>
|
||||
/// <param name="fromGroup">原分组</param>
|
||||
/// <param name="toGroup">目标分组</param>
|
||||
private static void MoveCollectorToGroup(BundleCollector collector, BundleCollectorGroup fromGroup, BundleCollectorGroup toGroup)
|
||||
{
|
||||
if (toGroup == fromGroup)
|
||||
return;
|
||||
|
||||
RecordUndo("YooAsset.Inspector Move Collector Group");
|
||||
fromGroup.Collectors.Remove(collector);
|
||||
toGroup.Collectors.Add(collector);
|
||||
BundleCollectorSettingData.ModifyCollector(toGroup, collector);
|
||||
BundleCollectorSettingData.SaveFile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记收集器已修改并持久化配置文件
|
||||
/// </summary>
|
||||
/// <param name="group">收集器所属分组</param>
|
||||
/// <param name="collector">被修改的收集器</param>
|
||||
private static void CommitModify(BundleCollectorGroup group, BundleCollector collector)
|
||||
{
|
||||
BundleCollectorSettingData.ModifyCollector(group, collector);
|
||||
BundleCollectorSettingData.SaveFile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在修改配置前登记 Undo,使操作可撤销。
|
||||
/// </summary>
|
||||
/// <param name="name">Undo 操作名称</param>
|
||||
private static void RecordUndo(string name)
|
||||
{
|
||||
Undo.RecordObject(BundleCollectorSettingData.Setting, name);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f06fac18179b404988b68a675c421af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,19 +3,19 @@ using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏对象弱引用,序列化时只保存资源 GUID
|
||||
/// 资源弱引用基类
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class GameObjectReference
|
||||
public abstract class AssetReference
|
||||
{
|
||||
[SerializeField]
|
||||
private string _packageName = "DefaultPackage";
|
||||
protected string _packageName = "DefaultPackage";
|
||||
|
||||
[SerializeField]
|
||||
private string _assetGUID = "";
|
||||
protected string _assetGUID = "";
|
||||
|
||||
[NonSerialized]
|
||||
private AssetHandle _handle;
|
||||
protected AssetHandle _handle;
|
||||
|
||||
/// <summary>
|
||||
/// 资源所属的包裹名称
|
||||
@@ -27,6 +27,16 @@ public class GameObjectReference
|
||||
/// </summary>
|
||||
public string AssetGUID => _assetGUID;
|
||||
|
||||
/// <summary>
|
||||
/// 当前加载句柄(未加载时为 null)
|
||||
/// </summary>
|
||||
public AssetHandle Handle => _handle;
|
||||
|
||||
/// <summary>
|
||||
/// 该引用负责加载的资源类型,由子类指定
|
||||
/// </summary>
|
||||
public abstract Type AssetType { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 检查运行时引用键是否有效
|
||||
@@ -37,18 +47,18 @@ public class GameObjectReference
|
||||
return false;
|
||||
|
||||
var package = YooAssets.GetPackage(_packageName);
|
||||
var assetInfo = package.GetAssetInfoByGuid(_assetGUID, typeof(GameObject));
|
||||
var assetInfo = package.GetAssetInfoByGuid(_assetGUID, AssetType);
|
||||
return assetInfo.IsValid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载引用的游戏对象
|
||||
/// 异步加载引用的资源
|
||||
/// </summary>
|
||||
/// <returns>加载操作句柄</returns>
|
||||
public AssetHandle LoadAssetAsync()
|
||||
{
|
||||
if (_handle != null)
|
||||
throw new InvalidOperationException("GameObject reference has already been loaded. Release it first.");
|
||||
throw new InvalidOperationException($"{GetType().Name} has already been loaded. Release it first.");
|
||||
|
||||
if (string.IsNullOrEmpty(_packageName))
|
||||
throw new ArgumentException("Package name is not set.", nameof(_packageName));
|
||||
@@ -56,7 +66,7 @@ public class GameObjectReference
|
||||
throw new ArgumentException("Asset GUID is not set.", nameof(_assetGUID));
|
||||
|
||||
var package = YooAssets.GetPackage(_packageName);
|
||||
var assetInfo = package.GetAssetInfoByGuid(_assetGUID, typeof(GameObject));
|
||||
var assetInfo = package.GetAssetInfoByGuid(_assetGUID, AssetType);
|
||||
_handle = package.LoadAssetAsync(assetInfo);
|
||||
return _handle;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7de4fe4f4d6834c47977a97c7ad20bd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// GameObject 资源弱引用
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AssetReferenceGameObject : AssetReference
|
||||
{
|
||||
public override Type AssetType => typeof(GameObject);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 764d480d7edd8cd48b8105dc6b24bd2a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,36 +3,58 @@ using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
/// <summary>
|
||||
/// 演示如何使用 GameObjectReference 弱引用加载并实例化游戏对象
|
||||
/// 演示如何使用 AssetReference 弱引用加载不同类型的资源
|
||||
/// </summary>
|
||||
public class AssetReferenceSample : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private GameObjectReference _reference;
|
||||
private AssetReferenceGameObject _prefabReference;
|
||||
|
||||
[SerializeField]
|
||||
private AssetReferenceTexture2D _textureReference;
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
if (_reference.RuntimeKeyIsValid() == false)
|
||||
// 加载并实例化预制体
|
||||
if (_prefabReference.RuntimeKeyIsValid())
|
||||
{
|
||||
yield break;
|
||||
AssetHandle handle = _prefabReference.LoadAssetAsync();
|
||||
yield return handle;
|
||||
|
||||
if (handle.Status == EOperationStatus.Succeeded)
|
||||
{
|
||||
GameObject instance = handle.InstantiateSync(new InstantiateOptions(true, transform, false));
|
||||
if (instance == null)
|
||||
Debug.LogError($"Failed to instantiate GameObject reference '{_prefabReference.AssetGUID}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Failed to load GameObject reference '{_prefabReference.AssetGUID}': {handle.Error}.");
|
||||
}
|
||||
}
|
||||
|
||||
AssetHandle handle = _reference.LoadAssetAsync();
|
||||
yield return handle;
|
||||
// 加载纹理并赋值给渲染器材质
|
||||
if (_textureReference.RuntimeKeyIsValid())
|
||||
{
|
||||
AssetHandle handle = _textureReference.LoadAssetAsync();
|
||||
yield return handle;
|
||||
|
||||
if (handle.Status == EOperationStatus.Succeeded)
|
||||
{
|
||||
GameObject instance = handle.InstantiateSync(new InstantiateOptions(true, transform, false));
|
||||
if (instance == null)
|
||||
Debug.LogError($"Failed to instantiate GameObject reference '{_reference.AssetGUID}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Failed to load GameObject reference '{_reference.AssetGUID}': {handle.Error}.");
|
||||
if (handle.Status == EOperationStatus.Succeeded)
|
||||
{
|
||||
var renderer = GetComponent<Renderer>();
|
||||
if (renderer != null)
|
||||
renderer.material.mainTexture = handle.AssetObject as Texture2D;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Failed to load Texture2D reference '{_textureReference.AssetGUID}': {handle.Error}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_reference.ReleaseAsset();
|
||||
_prefabReference?.ReleaseAsset();
|
||||
_textureReference?.ReleaseAsset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Texture2D 资源弱引用
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AssetReferenceTexture2D : AssetReference
|
||||
{
|
||||
public override Type AssetType => typeof(Texture2D);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c6c6d8423826d24083eb0608e0fd023
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Texture3D 资源弱引用
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AssetReferenceTexture3D : AssetReference
|
||||
{
|
||||
public override Type AssetType => typeof(Texture3D);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be5c51911e8f45a45b7ed11af7d0b50c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -11,14 +11,14 @@ public static class AlipayFileSystemCreater
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawbundleDecryptor, rawBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawBundleDecryptor, rawBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# 支付宝小游戏文件系统
|
||||
|
||||
该示例用于在 YooAsset 的 WebGL 运行模式下接入支付宝小游戏。
|
||||
|
||||
参考文档:[支付宝小游戏开发文档](https://opendocs.alipay.com/mini-game/)
|
||||
|
||||
## 环境要求
|
||||
|
||||
先安装支付宝小游戏 Unity/团结 WebGL 适配 SDK,并将项目切换到 WebGL 构建目标。
|
||||
|
||||
在 WebGL Player 的 Scripting Define Symbols 中启用以下宏:
|
||||
|
||||
- `UNITY_ALIMINIGAME`
|
||||
|
||||
该宏是 YooAsset 支付宝小游戏示例约定的编译开关,用于和其它小游戏平台适配代码保持一致。
|
||||
|
||||
如果启用宏后编译提示找不到 `AlipaySdk`、`APAssetBundle` 或 `DownloadHandlerAPAssetBundle`,请确认支付宝 SDK 已导入,并将支付宝 SDK 的程序集引用添加到 `YooAsset.MiniGame.asmdef`。
|
||||
|
||||
## 初始化 YooAsset
|
||||
|
||||
在支付宝小游戏构建中初始化 `WebPlayModeOptions` 时,使用 `AlipayFileSystemCreater` 创建文件系统参数。
|
||||
|
||||
```csharp
|
||||
#if UNITY_WEBGL && UNITY_ALIMINIGAME && !UNITY_EDITOR
|
||||
var createParameters = new WebPlayModeOptions();
|
||||
|
||||
string defaultHostServer = GetHostServerURL();
|
||||
string fallbackHostServer = GetHostServerURL();
|
||||
IRemoteService remoteService = new RemoteService(defaultHostServer, fallbackHostServer);
|
||||
|
||||
createParameters.WebServerFileSystemParameters =
|
||||
AlipayFileSystemCreater.CreateFileSystemParameters(remoteService);
|
||||
|
||||
var initializationOperation = package.InitializePackageAsync(createParameters);
|
||||
#endif
|
||||
```
|
||||
|
||||
支付宝小游戏底层会对远程 AssetBundle 请求做平台适配,业务侧仍然按照远程异步加载流程使用 YooAsset。
|
||||
|
||||
## 资源包命名
|
||||
|
||||
支付宝小游戏构建推荐让资源包文件名携带 hash。YooAsset 推荐只使用 `HashName` 文件命名风格。
|
||||
|
||||
`HashName` 会生成纯 hash 文件名,例如:
|
||||
|
||||
```text
|
||||
8d265a9dfd6cb7669cdb8b726f0afb1e.bundle
|
||||
```
|
||||
|
||||
该命名方式更适合小游戏平台的缓存和更新识别,也能避免暴露原始 Bundle 名称。支付宝小游戏构建不建议使用 `BundleName` 或 `BundleName_HashName`。
|
||||
|
||||
## 注意事项
|
||||
|
||||
加密 AssetBundle 仍然会走 YooAsset 常规的 Web 下载和解密流程。非加密 AssetBundle 会使用支付宝平台适配器,并通过 `APAssetBundle.GetAssetBundle` 发起请求。
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fcf69ed8b0d43141bdf2412c9822ddc
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -11,14 +11,14 @@ public static class KuaiShouFileSystemCreater
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawbundleDecryptor, rawBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawBundleDecryptor, rawBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@ public static class OppoFileSystemCreater
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawbundleDecryptor, rawBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawBundleDecryptor, rawBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# TapTap 小游戏文件系统
|
||||
|
||||
该示例用于在 YooAsset 的 WebGL 运行模式下接入 TapTap 小游戏。
|
||||
|
||||
参考文档:[TapTap 小游戏 Unity 适配指南](https://developer.taptap.cn/minigameapidoc/dev/engine/unity-adaptation/guide/)
|
||||
|
||||
## 环境要求
|
||||
|
||||
先安装 TapTap 小游戏 Unity/团结 WebGL 适配 SDK,并将项目切换到 WebGL 构建目标。
|
||||
|
||||
在 WebGL Player 的 Scripting Define Symbols 中启用以下宏:
|
||||
|
||||
- `TAPMINIGAME`
|
||||
|
||||
该宏是 YooAsset TapTap 小游戏示例约定的编译开关,用于和其它小游戏平台适配代码保持一致。
|
||||
|
||||
如果启用宏后编译提示找不到 `TapTapMiniGame`、`TapAssetBundle` 或 `DownloadHandlerTapAssetBundle`,请确认 TapTap SDK 已导入,并将 TapTap SDK 的程序集引用添加到 `YooAsset.MiniGame.asmdef`。
|
||||
|
||||
## 初始化 YooAsset
|
||||
|
||||
在 TapTap 小游戏构建中初始化 `WebPlayModeOptions` 时,使用 `TaptapFileSystemCreater` 创建文件系统参数。
|
||||
|
||||
```csharp
|
||||
#if UNITY_WEBGL && TAPMINIGAME && !UNITY_EDITOR
|
||||
var createParameters = new WebPlayModeOptions();
|
||||
|
||||
string defaultHostServer = GetHostServerURL();
|
||||
string fallbackHostServer = GetHostServerURL();
|
||||
IRemoteService remoteService = new RemoteService(defaultHostServer, fallbackHostServer);
|
||||
|
||||
createParameters.WebServerFileSystemParameters =
|
||||
TaptapFileSystemCreater.CreateFileSystemParameters(remoteService);
|
||||
|
||||
var initializationOperation = package.InitializePackageAsync(createParameters);
|
||||
#endif
|
||||
```
|
||||
|
||||
TapTap 小游戏底层会对远程 AssetBundle 请求做平台适配,业务侧仍然按照远程异步加载流程使用 YooAsset。
|
||||
|
||||
## 资源包命名
|
||||
|
||||
TapTap 小游戏构建推荐让资源包文件名携带 hash。YooAsset 推荐只使用 `HashName` 文件命名风格。
|
||||
|
||||
`HashName` 会生成纯 hash 文件名,例如:
|
||||
|
||||
```text
|
||||
8d265a9dfd6cb7669cdb8b726f0afb1e.bundle
|
||||
```
|
||||
|
||||
该命名方式更适合小游戏平台的缓存和更新识别,也能避免暴露原始 Bundle 名称。TapTap 小游戏构建不建议使用 `BundleName` 或 `BundleName_HashName`。
|
||||
|
||||
## 注意事项
|
||||
|
||||
加密 AssetBundle 仍然会走 YooAsset 常规的 Web 下载和解密流程。非加密 AssetBundle 会使用 TapTap 平台适配器,并通过 `TapAssetBundle.GetAssetBundle` 发起请求。
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52a8b3cdfd92faf4d8ed04f35b602210
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,14 +12,14 @@ public static class TaptapFileSystemCreater
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawbundleDecryptor, rawBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawBundleDecryptor, rawBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@ public static class TiktokFileSystemCreater
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawbundleDecryptor, rawBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawBundleDecryptor, rawBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@ public static class VivoFileSystemCreater
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
public static FileSystemParameters CreateFileSystemParameters(IRemoteService remoteService, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor)
|
||||
{
|
||||
var fileSystemParams = CreateBaseFileSystemParameters(remoteService);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawbundleDecryptor, rawBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawBundleDecryptor, rawBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ public static class WechatFileSystemCreater
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.WebPlatformStrategy, new WechatPlatform());
|
||||
|
||||
if (assetBundleDecryptor != null)
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetbundleDecryptor, assetBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.AssetBundleDecryptor, assetBundleDecryptor);
|
||||
if (rawBundleDecryptor != null)
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawbundleDecryptor, rawBundleDecryptor);
|
||||
fileSystemParams.AddParameter(EFileSystemParameter.RawBundleDecryptor, rawBundleDecryptor);
|
||||
return fileSystemParams;
|
||||
}
|
||||
}
|
||||
@@ -87,4 +87,4 @@ internal class WechatFileSystem : WebNetworkFileSystem
|
||||
return _wxCacheRoot;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
#if YOOASSET_UNITASK_SUPPORT
|
||||
#if UNITY_2020_1_OR_NEWER && ! UNITY_2021
|
||||
#define UNITY_2020_BUG
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using YooAsset;
|
||||
@@ -40,7 +36,6 @@ namespace Cysharp.Threading.Tasks
|
||||
TaskPool.RegisterSizeGetter(typeof(HandleBaseConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<HandleBase> completedCallback;
|
||||
HandleBase handle;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
@@ -52,7 +47,6 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
HandleBaseConfiguredSource()
|
||||
{
|
||||
completedCallback = HandleCompleted;
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(HandleBase handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
@@ -85,11 +79,7 @@ namespace Cysharp.Threading.Tasks
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
// BUG 在 Unity 2020.3.36 版本测试中, IL2Cpp 会报 如下错误
|
||||
// BUG ArgumentException: Incompatible Delegate Types. First is System.Action`1[[YooAsset.AssetHandle, YooAsset, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]] second is System.Action`1[[YooAsset.HandleBase, YooAsset, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]
|
||||
// BUG 也可能报的是 Action '1' Action '1' 的 InvalidCastException
|
||||
// BUG 此处不得不这么修改, 如果后续 Unity 修复了这个问题, 可以恢复之前的写法
|
||||
#if UNITY_2020_BUG
|
||||
// 注意:统一用强类型回调订阅 Handle.Completed,修复 IL2CPP 逆变委托崩溃
|
||||
switch (handle)
|
||||
{
|
||||
case AssetHandle asset_handle:
|
||||
@@ -108,38 +98,16 @@ namespace Cysharp.Threading.Tasks
|
||||
all_assets_handle.Completed += result.AllAssetsContinuation;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
switch (handle)
|
||||
{
|
||||
case AssetHandle asset_handle:
|
||||
asset_handle.Completed += result.completedCallback;
|
||||
break;
|
||||
case SceneHandle scene_handle:
|
||||
scene_handle.Completed += result.completedCallback;
|
||||
break;
|
||||
case SubAssetsHandle sub_asset_handle:
|
||||
sub_asset_handle.Completed += result.completedCallback;
|
||||
break;
|
||||
case BundleFileHandle bundle_file_handle:
|
||||
bundle_file_handle.Completed += result.completedCallback;
|
||||
break;
|
||||
case AllAssetsHandle all_assets_handle:
|
||||
all_assets_handle.Completed += result.completedCallback;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
#if UNITY_2020_BUG
|
||||
void AssetContinuation(AssetHandle _) => HandleCompleted(null);
|
||||
void SceneContinuation(SceneHandle _) => HandleCompleted(null);
|
||||
void SubContinuation(SubAssetsHandle _) => HandleCompleted(null);
|
||||
void BundleFileContinuation(BundleFileHandle _) => HandleCompleted(null);
|
||||
void AllAssetsContinuation(AllAssetsHandle _) => HandleCompleted(null);
|
||||
#endif
|
||||
|
||||
void HandleCompleted(HandleBase _)
|
||||
{
|
||||
@@ -162,7 +130,6 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
if (handle == null || !handle.IsValid) return;
|
||||
|
||||
#if UNITY_2020_BUG
|
||||
switch (handle)
|
||||
{
|
||||
case AssetHandle asset_handle:
|
||||
@@ -181,26 +148,6 @@ namespace Cysharp.Threading.Tasks
|
||||
all_assets_handle.Completed -= AllAssetsContinuation;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
switch (handle)
|
||||
{
|
||||
case AssetHandle asset_handle:
|
||||
asset_handle.Completed -= completedCallback;
|
||||
break;
|
||||
case SceneHandle scene_handle:
|
||||
scene_handle.Completed -= completedCallback;
|
||||
break;
|
||||
case SubAssetsHandle sub_asset_handle:
|
||||
sub_asset_handle.Completed -= completedCallback;
|
||||
break;
|
||||
case BundleFileHandle bundle_file_handle:
|
||||
bundle_file_handle.Completed -= completedCallback;
|
||||
break;
|
||||
case AllAssetsHandle all_assets_handle:
|
||||
all_assets_handle.Completed -= completedCallback;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "com.tuyoogame.yooasset",
|
||||
"displayName": "YooAsset",
|
||||
"version": "3.0.2-beta",
|
||||
"version": "3.0.3-beta",
|
||||
"unity": "2019.4",
|
||||
"description": "unity3d resources management system.",
|
||||
"author": {
|
||||
|
||||
Reference in New Issue
Block a user