04-05-源码-清单生成与构建报告

源码-清单与报告

篇章 :04-源码深度-编辑器模块 阅读时间 :约 25 分钟 前置知识:了解构建管线分析


一、引言

本章将深入解析 YooAsset 清单(Manifest)与报告(Report)的源码实现。PackageManifest 是 YooAsset Editor 端和 Runtime 端之间的数据桥梁,记录了所有 Bundle 的信息;BuildReport 记录了构建的详细信息,是开发者分析构建结果的重要依据。

本章将从源码层面深入解析 PackageManifest 和 BuildReport 的设计与实现。


二、PackageManifest 概述

2.1 PackageManifest 的作用

PackageManifest 是 YooAsset 的资源清单,在整个资源管理流程中起到核心作用:

数据桥梁:

PackageManifest 是 Editor 端和 Runtime 端之间的数据桥梁。Editor 端生成 PackageManifest,Runtime 端读取 PackageManifest。

资源定位:

PackageManifest 记录了所有资源地址到 Bundle 的映射关系,运行时通过 PackageManifest 定位资源。

依赖管理:

PackageManifest 记录了 Bundle 之间的依赖关系,运行时可以根据依赖关系加载 Bundle。

版本管理:

PackageManifest 包含版本信息,支持热更新时的版本比较。

加密信息:

PackageManifest 包含加密信息,运行时根据加密信息解密资源。

2.2 PackageManifest 的核心字段

csharp 复制代码
[Serializable]
public class PackageManifest
{
    // 包名
    public string PackageName;
    
    // 包版本
    public string PackageVersion;
    
    // 包描述
    public string PackageDesc;
    
    // 资源版本(用于热更新)
    public string ResourceVersion;
    
    // 构建时间
    public long BuildTime;
    
    // 构建引擎版本
    public string BuildEngineVersion;
    
    // 资源清单
    public AssetList AssetList = new AssetList();
    
    // Bundle 列表
    public BundleList BundleList = new BundleList();
}

[Serializable]
public class AssetList
{
    public List<PackageAsset> Assets = new List<PackageAsset>();
}

[Serializable]
public class PackageAsset
{
    public string Address;        // 资源地址
    public string AssetPath;      // 资源路径
    public string AssetGUID;      // 资源 GUID
    public int BundleID;          // 所属 Bundle ID
    public int DependIDs;         // 依赖的 Bundle ID 列表
    public string AssetTags;      // 资源标签
}

[Serializable]
public class BundleList
{
    public List<PackageBundle> Bundles = new List<PackageBundle>();
}

[Serializable]
public class PackageBundle
{
    public int BundleID;          // Bundle ID
    public string BundleName;     // Bundle 名称
    public string FileName;       // Bundle 文件名
    public string FileHash;       // Bundle 文件 Hash
    public long FileSize;         // Bundle 文件大小
    public string FileCRC;        // Bundle 文件 CRC
    public bool Encrypted;        // 是否加密
    public EBundleType BundleType; // Bundle 类型
    public List<int> DependIDs;   // 依赖的 Bundle ID 列表
}

PackageName 详解:

PackageName 是 Package 的唯一标识符。运行时通过 PackageName 加载 Package。

PackageVersion 详解:

PackageVersion 是 Package 的版本号,用于热更新时的版本比较。

ResourceVersion 详解:

ResourceVersion 是资源版本号,可以与 PackageVersion 不同。用于更细粒度的版本管理。

AssetList 详解:

AssetList 包含了所有资源的信息,包括资源地址、路径、Bundle 等。

BundleList 详解:

BundleList 包含了所有 Bundle 的信息,包括 Bundle 名称、文件信息、依赖关系等。

2.3 PackageManifest 的序列化

PackageManifest 使用 JSON 格式序列化:

csharp 复制代码
public class PackageManifest
{
    public string ToJson()
    {
        return JsonUtility.ToJson(this, true);
    }
    
    public static PackageManifest FromJson(string json)
    {
        return JsonUtility.FromJson<PackageManifest>(json);
    }
    
    public void SaveToFile(string filePath)
    {
        string json = ToJson();
        File.WriteAllText(filePath, json);
    }
    
    public static PackageManifest LoadFromFile(string filePath)
    {
        string json = File.ReadAllText(filePath);
        return FromJson(json);
    }
}

JSON 序列化详解:

使用 Unity 的 JsonUtility 进行 JSON 序列化。JsonUtility 支持 Serializable 特性的类。

JSON 文件存储详解:

PackageManifest 通常保存为 PackageManifest_{PackageVersion}.bytes 或 PackageManifest_{PackageVersion}.json 文件。

序列化的兼容性:

JsonUtility 的序列化是向前兼容的。新版本可以读取旧版本的 JSON,但旧版本可能无法读取新版本。


三、PackageManifest 生成流程

3.1 生成流程概述

PackageManifest 的生成流程:

markdown 复制代码
PackageManifest 生成流程
1. 收集所有 Bundle 信息
2. 收集所有资源信息
3. 建立资源到 Bundle 的映射
4. 建立 Bundle 依赖关系
5. 计算资源地址
6. 计算 Bundle Hash 和大小
7. 序列化为 JSON
8. 写入输出文件

收集 Bundle 信息详解:

从 BuildMap 中收集所有 Bundle 的信息,包括 Bundle 名称、文件、依赖等。

收集资源信息详解:

从 BuildMap 中收集所有资源的信息,包括资源地址、路径、Bundle 等。

建立映射详解:

建立资源地址到 Bundle 的映射,便于运行时通过资源地址定位 Bundle。

建立依赖关系详解:

建立 Bundle 之间的依赖关系,便于运行时加载依赖的 Bundle。

计算 Hash 和大小详解:

计算 Bundle 文件的 Hash 和大小,用于版本管理和完整性验证。

3.2 BuildTaskCreateManifest

BuildTaskCreateManifest 是创建 PackageManifest 的构建任务:

csharp 复制代码
public class BuildTaskCreateManifest : IBuildTask
{
    public void Run(BuildContext context)
    {
        PackageManifest manifest = new PackageManifest();
        
        // 1. 填充基本信息
        manifest.PackageName = context.Parameters.PackageName;
        manifest.PackageVersion = context.Parameters.PackageVersion;
        manifest.ResourceVersion = context.Parameters.ResourceVersion;
        manifest.BuildTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        manifest.BuildEngineVersion = Application.unityVersion;
        
        // 2. 创建 Bundle 列表
        foreach (var buildBundle in context.BuildMap.Bundles)
        {
            var packageBundle = new PackageBundle();
            packageBundle.BundleID = manifest.BundleList.Bundles.Count;
            packageBundle.BundleName = buildBundle.BundleName;
            packageBundle.FileName = buildBundle.FileName;
            packageBundle.FileHash = buildBundle.Hash;
            packageBundle.FileSize = buildBundle.FileSize;
            packageBundle.FileCRC = buildBundle.CRC;
            packageBundle.Encrypted = context.Parameters.EnableEncryption;
            packageBundle.BundleType = buildBundle.BundleType;
            packageBundle.DependIDs = ConvertDependNamesToIds(buildBundle.Dependencies, manifest);
            
            manifest.BundleList.Bundles.Add(packageBundle);
        }
        
        // 3. 创建资源列表
        foreach (var assetEntry in context.BuildMap.AssetToBundle)
        {
            var packageAsset = new PackageAsset();
            packageAsset.Address = assetEntry.Key;
            packageAsset.AssetPath = assetEntry.Value.AssetPath;
            packageAsset.AssetGUID = assetEntry.Value.AssetGUID;
            packageAsset.BundleID = GetBundleId(assetEntry.Value.BundleName, manifest);
            packageAsset.DependIDs = GetDependIds(assetEntry.Value.Dependencies, manifest);
            packageAsset.AssetTags = string.Join(",", assetEntry.Value.AssetTags);
            
            manifest.AssetList.Assets.Add(packageAsset);
        }
        
        // 4. 保存到文件
        manifest.SaveToFile(context.ManifestFilePath);
    }
}

填充基本信息详解:

填充 PackageManifest 的基本信息,包括包名、版本、构建时间等。

创建 Bundle 列表详解:

将 BuildMap 中的 Bundle 转换为 PackageBundle,并建立 Bundle ID。

创建资源列表详解:

将 BuildMap 中的资源转换为 PackageAsset,并设置 Bundle ID。

保存到文件详解:

将 PackageManifest 序列化为 JSON 并保存到输出文件。

3.3 资源地址计算

资源地址是 PackageManifest 中的重要字段:

csharp 复制代码
public class AssetAddressCalculator
{
    public string CalculateAddress(string assetPath, AssetBundleCollector collector)
    {
        if (collector.AddressRule == EAddressRule.StringAddress)
        {
            return CalculateStringAddress(assetPath, collector);
        }
        else if (collector.AddressRule == EAddressRule.GUIDAddress)
        {
            return AssetDatabase.AssetPathToGUID(assetPath);
        }
        else if (collector.AddressRule == EAddressRule.LabelAddress)
        {
            return string.Join(",", collector.AssetTags);
        }
        return string.Empty;
    }
    
    private string CalculateStringAddress(string assetPath, AssetBundleCollector collector)
    {
        if (!string.IsNullOrEmpty(collector.Address))
        {
            return collector.Address + "/" + Path.GetFileNameWithoutExtension(assetPath);
        }
        
        string relativePath = assetPath.Substring(collector.CollectPath.Length);
        return relativePath.Replace("\\", "/").TrimStart('/').Replace(Path.GetExtension(assetPath), "");
    }
}

String Address 计算详解:

使用 Collector 的 Address 作为前缀,加上文件名生成地址。

GUID Address 计算详解:

使用 Unity 的 AssetPathToGUID API 获取资源的 GUID。

Label Address 计算详解:

使用 Collector 的 AssetTags 作为地址。


四、BuildReport 概述

4.1 BuildReport 的作用

BuildReport 记录了构建的详细信息:

  • 构建结果(成功/失败)
  • 构建参数
  • 构建时间
  • 资源统计
  • Bundle 统计
  • 错误和警告

4.2 BuildReport 的核心字段

csharp 复制代码
[Serializable]
public class BuildReport
{
    public string PackageName;
    public string PackageVersion;
    public bool BuildSuccess;
    public string BuildError;
    public long BuildStartTime;
    public long BuildEndTime;
    public long BuildDuration;
    public string BuildEngineVersion;
    public BuildTarget BuildTarget;
    public int TotalAssetCount;
    public int TotalBundleCount;
    public long TotalFileSize;
    public List<BuildBundleReport> BundleReports = new List<BuildBundleReport>();
    public List<string> Warnings = new List<string>();
}

[Serializable]
public class BuildBundleReport
{
    public string BundleName;
    public string FileName;
    public int AssetCount;
    public long FileSize;
    public string FileHash;
    public List<string> Assets = new List<string>();
    public List<string> Dependencies = new List<string>();
}

BuildSuccess 详解:

BuildSuccess 标识构建是否成功。

BuildError 详解:

BuildError 记录构建失败的错误信息。

BuildDuration 详解:

BuildDuration 记录构建耗时。

BundleReports 详解:

BundleReports 包含了所有 Bundle 的报告。

Warnings 详解:

Warnings 记录了构建过程中的警告信息。

4.3 BuildReport 的生成

csharp 复制代码
public class BuildReportGenerator
{
    public BuildReport Generate(BuildContext context)
    {
        BuildReport report = new BuildReport();
        
        // 1. 填充基本信息
        report.PackageName = context.Parameters.PackageName;
        report.PackageVersion = context.Parameters.PackageVersion;
        report.BuildStartTime = context.BuildStartTime;
        report.BuildEndTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        report.BuildDuration = report.BuildEndTime - report.BuildStartTime;
        report.BuildEngineVersion = Application.unityVersion;
        report.BuildTarget = context.BuildTarget;
        
        // 2. 统计资源
        foreach (var bundle in context.BuildMap.Bundles)
        {
            var bundleReport = new BuildBundleReport();
            bundleReport.BundleName = bundle.BundleName;
            bundleReport.FileName = bundle.FileName;
            bundleReport.AssetCount = bundle.Assets.Count;
            bundleReport.FileSize = bundle.FileSize;
            bundleReport.FileHash = bundle.Hash;
            bundleReport.Assets = bundle.Assets;
            bundleReport.Dependencies = bundle.Dependencies;
            
            report.BundleReports.Add(bundleReport);
        }
        
        // 3. 计算总数
        report.TotalBundleCount = report.BundleReports.Count;
        report.TotalAssetCount = report.BundleReports.Sum(b => b.AssetCount);
        report.TotalFileSize = report.BundleReports.Sum(b => b.FileSize);
        
        // 4. 设置构建结果
        report.BuildSuccess = context.BuildSuccess;
        report.BuildError = context.BuildError;
        report.Warnings = context.Warnings;
        
        return report;
    }
}

填充基本信息详解:

填充 BuildReport 的基本信息,包括包名、版本、构建时间、构建平台等。

统计 Bundle 信息详解:

为每个 Bundle 生成 BuildBundleReport,包含 Bundle 的详细信息。

计算总数详解:

计算所有 Bundle 的资源总数、文件总大小等。

设置构建结果详解:

设置构建的成功状态、错误信息、警告信息。

4.4 BuildReport 的展示

BuildReport 提供了多种展示方式:

Editor 内显示:

csharp 复制代码
[CustomEditor(typeof(BuildReport))]
public class BuildReportEditor : Editor
{
    public override void OnInspectorGUI()
    {
        // 显示 BuildReport 的内容
    }
}

导出为 JSON:

csharp 复制代码
public void SaveToJson(string filePath)
{
    string json = JsonUtility.ToJson(this, true);
    File.WriteAllText(filePath, json);
}

导出为 HTML:

可以自定义将 BuildReport 导出为 HTML 格式,便于在浏览器中查看。


五、版本管理

5.1 版本号格式

YooAsset 使用语义化版本号(Semantic Versioning):

格式:

Major.Minor.Patch(如 1.0.0)

Major 版本:

主版本号,表示重大更新,可能不兼容旧版本。

Minor 版本:

次版本号,表示新增功能,向后兼容。

Patch 版本:

补丁版本号,表示 bug 修复。

5.2 版本管理实现

csharp 复制代码
public class VersionManager
{
    public bool IsNewerVersion(string newVersion, string oldVersion)
    {
        var newVer = new Version(newVersion);
        var oldVer = new Version(oldVersion);
        return newVer > oldVer;
    }
    
    public string GenerateNextVersion(string currentVersion, EVersionIncrementType incrementType)
    {
        var ver = new Version(currentVersion);
        switch (incrementType)
        {
            case EVersionIncrementType.Major:
                return $"{ver.Major + 1}.0.0";
            case EVersionIncrementType.Minor:
                return $"{ver.Major}.{ver.Minor + 1}.0";
            case EVersionIncrementType.Patch:
                return $"{ver.Major}.{ver.Minor}.{ver.Build + 1}";
            default:
                return currentVersion;
        }
    }
}

版本比较详解:

使用 .NET 的 Version 类进行版本比较。

版本递增详解:

支持 Major、Minor、Patch 三种递增方式。

5.3 版本号的使用

版本号在 YooAsset 中有以下用途:

  • 标识 Package 的版本
  • 标识构建产物的版本
  • 标识热更新的差异
  • 标识缓存的版本

六、总结

本章深入解析了 YooAsset 清单(Manifest)与报告(Report)的源码实现,包括:

  • PackageManifest 概述:PackageManifest 的作用、字段、序列化
  • PackageManifest 生成流程:生成流程、BuildTaskCreateManifest、地址计算
  • BuildReport 概述:BuildReport 的作用、字段、生成
  • BuildReport 展示:Editor 内显示、JSON 导出、HTML 导出
  • 版本管理:版本号格式、版本比较、版本递增

PackageManifest 和 BuildReport 是 YooAsset 编辑器端的重要组成部分,深入理解它们的实现有助于更好地使用和扩展 YooAsset。


上一篇 :构建管线分析 下一篇 :调试与诊断工具

相关推荐
UWA1 天前
UGUI性能排查实战:隐藏血条后耗时下降,减少顶点为何没改善
ui·性能优化·cpu·游戏开发
SmalBox1 天前
04-04-源码-构建管线与任务系统
unity3d·游戏开发
应用市场2 天前
不用一张图片的像素小游戏:小人是字符串,房间是矩形,平衡靠两千个假玩家
android·kotlin·游戏开发
新的瑞拉公主2 天前
Cocos plist 粒子一键导入 Unity:Editor 工具实现与参数映射
unity·游戏开发·cocos2d-x·粒子系统·editor工具
SmalBox2 天前
04-03-源码-构建映射与依赖分析(BuildMap)
unity3d·游戏开发
甲维斯3 天前
我把“赶 Deadline”和AI做成了一款游戏
人工智能·游戏开发
SmalBox3 天前
04-02-源码-资源收集器(AssetBundleCollector)
unity3d·游戏开发
字节暗面3 天前
客户端上报不可信:游戏服务端数据校验与对局重演设计
c#·游戏开发·服务端校验·反作弊
字节暗面3 天前
游戏服务端如何校验客户端上报数据?四层校验思路详解
游戏开发