实战-资源配置与打包
篇章:10-实战篇-从零搭建
状态:已完成
阅读时间:约 25 分钟
一、引言
1.1 本章在系列中的定位
资源配置(Collector 配置)和打包(Build)是将 Unity 资源转化为 YooAsset 资源的核心环节。本章将详细讲解:
- 如何配置 Collector(资源收集器)
- 如何配置 Group(资源组)
- 如何执行打包
- 如何优化打包性能
1.2 本章要解决的核心问题
- Collector 的工作原理与配置方法
- Group 的合理划分
- 打包脚本的编写
- 打包性能优化
1.3 阅读前置要求
- 完成 10-01 到 10-04
- 了解 Unity Editor 扩展基础
二、AssetBundleCollector 详解
2.1 Collector 的概念
AssetBundleCollector是 YooAsset 的资源收集器,负责:
- 收集资源:从 Unity Asset 目录中按规则提取
- 分类资源:按 Group、Tag 分类
- 生成清单:构建 Bundle 划分方案
2.2 Collector 的工作流
[配置 Collectors] │ ▼ [AssetBundleCollector.CollectAssets] │ ├── 遍历 Asset 目录 │ ├── 匹配 Filter 规则 │ └── 应用 Address 规则 │ ├── 应用 PackRule │ ├── ByFile: 每个资源一个 Bundle │ ├── ByFolder: 每个文件夹一个 Bundle │ ├── ByTopLevel: 按顶层文件分 │ └── PackExplicit: 显式指定 │ └── 输出 BuildMap ├── Bundle 列表 ├── 资源列表 └── 依赖关系2.3 Collector 的配置
2.3.1 创建 Collector
[CreateAssetMenu(fileName = "MainCollector", menuName = "YooAsset/Collector")] public class MainCollector : ScriptableObject { public string CollectorName = "MainCollector"; public List<CollectRule> CollectRules = new(); } [Serializable] public class CollectRule { public string SearchPattern; // 搜索模式 public string[] ExcludePatterns; // 排除模式 public string GroupName; // 目标 Group public string AddressRule; // Address 规则 public EPackRuleType PackRule; // 打包规则 public string[] Tags; // 资源标签 }2.3.2 常用配置示例
示例 1:按文件类型收集
new CollectRule { SearchPattern = "Assets/Art/**/*.png", GroupName = "TextureGroup", AddressRule = "ByFileName", PackRule = EPackRuleType.PackByFile, Tags = new[] { "texture", "ui" } }示例 2:按文件夹收集
new CollectRule { SearchPattern = "Assets/Prefabs/UI/**/*.prefab", GroupName = "UIGroup", AddressRule = "ByFolderPath", PackRule = EPackRuleType.PackByFolder }示例 3:按收集组收集
new CollectRule { SearchPattern = "Assets/Bundles/**/*", GroupName = "SharedGroup", AddressRule = "ByCollectorGroup", PackRule = EPackRuleType.PackByCollectorGroup }2.4 PackRule 详解
YooAsset 的 PackRule 决定"哪些资源打到一个 Bundle":
| PackRule | 含义 | 适用 | Bundle 数量 |
|---|---|---|---|
PackByFile | 每个资源独立打包 | 通用 | 最多 |
PackByFolder | 同一文件夹打到一个 Bundle | UI、Audio | 中等 |
PackByCollectorGroup | 整个 Group 一个 Bundle | 小型游戏 | 少 |
PackByTopLevel | 按顶层子目录打包 | 模块化项目 | 中等 |
PackExplicit | 显式指定 | 特殊需求 | 自定 |
2.4.1 PackByFile
Bundle/ ├── bundle_Hero_Assassin ├── bundle_Hero_Mage ├── bundle_Hero_Tank └── ...2.4.2 PackByFolder
Bundle/ ├── UI/ (整个 UI 文件夹) │ ├── bundle_ui ├── Character/ (整个 Character 文件夹) │ ├── bundle_character └── ...2.4.3 PackExplicit
通过IPackRule自定义:
public class CustomPackRule : IPackRule { public bool Match(AssetInfo info) => true; public string GetBundleName(AssetInfo info) { // 自定义打包规则:按资源大小 if (info.FileSize > 1024 * 1024) // 大于 1MB return "large_assets"; else return "small_assets"; } }2.5 Address 规则
| AddressRule | 示例 | 适用 |
|---|---|---|
ByFileName | Hero_Assassin | 命名规范的项目 |
ByFolderPath | Characters/Hero_Assassin | 路径即语义 |
ByCollectorGroup | default/Characters/Hero_Assassin | 多 Group 项目 |
ByExplicit | 自定义 | 完全控制 |
三、Group 配置
3.1 Group 的作用
Group 是"打包配置的集合",每个 Group 包含:
- 压缩配置
- 加密配置
- 输出路径
- 资源依赖
3.2 Group 的划分原则
3.2.1 按"更新频率"划分
Group_HotUpdate # 高频更新(活动、商城) ├── CompressOption: LZ4 └── UpdatePolicy: Always Group_Stable # 低频更新(核心玩法) ├── CompressOption: LZ4 └── UpdatePolicy: OnMajor Group_Static # 极少更新(公共资源) ├── CompressOption: LZ4 └── UpdatePolicy: OnMajor3.2.2 按"加载时机"划分
Group_Startup # 启动时加载 ├── CompressOption: LZ4 └── LoadTime: Synchronous Group_Normal # 运行时加载 ├── CompressOption: LZ4 └── LoadTime: Asynchronous3.2.3 按"资源类型"划分
Group_Texture Group_Audio Group_Prefab Group_Scene Group_Config3.3 Group 的配置
[CreateAssetMenu(fileName = "GroupSettings", menuName = "YooAsset/Group")] public class GroupSettings : ScriptableObject { public string GroupName; public ECompressOption CompressOption = ECompressOption.LZ4; public EEncryptionOption EncryptionOption = EEncryptionOption.None; public string EncryptionSecret; public EBundledOutputStyle OutputStyle = EBundledOutputStyle.HashName; public long MaxBundleSize = 10 * 1024 * 1024; // 10MB }四、Build 流程
4.1 Build 流程总览
[BuildParameters] │ ▼ [ScriptableBuildPipeline.Build] │ ├── 1. AssetBundleCollector.Collect │ └── 输出 BuildMap │ ├── 2. Bundle 划分 │ └── 按 PackRule 分组 │ ├── 3. 依赖分析 │ └── 生成依赖图 │ ├── 4. BuildTaskBuilder │ └── 调用 BuildPipeline.BuildAssetBundles │ ├── 5. BuildTaskManifest │ └── 生成 PackageManifest │ ├── 6. BuildTaskReport │ └── 生成 BuildReport.html │ └── 7. 输出到 OutputPath └── BuildReport + Manifest + Bundles4.2 完整的 Build 脚本
using UnityEditor; using UnityEngine; using YooAsset.Editor; using System.IO; public class GameBuildPipeline { [MenuItem("Build/Android")] public static void BuildAndroid() { var buildParams = new BuildParameters { // 基础配置 BuildOutputRoot = GetBuildOutputRoot(), BuildVersion = PlayerSettings.bundleVersion, // 构建管线 BuildPipeline = EBuildPipeline.ScriptableBuildPipeline, // 压缩与加密 CompressOption = ECompressOption.LZ4, EncryptionOption = EEncryptionOption.None, // 平台 BuildTarget = BuildTarget.Android, // 输出 OutputNameStyle = EOutputNameStyle.HashName, CopyToStreamingAssets = true, // 共享 EnableSharedPackRule = true, // 验证 VerifyBuildingResult = true, // 报告 BuildReportPath = "BuildReports" }; var pipeline = new ScriptableBuildPipeline(); var manifest = pipeline.Build(buildParams); if (manifest.Success) { Debug.Log($"[Build] 构建成功: {manifest.OutputRoot}"); OpenBuildReport(manifest.OutputRoot); } else { Debug.LogError($"[Build] 构建失败: {manifest.ErrorInfo}"); } } static string GetBuildOutputRoot() { return Path.Combine(Application.dataPath, "..", "Builds", "YooAsset"); } static void OpenBuildReport(string outputRoot) { var reportPath = Path.Combine(outputRoot, "BuildReport.html"); if (File.Exists(reportPath)) { EditorUtility.RevealInFinder(reportPath); } } }4.3 多平台构建
public class MultiPlatformBuild { [MenuItem("Build/All Platforms")] public static void BuildAll() { var platforms = new[] { BuildTarget.Android, BuildTarget.iOS, BuildTarget.StandaloneWindows64, BuildTarget.WebGL }; foreach (var target in platforms) { BuildForPlatform(target); } } static void BuildForPlatform(BuildTarget target) { var buildParams = GetBuildParams(target); var pipeline = new ScriptableBuildPipeline(); var result = pipeline.Build(buildParams); Debug.Log($"[{target}] Build {result.Success}: {result.OutputRoot}"); } }4.4 命令行构建
public class CommandLineBuild { public static void Build() { // 从命令行参数读取 var buildPath = System.Environment.GetEnvironmentVariable("BUILD_OUTPUT") ?? "Builds"; var buildVersion = System.Environment.GetEnvironmentVariable("BUILD_VERSION") ?? "1.0.0"; var buildTargetStr = System.Environment.GetEnvironmentVariable("BUILD_TARGET") ?? "Android"; var buildTarget = (BuildTarget)System.Enum.Parse(typeof(BuildTarget), buildTargetStr); var buildParams = new BuildParameters { BuildOutputRoot = buildPath, BuildVersion = buildVersion, BuildTarget = buildTarget, // ... 其他配置 }; var pipeline = new ScriptableBuildPipeline(); var result = pipeline.Build(buildParams); // 退出码(CI 使用) EditorApplication.Exit(result.Success ? 0 : 1); } }五、构建性能优化
5.1 构建时间优化
| 优化项 | 效果 |
|---|---|
| 启用增量构建 | 构建时间减少 50%+ |
| 减少 Bundle 数量 | 构建时间减少 30%+ |
| 关闭 verifyBuildingResult | 构建时间减少 10% |
| 并行构建 | 构建时间减少 20%+ |
| SSD 硬盘 | 构建时间减少 30%+ |
| 关闭 LZMA 压缩 | 构建时间减少 50%+ |
5.2 增量构建
YooAsset 默认支持增量构建:
var buildParams = new BuildParameters { IncrementalBuild = true, // 启用增量 // ... };5.3 并行构建
public class ParallelBuild { [MenuItem("Build/Parallel")] public static void BuildParallel() { var tasks = new[] { Task.Run(() => BuildForPlatform(BuildTarget.Android)), Task.Run(() => BuildForPlatform(BuildTarget.iOS)), Task.Run(() => BuildForPlatform(BuildTarget.StandaloneWindows64)) }; Task.WaitAll(tasks); } }5.4 构建缓存
public class BuildCache { public void SetupCache() { var cachePath = Path.Combine(Application.dataPath, "..", "Library", "YooAssetBuildCache"); Directory.CreateDirectory(cachePath); YooAssetBuildCache.CachePath = cachePath; } }六、BuildReport 解读
6.1 报告结构
BuildReport.html 包含:
BuildReport.html ├── 总览 │ ├── 构建时间 │ ├── Bundle 总数 │ ├── 资源总数 │ ├── 总大小 │ └── 压缩率 │ ├── Bundle 列表 │ ├── bundle_01 │ │ ├── 大小 │ │ ├── 资源数 │ │ └── 依赖 │ └── ... │ ├── 资源列表 │ ├── 按类型分类 │ └── 按大小排序 │ ├── 警告与错误 └── 优化建议6.2 关键指标
6.2.1 压缩率
压缩率 = 压缩后大小 / 原始大小 理想值:< 70%(LZ4)6.2.2 Bundle 数量
| 资源总量 | 推荐 Bundle 数 |
|---|---|
| < 100MB | < 50 |
| 100MB-1GB | 50-200 |
| 1GB-10GB | 200-1000 |
| > 10GB | 1000-5000 |
6.2.3 重复资源
报告中的"重复资源"列表:
⚠️ 检测到以下资源被多个 Bundle 引用: - assets/Common/Atlas/UI.png (5 个 Bundle) - assets/Common/Font/Default.ttf (10 个 Bundle)6.3 优化建议
public class BuildReportAnalyzer { public void Analyze(string reportPath) { var report = LoadReport(reportPath); // 1. 检查 Bundle 数量 if (report.BundleCount > 1000) { Debug.LogWarning("Bundle 数量过多,考虑合并"); } // 2. 检查大 Bundle foreach (var bundle in report.Bundles) { if (bundle.Size > 50 * 1024 * 1024) // 50MB { Debug.LogWarning($"Bundle {bundle.Name} 过大: {bundle.Size}"); } } // 3. 检查重复资源 foreach (var kvp in report.DuplicatedAssets) { Debug.LogWarning($"资源 {kvp.Key} 重复 {kvp.Value} 次"); } } }七、CI/CD 集成
7.1 Jenkins 集成
// Jenkinsfile pipeline { agent any stages { stage('Checkout') { steps { git 'https://github.com/your-org/your-game.git' } } stage('Build') { steps { sh ''' docker run --rm \ -v $WORKSPACE:/workspace \ unityci/editor:2022.3.20f1 \ -batchmode -quit \ -projectPath /workspace \ -executeMethod GameBuildPipeline.BuildAndroid ''' } } stage('Upload') { steps { sh 'python3 upload.py $WORKSPACE/Builds $BUILD_NUMBER' } } } }7.2 GitLab CI 集成
# .gitlab-ci.yml build_android: stage: build image: unityci/editor:2022.3.20f1 script: - BUILD_OUTPUT=$(pwd)/Builds - BUILD_VERSION=$CI_PIPELINE_ID - BUILD_TARGET=Android - Unity -batchmode -quit -projectPath . -executeMethod GameBuildPipeline.BuildAndroid artifacts: paths: - Builds/ expire_in: 7 days7.3 GitHub Actions 集成
# .github/workflows/build.yml name: Build on: push: branches: [main] jobs: build: runs-on: ubuntu-latest container: image: unityci/editor:2022.3.20f1 steps: - uses: actions/checkout@v2 - name: Build Android env: BUILD_OUTPUT: ${{ github.workspace }}/Builds BUILD_VERSION: ${{ github.run_number }} BUILD_TARGET: Android run: | Unity -batchmode -quit \ -projectPath . \ -executeMethod GameBuildPipeline.BuildAndroid八、常见问题
8.1 构建失败
| 错误 | 原因 | 解决 |
|---|---|---|
| 资源找不到 | 路径错误 | 检查 SearchPattern |
| 循环依赖 | 资源互相引用 | 抽离共享包 |
| Bundle 命名冲突 | 同名 Bundle | 修改命名规则 |
| 类型不匹配 | 资源类型错误 | 检查 AssetImporter |
8.2 构建警告
⚠️ Warning: 资源 X 未被任何 Collector 收集 → 检查 Collect 规则 ⚠️ Warning: Bundle 数量过多(> 1000) → 调整 PackRule ⚠️ Warning: 资源大小超过 50MB → 拆分资源8.3 性能调优
// 1. 启用缓存 var buildParams = new BuildParameters { UseBuildCache = true, // ... }; // 2. 关闭类型树(减小体积) var buildParams = new BuildParameters { WriteTypeTree = false, // 不写入类型树 // 谨慎使用,可能影响兼容性 }; // 3. 启用并行 var buildParams = new BuildParameters { MaxConcurrent = 8, // 并行数 };九、总结
9.1 本章要点回顾
- Collector:配置资源收集规则,决定哪些资源进 Bundle
- Group:打包配置的集合,决定如何打包
- Build:构建流程,包括收集、依赖、构建、清单、报告
- 优化:增量构建、并行构建、构建缓存
9.2 与前后章节的关联
- 前章:10-04 介绍了资源组织与依赖管理
- 本章:聚焦资源配置与打包
- 后章:10-06 将介绍资源加载与使用
9.3 实践建议
- Collector 规则要严格:避免"什么都打包"
- Group 数量适中:3-10 个最佳
- 增量构建:日常开发用增量,正式发版用全量
- CI/CD 自动化:避免人工打包
- 报告必看:BuildReport 是优化的起点
上一篇:资源组织与依赖管理实战
下一篇:资源加载与使用