gamedev-skills/awesome-gamedev-agent-skillsCheck passed
SKILL DETAIL
unity-build-pipeline
gamedev-skills/awesome-gamedev-agent-skills/unity-build-pipeline
Build and ship Unity 6.3 LTS players: build settings and scenes, player/quality settings, the IL2CPP vs Mono scripting backend, managed code stripping, scripted BuildPipeline.BuildPlayer, and CI/headless builds. Use when configuring or automating a build, choosing a scripting backend, shrinking build size, or when the user mentions Unity build, player settings, IL2CPP, code stripping, or Addressables.
Installs · 260View source
Installation
npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill unity-build-pipeline
Skill files
SKILL.md
Last synced · Oct 25, 2026
references/ci-build-script.md›
# CI / headless build script (Unity 6.3 LTS)
Depth for `unity-build-pipeline`: a reusable editor build script that takes command-line
arguments, stamps a version, and returns a proper exit code. Verified against
`ScriptReference/BuildPipeline.BuildPlayer`, `ScriptReference/EditorUserBuildSettings.SwitchActiveBuildTarget`
and the 6000.3 Manual page "Editor command line arguments".
**Pick the platform with Unity's own `-buildTarget` flag, not from inside the script.**
`EditorUserBuildSettings.SwitchActiveBuildTarget` is not available in batch mode: a platform
switch needs a script recompile, which cannot happen while your `-executeMethod` code runs.
`-buildTarget` makes the Editor start on that platform, so scripts compile with the right
`UNITY_*` defines before your method is called. Its values are lowercase names such as
`win64`, `osxuniversal`, `linux64`, `android`, `ios` and `webgl`.
## A parameterised build method
```csharp
using System;
using System.Linq;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
public static class CIBuild
{
// Invoke with: -buildTarget win64 -executeMethod CIBuild.Run -out Builds/Game.exe -appVersion 1.2.3
public static void Run()
{
string[] args = Environment.GetCommandLineArgs();
if (!HasArg(args, "-buildTarget"))
{
// Without it the build silently uses whatever platform the project was last on.
Debug.LogError("[CIBuild] pass -buildTarget <name> so the Editor starts on that platform");
EditorApplication.Exit(2);
return;
}
string outPath = ArgValue(args, "-out", "Builds/Game.exe");
string version = ArgValue(args, "-appVersion", PlayerSettings.bundleVersion);
bool dev = HasArg(args, "-dev");
// Already switched by -buildTarget at startup, with matching script defines.
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
PlayerSettings.bundleVersion = version; // stamp the version into the player
var options = new BuildPlayerOptions
{
scenes = EnabledScenes(),
locationPathName = outPath,
target = target,
options = dev ? BuildOptions.Development : BuildOptions.None,
};
BuildReport report = BuildPipeline.BuildPlayer(options);
BuildSummary s = report.summary;
if (s.result == BuildResult.Succeeded)
{
Debug.Log($"[CIBuild] OK {target} {version} -> {outPath} ({s.totalSize} bytes)");
EditorApplication.Exit(0); // explicit success exit code for CI
}
else
{
Debug.LogError($"[CIBuild] FAILED: {s.totalErrors} errors, result={s.result}");
EditorApplication.Exit(1); // non-zero fails the pipeline
}
}
private static string[] EnabledScenes() =>
EditorBuildSettings.scenes.Where(sc => sc.enabled).Select(sc => sc.path).ToArray();
private static int IndexOf(string[] args, string key) =>
Array.FindIndex(args, a => string.Equals(a, key, StringComparison.OrdinalIgnoreCase));
private static bool HasArg(string[] args, string key) => IndexOf(args, key) >= 0;
private static string ArgValue(string[] args, string key, string fallback)
{
int i = IndexOf(args, key);
return (i >= 0 && i + 1 < args.Length) ? args[i + 1] : fallback;
}
}
```
## Invoking from CI
```bash
# No -quit needed: the method ends the process itself with EditorApplication.Exit.
Unity -batchmode -nographics \
-projectPath "$CI_PROJECT_DIR" \
-buildTarget win64 \
-executeMethod CIBuild.Run \
-out "Builds/Win/Game.exe" -appVersion "1.2.$BUILD_NUMBER" \
-logFile - || exit 1
```
- Pipe `-logFile -` to stdout so the CI captures the Unity log.
- `EditorApplication.Exit(code)` inside the method is the reliable way to set the exit code;
an uncaught exception also exits with 1, but `-quit` after a `Debug.LogError` returns 0.
- One platform per Editor launch: run the command again with another `-buildTarget` for the
next platform instead of switching inside the method.
- Activate the Unity license in batch mode (`-username`/`-password`/`-serial` or a license
file) as a separate step before building on a clean runner.
### Building a saved Build Profile with no script
If the team keeps its platform setup in Build Profile assets, Unity can build one directly:
```bash
Unity -batchmode -nographics -quit \
-projectPath "$CI_PROJECT_DIR" \
-activeBuildProfile "Assets/Settings/Build Profiles/WindowsRelease.asset" \
-build "Builds/Win/Game.exe" \
-logFile -
```
`-activeBuildProfile` takes a path relative to the project root and adds the profile's
scripting defines before compilation (it does not remove existing ones). Combined with
`-executeMethod` instead of `-build`, the defines are compiled in before your method runs; then
build with `BuildPipeline.BuildPlayer(new BuildPlayerWithProfileOptions { buildProfile = ..., locationPathName = ... })`
so the profile's own scene list and settings are used.
## Addressables content build (when used)
```csharp
using UnityEditor.AddressableAssets.Settings;
// Build the Addressables content bundles BEFORE the player build, or runtime loads fail.
public static void BuildContent()
{
AddressableAssetSettings.BuildPlayerContent(out var result);
if (!string.IsNullOrEmpty(result.Error))
throw new System.Exception($"Addressables build failed: {result.Error}");
}
```
Call it explicitly in CI even if the project's **Build Addressables on Player Build** setting
is on: when that setting is left at "Use global Settings", the answer comes from each
machine's Editor Preferences, so a clean runner may skip the content build.
## Gotchas
- Do not call `EditorUserBuildSettings.SwitchActiveBuildTarget` in batch mode; it is not
available there. Use `-buildTarget` (or `-activeBuildProfile`) on the command line.
- Reading args via `Environment.GetCommandLineArgs()` includes Unity's own flags. Name custom
keys so they cannot collide with a real Unity argument (`-buildTarget`, `-build`, `-quit`
and `-version` are all taken; `-version` prints the Editor version and exits without
building). `-out`, `-appVersion` and `-dev` are free.
- A build server needs the platform module installed (e.g. IL2CPP toolchain, Android SDK/NDK)
or the build fails late with a toolchain error.
SKILL.md›
---
name: unity-build-pipeline
description: >
Build and ship Unity 6.3 LTS players: build settings and scenes, player/quality settings, the
IL2CPP vs Mono scripting backend, managed code stripping, scripted BuildPipeline.BuildPlayer,
and CI/headless builds. Use when configuring or automating a build, choosing a scripting
backend, shrinking build size, or when the user mentions Unity build, player settings,
IL2CPP, code stripping, or Addressables.
---
# Unity Build Pipeline
Configure, script, and automate Unity 6.3 LTS player builds: scenes, platform target, scripting
backend, stripping, and headless/CI builds. Targets **Unity 6.3 LTS (6000.3)**.
## When to use
- Use when setting up Build Settings/Profiles, choosing a platform and scripting backend
(Mono vs IL2CPP), reducing build size with managed stripping, scripting a repeatable build
with `BuildPipeline.BuildPlayer`, or wiring a CI/headless build.
- Use when the project has `ProjectSettings/EditorBuildSettings.asset` or a CI build script.
**When *not* to use:** authoring a CI service config end-to-end is DevOps; this skill covers
the Unity-side build API and settings. Console/platform certification specifics are
platform-NDA territory. Storefront submission → `steam-publish` / `itch-publish`.
## Core workflow
1. **List the scenes to build** (File → Build Profiles → Scene List, or
`EditorBuildSettings.scenes`). Only listed, enabled scenes ship; scene 0 is the start scene.
A Build Profile asset can carry its own Scene List (Add Settings → Scene List); when it
does, that profile builds its list, not the global one.
2. **Pick the platform target** and switch the active build target if needed
(`BuildTarget` / `EditorUserBuildSettings`). In batch mode, pass `-buildTarget <name>` on
the command line instead: `SwitchActiveBuildTarget` is not available there.
3. **Choose the scripting backend** (Player Settings): **Mono** (fast iteration, desktop) vs
**IL2CPP** (AOT C++; required for many platforms, better perf, harder to reverse). IL2CPP
needs the platform's C++ toolchain installed. (Forward note: Unity is developing a
**CoreCLR** backend as a modern .NET runtime successor to Mono, but in the Unity 6.7 beta
it is still *experimental*, desktop-only, and documented as not suitable for production —
so for 6.3 LTS the choice stays Mono vs IL2CPP.)
4. **Tune size/perf:** set Managed Stripping Level (Disabled → Minimal → Low → Medium → High)
and protect reflection-only code with a `link.xml`. Set Quality Settings per platform.
5. **Script the build** with `BuildPipeline.BuildPlayer(BuildPlayerOptions)` and **inspect the
returned `BuildReport`** — a non-`Succeeded` result must fail your pipeline.
6. **Run headless** for CI with `-batchmode -quit -executeMethod`, and check the exit code.
7. **Verify** the actual output runs (launch the player), not just that the build returned
without throwing.
## Patterns
### 1. Scripted build with a result check
```csharp
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
public static class BuildScript
{
[MenuItem("Build/Windows x64")]
public static void BuildWindows()
{
var options = new BuildPlayerOptions
{
scenes = new[] { "Assets/Scenes/Main.unity", "Assets/Scenes/Level1.unity" },
locationPathName = "Builds/Windows/Game.exe",
target = BuildTarget.StandaloneWindows64,
options = BuildOptions.None, // add BuildOptions.Development for a dev build
};
BuildReport report = BuildPipeline.BuildPlayer(options);
BuildSummary summary = report.summary;
if (summary.result != BuildResult.Succeeded)
throw new System.Exception($"Build failed: {summary.totalErrors} errors");
Debug.Log($"Build OK: {summary.totalSize} bytes in {summary.totalTime}");
}
}
```
### 2. Headless / CI invocation
```bash
# Exit code is 0 on success; -quit ensures the editor closes; -nographics for build servers.
# -buildTarget makes the Editor start on that platform, so scripts compile with its defines.
Unity -batchmode -quit -nographics \
-projectPath "/path/to/Project" \
-buildTarget win64 \
-executeMethod BuildScript.BuildWindows \
-logFile -
```
### 3. Build a saved Build Profile
```csharp
using UnityEditor;
using UnityEditor.Build.Profile;
using UnityEditor.Build.Reporting;
public static class ProfileBuild
{
[MenuItem("Build/Windows Release Profile")]
public static void Build()
{
var profile = AssetDatabase.LoadAssetAtPath<BuildProfile>(
"Assets/Settings/Build Profiles/WindowsRelease.asset");
var options = new BuildPlayerWithProfileOptions
{
buildProfile = profile, // platform, scene list, defines from the asset
locationPathName = "Builds/Windows/Game.exe",
options = BuildOptions.None,
};
BuildReport report = BuildPipeline.BuildPlayer(options);
if (report.summary.result != BuildResult.Succeeded)
throw new System.Exception($"Build failed: {report.summary.totalErrors} errors");
}
}
```
### 4. Protect stripped code with `link.xml`
```xml
<!-- Assets/link.xml — keep types the linker can't see are used (reflection, JSON, plugins). -->
<linker>
<assembly fullname="MyGameRuntime" preserve="all"/>
</linker>
```
## Pitfalls
- **A scene loads in the Editor but is missing in the build** — it isn't in the scene list
(File → Build Profiles → Scene List, or the built profile's own list) or is disabled.
`SceneManager.LoadScene` only sees listed scenes.
- **IL2CPP build fails on a fresh machine** — the platform C++ toolchain (e.g. Windows build
tools, Android NDK) isn't installed. Mono has no such requirement.
- **`MissingMethodException`/`TypeLoadException` only in the build** — managed stripping removed
reflection-only code. Lower the stripping level or add a `link.xml` preserve entry.
- **Treating "BuildPlayer returned" as success** — always check `BuildReport.summary.result`;
it can return with errors.
- **Addressables content is stale/missing** — Addressables (`com.unity.addressables`) content
is built by Build → Addressables or by `AddressableAssetSettings.BuildPlayerContent`. The
**Build Addressables on Player Build** setting can do it during a player build, but when it
is left at "Use global Settings" the result depends on each machine's Preferences — call the
content build explicitly in CI, and check the profile's load path.
- **The build platform's code didn't compile in** — a scripted `BuildPlayer` for a target
other than the active one keeps the active platform's `UNITY_*` defines, and define changes
only apply after a domain reload. Switch platform first (in the Editor) or launch with
`-buildTarget` (batch mode).
- **Build ignores scenes you added to the global list** — the Build Profile you build has its
own Scene List. Edit that list; a profile without a Scene List uses the global one.
- **Shipping a Development build** — `BuildOptions.Development` enables the profiler/debugging
and is slower; use `BuildOptions.None` for release.
## References
- For a complete **multi-platform CI build script** (`-buildTarget` per platform, version
stamping, argument parsing, exit codes), building a Build Profile from the command line, and
an Addressables content-build call, read `references/ci-build-script.md`.
- Primary docs: `ScriptReference/BuildPipeline.BuildPlayer`, `ScriptReference/BuildPlayerWithProfileOptions`,
the Manual pages "Editor command line arguments" and "Manage scenes in a build", and Unity
Manual build sections (player settings, managed code stripping). For the forward-looking
CoreCLR backend, see the experimental note at
`https://docs.unity3d.com/6000.7/Documentation/Manual/scripting-backends-coreclr.html`.
## Related skills
- `steam-publish` / `itch-publish` — distributing the player you just built.
- `unity-csharp-scripting` — editor scripting conventions used by build scripts.