当前位置: 首页 > news >正文

【SBP】Unity 打包构建管线原理解析于对比

Unity内置打包流程与SBP(Scriptable Build Pipeline)的对比分析及全流程梳理,了解打包过程到底在干什么,怎么做的。SBP又赋予了什么新的能力。SBP的运行原理机制。SBP打包的示例。

‌一、发展历史与核心差异‌

内置打包流程

  • 传统方式基于BuildPipeline.BuildAssetBundles,通过C++实现黑盒操作,开发者仅能控制输入参数(如压缩格式、目标平台),无法干预内部逻辑。
  • 局限性:增量构建效率低,无法定制资产处理逻辑,大型项目构建耗时显著。

SBP的演进

  • Unity 2018后引入SBP,将构建逻辑从C++迁移到C#层,开放流水线式架构,支持自定义任务注入。
  • 目标:提升构建速度(尤其是增量构建)、增强灵活性,成为Addressables系统的底层基础

‌二、全流程对比与操作权限‌

完整可编程化标记图‌

内置管线流程:
[资源收集] → [依赖分析(黑盒)] → [序列化(固定)] → [Bundle生成] → [压缩(有限)] → [输出]SBP可编程化节点:↓                      ↓                  ↓               ↓[自定义依赖分析]       [自定义序列化]    [Bundle布局调整]    [动态压缩/加密]↓                      ↓                  ↓               ↓
[资源收集] → [依赖分析(开放)] → [序列化(可扩展)] → [Bundle生成(可控)] → [后处理(可注入)] → [输出]

SBP通过解耦流水线任务(IBuildTask)和开放底层接口(如ContentBuildInterface),将内置管线中90%的黑盒操作转为可编程节点.

‌内置打包流程‌

  • 资源收集
    • 自动扫描项目资产,但依赖关系分析不可见。
  • 资产打包-Bundle生成
    • 调用BuildPipeline.BuildAssetBundles,内部完成依赖解析、压缩、序列化,开发者仅能设置输出路径和压缩格式。
  • 可执行文件构建
    • 通过BuildSettings配置场景列表、平台参数,生成exe/apk/ipa等文件。
  • 可操作项‌:
    • 设置Bundle名称、压缩格式(LZMA/LZ4)。
    • 配置构建设备参数(如分辨率、图标)。
  • 不可操作项‌:
    • 资产依赖解析逻辑、序列化过程、并行任务调度。

‌SBP流程‌

  • 流水线初始化
    • 通过ContentPipeline.BuildAssetBundles启动,允许自定义BuildParametersBuildTasks
  • 资产处理阶段
    • 依赖分析‌:开放API(如CalculateAssetDependencyData)支持自定义依赖规则。
    • 序列化‌:可插入自定义序列化器(如优化特定资产类型的二进制格式)。
  • Bundle生成与输出
    • 支持多线程分发任务,开发者可控制压缩、加密等后处理逻辑。
  • 可操作项‌:
    • 自定义依赖分析、序列化、压缩策略。
    • 注入预处理/后处理任务(如资源加密)。
  • 不可操作项‌:
    • 底层引擎接口(如物理系统集成)仍由Unity控制

内置打包管线中可编程化改造的详细对比

‌一、资源收集与依赖分析‌

  • 内置管线
    • 黑盒操作:自动扫描项目资产,依赖分析不可见。
    • 不可定制:依赖关系由Unity内部算法处理,无法干预。
  • SBP的可编程化
    • 开放API‌:通过CalculateAssetDependencyDataCalculateCustomDependencyData任务暴露依赖分析逻辑。
    • 自定义规则‌:开发者可注入IBuildTask修改依赖关系(如排除特定资源)。

‌二、资产序列化与Bundle生成‌

  • 内置管线
    • 固定流程:资产序列化为二进制格式(不可定制),Bundle布局由Unity控制。
  • SBP的可编程化
    • 序列化控制‌:通过WriteSerializedFile任务支持自定义序列化器(如优化二进制结构)。
    • Bundle布局‌:GenerateBundlePacking任务允许调整Bundle分块策略(如按类型分组)。

‌三、压缩与加密‌

  • 内置管线
    • 有限选项:仅支持LZMA/LZ4压缩,无加密接口。
  • SBP的可编程化
    • 动态压缩‌:通过ArchiveAndCompressBundles任务支持自定义压缩算法(如Zstd)。
    • 加密扩展‌:可插入后处理任务(如PostWritingCallback)实现资源加密。

‌四、增量构建与缓存‌

  • 内置管线
    • 效率低下:全量构建,无细粒度缓存机制。
  • SBP的可编程化
    • 增量处理‌:通过ContentBuildInterface的缓存API实现仅构建变更资源。
    • 缓存复用‌:依赖分析结果和中间文件可持久化复用。

‌五、平台特定处理‌

  • 内置管线
    • 硬编码逻辑:平台适配(如纹理压缩转换)不可修改。
  • SBP的可编程化
    • 平台钩子‌:通过SwitchToBuildPlatform任务插入平台预处理逻辑(如Android纹理格式重定向)

‌三、操作示例‌

‌内置打包代码‌

csharp
// 生成AssetBundle
BuildPipeline.BuildAssetBundles("OutputPath",BuildAssetBundleOptions.ChunkBasedCompression,BuildTarget.Android);

‌SBP自定义任务‌

csharp
var customParams = new BundleBuildParameters(BuildTarget.Android,CompressionType.LZ4);
var content = new BundleBuildContent(assetEntries);
var tasks = new List<IBuildTask> {new CustomDependencyTask(),// 自定义依赖分析new BundleCompressionTask()// 自定义压缩
};
ContentPipeline.BuildAssetBundles(customParams, content, tasks);

‌内置打包管线完整示例‌

  • 功能‌:生成压缩格式为LZ4的AssetBundle,输出到Assets/AssetBundles目录

  • 限制‌:无法干预依赖分析、序列化等内部逻辑

  • BuildAssetBundles.cs

    using UnityEditor;
    using System.IO;public class BuiltInPipelineExample
    {[MenuItem("Tools/Build AssetBundles")]static void BuildAllAssetBundles(){string outputPath = Path.Combine(Application.dataPath, "AssetBundles");if (!Directory.Exists(outputPath))Directory.CreateDirectory(outputPath);// 配置Bundle参数BuildAssetBundleOptions options = BuildAssetBundleOptions.ChunkBasedCompression;BuildTarget target = BuildTarget.Android;// 执行打包BuildPipeline.BuildAssetBundles(outputPath, options, target);AssetDatabase.Refresh();}
    }
    

SBP定制化模块示例

  • SBP给出了兼容内置打包管线的CompatibilityBuildPipeline.BuildAssetBundles(outputPath, options, buildTarget);来快速从内置管线升级到SBP。详细升级指南见https://docs.unity3d.com/Packages/com.unity.scriptablebuildpipeline@1.21/manual/UpgradeGuide.html

  • 也给出了SBP主要的打包方式ContentPipeline.BuildAssetBundles(...)

  • 其中ContentPipeline.BuildAssetBundles(...)方式主要依赖于一系列打包任务List和相应的上下文信息(IBuildContext实现这个接口的各种参数信息).通过这个方法将参数和任务注入进去,方法内会根据传入信息组装完善好上下文,然后根据上下文,依此执行传入的task。BuildTasksRunner.Run(taskList, buildContext);这里的BuildTaskRunner会在Run时将任务列表和构建的上下文信息传入,然后对每个任务调用数据注入ContextInjector.Inject(context, task);ContextInjector.Extract(context, task);来扫描每个任务中用属性[InjectContext(ContextUsage.**In**)]标记的可传入或传出的数据,根据标记的属性In、Out、InOut,对每个任务之间的数据进行传递。

  • 属性标记脚本:

    • com.unity.scriptablebuildpipeline@1.21.25/Editor/Injector/ContextInjector.cs
  • 默认任务脚本:

    • com.unity.scriptablebuildpipeline@1.21.25/Editor/Shared/DefaultBuildTasks.cs
  • 默认DefaultBuildTasks.ContentFileCompatible()中给出了一系列打包任务的必要示例流程。

    public static IList<IBuildTask> ContentFileCompatible()
    {var buildTasks = new List<IBuildTask>();// SetupbuildTasks.Add(new SwitchToBuildPlatform());buildTasks.Add(new RebuildSpriteAtlasCache());// Player ScriptsbuildTasks.Add(new BuildPlayerScripts());buildTasks.Add(new PostScriptsCallback());// DependencybuildTasks.Add(new CalculateSceneDependencyData());buildTasks.Add(new CalculateCustomDependencyData());buildTasks.Add(new CalculateAssetDependencyData());buildTasks.Add(new StripUnusedSpriteSources());buildTasks.Add(new PostDependencyCallback());// PackingbuildTasks.Add(new ClusterBuildLayout());buildTasks.Add(new PostPackingCallback());// WritingbuildTasks.Add(new WriteSerializedFiles());buildTasks.Add(new ArchiveAndCompressBundles());buildTasks.Add(new GenerateLinkXml());buildTasks.Add(new PostWritingCallback());return buildTasks;
    }
    

‌1. 依赖分析定制‌

  • 原理‌:通过IBuildTask接口修改DependencyData,实现资源过滤

  • CustomDependencyTask.cs

    using UnityEditor.Build.Content;
    using UnityEditor.Build.Pipeline.Interfaces;public class CustomDependencyTask : IBuildTask
    {public int Version => 1;[InjectContext(ContextUsage.In)]IBuildContent m_Content;public string[] excludePaths = { "Assets/Resources/Dev/" };public ReturnCode Run(){m_Content.Assets.RemoveAll(asset =>{var path = AssetDatabase.GUIDToAssetPath(asset);return System.Array.Exists(excludePaths, p => path.StartsWith(p));});return ReturnCode.Success;}
    }
    

‌2. 自定义序列化‌

  • 控制点‌:替换特定类型资产的Serializer实现

  • CustomSerializationTask.cs

    using UnityEditor.Build.Content;
    using UnityEditor.Build.Pipeline.Interfaces;public class CustomSerializationTask : IBuildTask
    {public int Version => 1;[InjectContext(ContextUsage.In)]IBuildContent m_Content;public ReturnCode Run(){// 自定义序列化逻辑:过滤特定类型资源m_Content.Assets.RemoveAll(asset => {var path = AssetDatabase.GUIDToAssetPath(asset);return path.EndsWith(".tmp") || path.Contains("/Temp/");});return ReturnCode.Success;}
    }
    

‌3. Bundle生成调整‌

  • 逻辑‌:调整Bundle命名规则:添加构建时间戳

  • BundleAdjustmentTask.cs

    using UnityEditor.Build.Content;
    using UnityEditor.Build.Pipeline.Interfaces;public class BundleAdjustmentTask : IBuildTask
    {public int Version => 1;[InjectContext(ContextUsage.In)]IBundleLayout m_Layout;public ReturnCode Run(){// 调整Bundle命名规则:添加构建时间戳foreach (var bundle in m_Layout.BundleToAssets.Keys.ToList()){var newName = $"{bundle}_{DateTime.Now:yyyyMMdd}";m_Layout.BundleToAssets[newName] = m_Layout.BundleToAssets[bundle];m_Layout.BundleToAssets.Remove(bundle);}return ReturnCode.Success;}
    }
    

‌4. 后处理动态加密‌

  • 触发时机‌:在IPostBuildTask中处理已生成的Bundle文件

  • PostProcessEncrypt.cs

    using System.IO;
    using UnityEditor.Build.Pipeline.Interfaces;public class PostProcessEncryptionTask : IBuildTask
    {public int Version => 1;[InjectContext(ContextUsage.In)]IBuildResults m_Results;public ReturnCode Run(){// 对生成的Bundle进行XOR简单加密foreach (var bundle in m_Results.BundleInfos){var bytes = File.ReadAllBytes(bundle.Value.FileName);for (int i = 0; i < bytes.Length; i++)bytes[i] ^= 0xAA;File.WriteAllBytes(bundle.Value.FileName, bytes);}return ReturnCode.Success;}
    }
    

‌5. 完整SBP流水线整合‌

  • 增量构建‌:通过UseCache复用历史构建数据

  • 模块组合‌:按需注入自定义任务链

  • SBPPipeline.cs

    using UnityEditor.Build.Content;
    using UnityEditor.Build.Pipeline;
    using System.Collections.Generic;public static class SBPPipeline
    {public static void Build(){var customParams = new BundleBuildParameters(BuildTarget.Android, BuildTargetGroup.Android, "OutputPath");var content = new BundleBuildContent(AssetDatabase.GetAllAssetPaths());var tasks = new List<IBuildTask> {new CustomDependencyTask(), // 依赖分析new CustomSerializationTask(), // 序列化new BundleAdjustmentTask(), // Bundle布局new ArchiveAndCompressBundlesTask(), // 默认压缩new PostProcessEncryptionTask() // 加密};// 增量构建:启用缓存params.UseCache = true;ContentPipeline.BuildAssetBundles(customParams, content, out _, tasks);}
    }
    

差异总对比

模块内置管线SBP
依赖分析不可定制通过IBuildTask重写逻辑
Bundle布局固定分组规则动态分块策略
后处理仅支持基础压缩支持加密、签名等扩展
增量构建全量重建缓存复用提升效率
  • SBP通过解耦构建步骤为可编程任务(IBuildTask),实现传统黑盒操作的全面开放

‌四、实现原理对比‌

  • 内置流程‌:通过C++硬编码实现资产处理,逻辑不可扩展。
  • SBP‌:基于C#反射和接口抽象,将构建步骤拆分为可组合的IBuildTask,通过依赖注入实现扩展

‌五、迁移建议‌

  • 适用场景‌:大型项目需增量构建优化时推荐SBP,小型项目可使用内置流程。
  • 注意事项‌:SBP需适配Addressables系统,旧项目需重构构建脚本

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

http://www.dtcms.com/a/348258.html

相关文章:

  • 什么是服装企业管理软件?
  • 【Canvas与旗帜】金波浪圈法兰西国旗
  • 广告业务连续四季度双位数增长,B站做了什么?
  • DAY 51 复习日+退款开始
  • 数据挖掘 4.8 评估泛化能力
  • 【DeepResearch调研】基于知识图谱与数据合成的大语言模型幻觉缓解研究前沿
  • C++ Core Guidelines: 最佳实践与深入解析
  • 服务器硬件电路设计之 SPI 问答(五):服务器场景下的ESD防护策略与通信故障诊断指南
  • Flink元空间异常深度解析:从原理到实战调优指南
  • LLM实践系列:利用LLM重构数据科学流程07 - 工程化实践与挑战
  • 计算机网络基础(三) --- TCP/IP网络结构(运输层)
  • 实时操作系统FreeRTOS移植到STM32VGT6
  • Axure RP 9的安装
  • 2025年渗透测试面试题总结-31(题目+回答)
  • leetcode 1504. 统计全 1 子矩形 中等
  • `malloc` 内存分配函数
  • fastdds:topic instance
  • 【嵌入式】【搜集】状态机、状态迁移图及状态模式材料
  • 【线性代数】常见矩阵类型
  • 【Nginx系列】查看 Nginx 的日志
  • Building Systems with the ChatGPT API 使用 ChatGPT API 搭建系统(第八章学习笔记及总结)
  • Hibernate详解
  • GaussDB 数据库架构师修炼(十八) SQL引擎-分布式计划
  • 保姆级Maven安装与配置教程(Windows版)
  • SpringCloud Alibaba核心知识点
  • MIT 6.5840 (Spring, 2024) 通关指南——入门篇
  • 项目学习总结(4)
  • Java内存泄漏详解:检测、分析与预防策略
  • 大语言模型的自动驾驶 LMDrive/DriveVLM-Dual
  • 电动车运行原理与最新人工智能驾驶技术在电动车上的应用展望:从基础动力系统到L5级完全自动驾驶的技术深度解析