☰
Unity构建全流程揭秘:从编译到打包的终极优化
2026/10/18 1:16:42 网站建设 项目流程

点击Build后,Unity 会把 C# 代码、场景、资源和 Shader 处理成目标平台能运行的安装包。了解每个阶段做了什么,有助于控制包体、加快构建、排查问题。

💡 本文以 Unity 2021 LTS ~ Unity 6 为参考。Unity 6 中 Build Settings 改名为Build Profiles,但核心流程相同。


一、构建流程全景图

点击 Build │ ├─① 构建前回调(IPreprocessBuild) │ ├─② 脚本编译 ──→ C# → IL(.dll) │ │ │ ├─ Mono:直接打包 DLL │ └─ IL2CPP:裁剪 → 转 C++ → 原生编译 │ ├─③ 场景处理(IProcessSceneWithReport) │ ├─④ 资源收集:Scenes In Build + 依赖 + Resources │ ├─⑤ 资源转换:纹理压缩、模型、音频、Shader 变体编译 │ ├─⑥ 序列化写入:level0、sharedassets、resources.assets │ ├─⑦ 平台打包:Gradle(Android)/ Xcode 工程(iOS) │ └─⑧ 构建后回调(IPostprocessBuild)

二、脚本编译:Mono 与 IL2CPP

1. 两种脚本后端

对比项MonoIL2CPP
原理打包 IL,运行时 JIT 编译IL 转 C++,提前编译为机器码(AOT)
构建速度快慢(需编译大量 C++)
运行性能一般更好
反编译难度低(DLL 可直接反编译)较高
平台要求—iOS 必须;Google Play 上架 64 位必须
热更新可直接替换 DLL需借助 HybridCLR、xLua 等方案

2. IL2CPP 的完整流程

C# 源码 ↓ Roslyn 编译 Assembly-CSharp.dll 等 IL 程序集 ↓ UnityLinker(托管代码裁剪) 裁剪后的 DLL ↓ il2cpp.exe C++ 源码(Library/Il2cppBuildCache) ↓ Clang / NDK / Xcode libil2cpp.so(Android)/ UnityFramework(iOS)/ GameAssembly.dll(PC)

3. 代码裁剪(Managed Stripping)

Player Settings → Other Settings → Managed Stripping Level

级别说明
Disabled不裁剪(仅 Mono 可选)
Minimal / Low保守裁剪,较安全
Medium裁剪更多,反射代码可能出问题
High包体最小,风险最高

被反射、序列化库(如 Newtonsoft.Json)调用的类型可能被误删,导致运行时报MissingMethodException。可以用link.xml保留:

<!-- Assets/link.xml --><linker><assemblyfullname="Assembly-CSharp"><typefullname="MyGame.Data.PlayerConfig"preserve="all"/></assembly><assemblyfullname="Newtonsoft.Json"preserve="all"/></linker>

或者在代码中标记:

usingUnityEngine.Scripting;[Preserve]publicclassPlayerConfig{}

4. 加快脚本编译

  • 使用Assembly Definition(.asmdef)拆分程序集,修改代码时只重新编译相关部分;
  • 第三方插件放入独立的程序集;
  • 编辑器代码放在Editor文件夹或 Editor-only 程序集中,避免被打进包体。

三、资源收集:哪些东西会被打进包?

Unity 采用依赖追踪,并不是把 Assets 下所有文件都打进去。

来源是否打包说明
Scenes In Build中的场景✅以及它们引用的所有资源
场景引用的资源✅自动追踪依赖
Resources文件夹✅全部打包无论是否使用,都会打进resources.assets
StreamingAssets文件夹✅ 原样复制不做任何处理
Always Included Shaders✅Graphics Settings 中配置
未被引用的资源❌不会打包
Editor 文件夹❌仅编辑器使用

构建产物结构(以 PC 为例)

MyGame_Data/ ├── level0, level1 ... ← 场景数据 ├── sharedassets0.assets ... ← 场景共享的资源 ├── resources.assets ← Resources 文件夹内容 ├── globalgamemanagers ← 项目设置 ├── StreamingAssets/ ← 原样复制 └── Managed/ 或 il2cpp_data/ ← 代码相关

⚠️Resources是包体膨胀的常见原因。官方建议仅用于少量全局配置,大量资源应改用 AssetBundle 或 Addressables。


四、AssetBundle 与 Addressables

1. 为什么需要它们

  • 减小首包体积,资源可按需下载;
  • 支持资源热更新;
  • 精细控制内存加载与卸载。

2. 构建顺序很重要

使用 Addressables 时,需要先构建资源,再构建 Player:

① Addressables Groups → Build → New Build → Default Build Script ② File → Build Settings → Build

也可以在Addressable Asset Settings中勾选Build Addressables on Player Build,让它自动执行。

3. 常见注意点

问题建议
资源被重复打包使用Analyze工具检查重复依赖,将公共资源单独分组
压缩格式本地包用LZ4(加载快);远程包可选 LZMA(体积小,但需解压)
热更新使用Update a Previous Build,并保留addressables_content_state.bin
Shader 重复公共 Shader 单独打包,避免每个包各带一份

五、Shader 编译与变体

Shader 往往是构建慢、包体大、内存高的头号原因。

1. 变体是怎么产生的

#pragma multi_compile _ _FOG_ON // 2 个 #pragma multi_compile _ _SHADOWS_ON // 2 个 #pragma multi_compile _LOW _MID _HIGH // 3 个 // 2 × 2 × 3 = 12 个变体,再乘以 Pass 数和图形 API 数

2. multi_compile 与 shader_feature

关键字打包规则适用场景
multi_compile所有组合都打包运行时代码会切换的功能
shader_feature只打包材质实际用到的组合材质上固定的功能开关
_local后缀局部关键字,不占全局关键字数量推荐优先使用

3. 剔除不需要的变体

usingSystem.Collections.Generic;usingUnityEditor.Build;usingUnityEditor.Rendering;usingUnityEngine;usingUnityEngine.Rendering;publicclassStripDebugShaderVariants:IPreprocessShaders{publicintcallbackOrder=>0;privatestaticreadonlyShaderKeywordDebugKeyword=newShaderKeyword("_DEBUG_VIEW");publicvoidOnProcessShader(Shadershader,ShaderSnippetDatasnippet,IList<ShaderCompilerData>data){for(inti=data.Count-1;i>=0;i--){if(data[i].shaderKeywordSet.IsEnabled(DebugKeyword))data.RemoveAt(i);}}}

4. 其他建议

  • URP Asset中关闭不使用的功能(如额外光源阴影、SSAO),URP 会自动剔除对应变体;
  • Graphics Settings → Shader Stripping中剔除不用的雾效、光照贴图模式;
  • 使用ShaderVariantCollection记录并预热变体,避免运行时首次使用卡顿;
  • Editor.log 中搜索Compiling shader可查看各 Shader 的变体数量。

六、Android 构建细节

1. 关键设置

设置推荐
Scripting BackendIL2CPP
Target ArchitecturesARM64(Google Play 要求 64 位)
纹理压缩ASTC
输出格式上架 Google Play 用AAB;测试用 APK
MinifyRelease 开启 R8,注意保留插件类
Target API Level满足 Google Play 的最新要求

2. 构建流程

Unity 导出 Gradle 工程(Library/Bee/Android/Prj) ↓ Gradle 编译 Java/Kotlin、合并 AndroidManifest、打包资源 ↓ 签名(Keystore) ↓ APK / AAB

3. 常见问题

问题解决方法
Gradle 构建失败查看 Console 中完整的 Gradle 报错;检查 SDK / NDK / JDK 版本
依赖冲突使用External Dependency Manager统一处理
需要自定义 GradlePublishing Settings 中启用 Custom Main Gradle Template 等模板
AAB 超过 150MB使用Play Asset Delivery拆分资源
需要导出工程调试勾选Export Project,用 Android Studio 打开

七、iOS 构建细节

Unity不直接生成 ipa,而是导出一个Xcode 工程,再由 Xcode 编译、签名、打包。

Unity Build → Xcode 工程 → Xcode Archive → 签名 → ipa

1. Append 与 Replace

选项说明
Append保留你在 Xcode 中的修改,增量更新,速度快
Replace完全重新生成工程,手动修改会丢失

2. 用代码自动修改 Xcode 工程

手动改 Xcode 设置容易遗漏,推荐在构建后回调中自动处理:

#ifUNITY_IOSusingSystem.IO;usingUnityEditor;usingUnityEditor.Callbacks;usingUnityEditor.iOS.Xcode;publicstaticclassIOSPostBuild{[PostProcessBuild(100)]publicstaticvoidOnPostBuild(BuildTargettarget,stringpath){if(target!=BuildTarget.iOS)return;// 修改工程设置stringprojPath=PBXProject.GetPBXProjectPath(path);varproj=newPBXProject();proj.ReadFromFile(projPath);stringframeworkTarget=proj.GetUnityFrameworkTargetGuid();proj.SetBuildProperty(frameworkTarget,"ENABLE_BITCODE","NO");proj.AddFrameworkToProject(frameworkTarget,"StoreKit.framework",false);proj.WriteToFile(projPath);// 修改 Info.pliststringplistPath=Path.Combine(path,"Info.plist");varplist=newPlistDocument();plist.ReadFromFile(plistPath);plist.root.SetString("NSCameraUsageDescription","需要使用相机进行扫码");plist.WriteToFile(plistPath);}}#endif

八、构建回调:在流程中插入自己的逻辑

接口时机典型用途
IPreprocessBuildWithReport构建开始前写入版本号、切换配置、检查资源
IProcessSceneWithReport处理每个场景时删除调试物体、烘焙数据
IPreprocessShaders编译 Shader 变体前剔除变体
IPostprocessBuildWithReport构建完成后修改工程、上传、通知
usingUnityEditor;usingUnityEditor.Build;usingUnityEditor.Build.Reporting;usingUnityEngine;publicclassBuildVersionWriter:IPreprocessBuildWithReport{publicintcallbackOrder=>0;publicvoidOnPreprocessBuild(BuildReportreport){stringbuildTime=System.DateTime.Now.ToString("yyyyMMdd_HHmm");PlayerSettings.bundleVersion=$"1.0.{buildTime}";Debug.Log($"[Build] 平台:{report.summary.platform},版本:{PlayerSettings.bundleVersion}");}}

九、自动化构建与 CI

1. 构建脚本

// Assets/Editor/Builder.csusingSystem;usingSystem.Linq;usingUnityEditor;usingUnityEditor.Build.Reporting;usingUnityEngine;publicstaticclassBuilder{publicstaticvoidBuildAndroid(){// 取 Build Settings 中已启用的场景string[]scenes=EditorBuildSettings.scenes.Where(s=>s.enabled).Select(s=>s.path).ToArray();EditorUserBuildSettings.buildAppBundle=false;// true 则输出 AABPlayerSettings.SetScriptingBackend(BuildTargetGroup.Android,ScriptingImplementation.IL2CPP);PlayerSettings.Android.targetArchitectures=AndroidArchitecture.ARM64;varoptions=newBuildPlayerOptions{scenes=scenes,locationPathName="Builds/Android/MyGame.apk",target=BuildTarget.Android,options=BuildOptions.None// 调试可用 Development | ConnectWithProfiler};BuildReportreport=BuildPipeline.BuildPlayer(options);BuildSummarysummary=report.summary;if(summary.result==BuildResult.Succeeded){Debug.Log($"构建成功:{summary.totalSize/1024/1024}MB,耗时{summary.totalTime}");}else{Debug.LogError($"构建失败:{summary.result}");if(Application.isBatchMode)EditorApplication.Exit(1);// 让 CI 识别失败}}}

2. 命令行构建

# Windows"C:\Program Files\Unity\Hub\Editor\2022.3.xxf1\Editor\Unity.exe"^-batchmode-quit-nographics^-projectPath"D:\Projects\MyGame"^-buildTargetAndroid ^-executeMethodBuilder.BuildAndroid ^-logFile"D:\Projects\MyGame\build.log"
参数作用
-batchmode无界面运行
-quit执行完自动退出
-buildTarget启动时直接切换平台,避免二次导入资源
-executeMethod调用静态构建方法
-logFile输出日志,CI 中排查问题必备

可接入Jenkins、GitHub Actions(GameCI)、GitLab CI、TeamCity等工具实现自动出包。

⚠️ 同一个工程目录不能同时打开两个 Unity 实例,CI 构建前需确保编辑器已关闭。


十、包体分析与优化

1. 查看构建报告

构建完成后,打开Editor.log(Console 右上角菜单 → Open Editor Log),搜索Build Report:

Textures 120.5 mb 58.3% Meshes 20.1 mb 9.7% Animations 15.2 mb 7.4% Sounds 18.0 mb 8.7% Shaders 8.3 mb 4.0% ... Used Assets and files from the Resources folder, sorted by uncompressed size: 12.0 mb Assets/Textures/Background.png ...

也可以安装Build Report Inspector包,或使用 Asset Store 上的 Build Report Tool,可视化分析。

2. 常见优化手段

类别方法
纹理移动端用 ASTC;按需设置 Max Size;关闭不需要的 Mipmap 和 Read/Write
模型关闭 Read/Write;压缩网格;去掉不需要的法线、切线、UV2
动画设置 Anim. Compression 为 Optimal;去掉缩放曲线
音频长音乐用 Streaming + Vorbis;短音效用 ADPCM;单声道化
代码提高 Managed Stripping Level;移除无用插件
Shader减少 multi_compile;剔除变体
Resources清理不用的资源,迁移到 Addressables
字体中文字体只保留使用到的字符(TMP 静态字体集)

十一、提升构建速度

方法说明
保留 Library 文件夹资源导入缓存都在这里,删除后需要重新导入,非常耗时
使用 Unity Accelerator团队共享资源导入缓存
IL2CPP Code Generation选Faster (smaller) builds可加快构建
开发阶段用 Mono在 PC / Android 上快速迭代,发版时再切 IL2CPP
Patch / Patch And RunAndroid 开发包仅推送改动的部分
减少 Shader 变体往往是构建最慢的环节
SSD + 足够内存IL2CPP 和 Shader 编译对 IO、CPU 要求很高
固定构建机平台避免反复切换平台导致资源重新导入

十二、常见构建问题速查

现象可能原因解决方法
编辑器正常,真机报MissingMethodException代码被裁剪添加link.xml或[Preserve]
构建时报UnityEditor命名空间错误编辑器代码被打进包放进Editor文件夹或用#if UNITY_EDITOR包裹
真机材质粉红Shader 变体被剔除或未打包检查 shader_feature、Always Included Shaders、ShaderVariantCollection
首次出现特效时卡顿Shader 运行时编译使用 ShaderVariantCollection 预热
包体突然变大Resources 新增资源、AB 重复依赖查看 Build Report,使用 Addressables Analyze
IL2CPP 构建失败NDK 版本或路径问题、路径含中文使用 Unity Hub 安装的 NDK;工程路径避免中文和空格
构建越来越慢Shader 变体增长、缓存失效分析 Editor.log 中各阶段耗时
平台切换很久资源需重新导入使用-buildTarget参数或 Accelerator

总结

代码 → 编译与裁剪(Mono / IL2CPP) 资源 → 依赖追踪 + 格式转换(纹理压缩、Shader 变体) 打包 → 平台工程(Gradle / Xcode) 控制 → 构建回调 + 构建脚本 + CI 自动化 优化 → Build Report 分析包体,Library 缓存与变体剔除提升速度

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询