Unity expert (technical director and agent protocol)
SkillWeb & browsingLets your agent work with Unity 6.3 on a Mac, handling batch runs, editor sessions, tests, and command-line builds.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Unity expert (technical director and agent protocol) skill
About this skill
Use when an agent drives Unity 6.3 (6000.3) on a Mac for any task: batch mode and -executeMethod jobs, a running editor (the user's GUI editor or a headless one), the Unity CLI and com.unity.pipeline, MCP servers, the Test Framework, command-line builds; when a batch run fails silently, 'Scripts hav
What this skill tells your AI
The instructions your AI receives, as published by scenario-labs/skills in skills/game-engines/unity/scenario-unity-expert/SKILL.md and read by ahel’s review.
Expert Unity work is a loop run against numbers: set the budget, build one stage, measure it, look at it, fix it, then advance. An agent without a mouse gets there by choosing the right channel for each action, editing through serialization with explicit saves, and never trusting its own eyes alone. This skill is that protocol, the shared toolkit every scenario-unity-* skill imports (scripts/), and the map of the team. Target: Unity 6000.3.21f1 (6.3 LTS), URP 17.3, macOS Apple Silicon. If a sibling skill named here is missing from your available skills, ask the user to install it (npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and flag the gap.
Status (2026-09-24, after blind grading and the refactor): every toolkit function ran in Unity 6000.3.21f1 here (tests/code/unity-expert/run_all.sh --live --builds; results in archive/tests/unity-expert/live_results.jsonl). Every call from before the refactor is unchanged.
Stance (the expert delta)
- One editor per project, and the live editor wins. "You can't open a project in batch mode while the Editor has the same project open" (6.3 Manual). When an editor holds the project, drive it (
ut_live), never hand-edit.unity/.prefab/.assetYAML: wrong fileIDs, invisible until reimport, easy to hit the wrong scene (Unity's unity-cli skill). - A job is only as honest as its result line. Pass on the
AGENT_RESULTenvelope and the log scan, not on "it exited". A compile error in ANY assembly aborts every batch job with "Scripts have compiler errors" (observed; the live editor's twin is Safe Mode, which also blockscom.unity.pipeline). - Numbers before pixels, milliseconds not fps. 16.67 ms at 60 fps, 33.33 at 30; mobile keeps about 65% for thermals (Unity profiling e-book). Editor Play mode is for iteration; the verdict comes from a development player on the lowest target device.
- Look AND check, and know the capture's lies. Every frame goes through
ut_review.image_checksand a contact sheet you open. Epic's lighting agent approved an all-white frame (scenario-unreal-expert sources); here batch captures showed first-frame white materials, culled 2D lights, bind poses, no Overlay UI and black metals (observed by the team): anything wrong only in an Editor capture is re-checked in a player first. Agent-built levels come out "90%" done, a shelf in front of a door (Code Monkey, ppFshgFOgXU [00:10:52]). - Fix the platform at launch. Target switches inside a batch job silently do nothing (6.3 Manual);
-buildTarget/-activeBuildProfileat launch, one process per target. The editor reopens on the last active platform (observed): pin it. - Named jobs over eval.
evalto explore, a named job for anything repeated: "A written command turns that workflow into something deterministic and repeatable" (DgNrgZeJOxQ [00:07:41]); generic run-command tools drift (git-amend, VPjo-M6mPkE [00:03:54]). - Tests are the contract. Give work a failing test and a fence ("It can only touch the card data", DgNrgZeJOxQ [00:05:31]);
-runTestsnever with-quit. - Be honest about visual editors. Shader Graph, VFX Graph, the Animator window, UI Builder, Tile Palette painting and terrain sculpting have no authoring API: use the substitute (HLSL, templates with exposed properties,
AnimatorControllerAPI, UXML/USS text,TerrainData) or name the human path.
Channels
| Channel | Use for | Cost (measured here) | Rules |
|---|---|---|---|
Batch job ut_run.run_method | anything when no editor holds the project | 6 to 13 s per job (editor boot included) | -nographics by default; graphics=True for captures, bakes (never bake with -nographics), profiling; quit=False for async jobs; an editor that hangs after its result is ended by PID (grace, 10 s) |
Resident headless editor + AgentKit bridge ut_live.start_headless, call | many calls in a row | boot 5 to 20 s once, then 0.1 to 0.5 s per call | holds the project and a license seat; stop_headless |
User's GUI editor + AgentKit bridge ut_live.call, eval | the user has the project open | same code path as headless (not run: no GUI windows during tests) | the user may need to focus Unity once so it compiles AgentKit; never re-open the user's scene without asking; save what you change |
Unity CLI + com.unity.pipeline ut_live.cli_*, eval (auto) | fastest live channel: 151 commands, Roslyn eval with no domain reload | eval 0.03 to 0.15 s after a 0.5 to 1.1 s first call | CLI 1.0.0-beta.11 (portable copy), package 0.7.0-exp.1 installed before the editor opens; blocked by Safe Mode, possibly by a sandbox |
| MCP | only unity mcp or a vetted community server | Unity's AI Assistant MCP server is deprecated ("Use the Unity CLI instead") | |
Test Framework ut_run.run_tests | EditMode and PlayMode verdicts, content validation | about 14 s per platform | NUnit XML parsed; exit 2 when a test failed (observed) |
ut_live.channel(P) picks batch, bridge, pipeline, or blocked (ask the user). "Can't connect" does not prove the editor is closed: rule out Safe Mode and a sandboxed shell first (unity-cli skill).
Establish first
Ask once, then run: target platforms and device class; frame budget (default 60 fps desktop, 30 fps mobile at 65%); 2D or 3D; pipeline (default URP 17.3, HDRP only for high-end PC or console); quality tiers; art style; whether the user's editor has the project open; deliverable. Then ut_env.preflight(P) and AgentJob.Echo; write what they report (pipeline asset, color space, active target, quality level, Enter Play Mode setting, input handler, scripting backend) into the plan.
Workflow (the expert loop)
- Brief in numbers. Budgets per platform, resolution, scale, style. GATE: every later check has a number to compare with.
- Preflight. License days left, modules, lock state, compile health (one
Echojob). GATE:ok, zero compile errors, zero new CS0618 warnings. - Baseline.
AgentAudit.AuditScene/AuditAssets; captures of fixed bookmarks (AgentCapture.SaveBookmark); a Play-mode profile if performance matters. GATE: audit errors fixed or listed; contact sheet opened. - Build one stage with small idempotent jobs (
references/writing-jobs.md): get-or-create, serialized edits, explicit saves. Route domain work to the owner (below). - Measure. Re-run the audit, the tests,
AgentProfile.PlayModeTimings+ut_stat.budget_check, or the build summary. GATE: numbers against the brief. - Look AND check. Same bookmarks,
ut_review.review_capture, open the sheet, judge with the domain skill's critique. GATE: no blank or magenta frame, saturation not collapsed, the change visible. - Fix before advancing, one change at a time;
ut_review.compareandut_stat.compare_framesprove parity or gain. - Deliver with evidence. Each step with its Unity call (
references/toolkit-map.md), then Verified (what ran, job id, number or frame) and Assumed (not run,[verify], defaults). Never call a scene, shader, level or build done without numbers and a frame you looked at.
Numbers
| Value | Relative to | Source |
|---|---|---|
| 16.67 / 33.33 ms | frame budget at 60 / 30 fps | profiling e-book |
| x 0.65 | mobile sustained budget (10.8 ms at 60, 21.7 at 30) | profiling and mobile e-books |
| 6 to 13 s | one batch job on this project, editor boot included | observed |
| about 14 s | one run_tests platform run (EditMode 2 tests, PlayMode 1 test) | observed |
| 60 s (12 s incremental), 114.7 MB | macOS Mono player build of the demo scene (universal) | observed |
| 259 s (25 s incremental), 12.2 MB | Web build (IL2CPP, Brotli) of the same scene | observed |
| 0.1 to 0.5 s / 0.03 to 0.15 s | live call through the AgentKit bridge / Pipeline eval (two runs) | observed |
| 30 days | Personal license offline window (license_ok() reads it) | observed + Unity support FAQ |
| 10 s / 120 s | grace before ut_run ends a silent editor after its result with / without a shutdown line | toolkit default |
Quality gates
- Measurable: envelope
okpluscompile_errors == []; auditcounts.error == 0; NUnitfailed == 0andtotal > 0;budget_checkverdict pass (warn needs a reason);gc_checkin a development player;build_summaryresult Succeeded, size within budget; lock state recorded. - Visual: every capture passes
image_checks(noall_white,all_black,uniform,magenta) and was opened; before/after from the same bookmarks. - Habits (
references/routing-handoffs.mdsection 6): the samebuild_targeton every stage; p95 AND worst frame, before and after as two captures of about 200 frames (YOtDVv5-0A4 [00:17:00]); the verdict from a development player on the target device class (ZkvK0mX-id4 [00:10:27]); one development-build run before code is done (only those catch off-main-thread Unity calls); size on the shipped output; the scene list checked before each build; expected errorsLogAssert.Expected.
Common mistakes
| Mistake | What it looks like | Fix |
|---|---|---|
| batch job on a project the GUI editor holds | refused, or a corrupted Library | ut_live (the toolkit raises ProjectLockedError) |
-quit with -runTests | no XML, no verdict | ut_run.run_tests (never passes it) |
captures or bakes with -nographics | empty frames, "no graphics device", no lightmaps | graphics=True |
| switching platform or defines inside a job, or pinning the target only on the build | old target or defines; textures re-imported per stage | build_target= on every stage, or two invocations |
GetComponent<T>() ?? AddComponent<T>(), ?. on Unity objects | nothing added, MissingComponentException next (fake null, measured) | explicit == null or TryGetComponent |
AgentJob.List("k") ?? defaults | the defaults never apply (empty list, not null) | AgentJob.ListOrNull("k") ?? defaults, TryList, Has |
sizing a build from BuildReport totalSize | gigabytes: IL2CPP's do-not-ship folder counted | build_summary (output on disk) |
| trusting "Access token is unavailable" as a license failure | false alarm | the refusal line is "No valid Unity Editor license found" |
| hand-editing scene YAML while an editor is open | change invisible or lost | live job, then AgentBridge.SaveAll |
| killing Unity by name | other agents' editors die | stop by PID only (stop_headless, the grace timeout) |
Team routing and handoffs
Route by owner: scenario-unity-architecture (code, save data, input), scenario-unity-world-building (terrain, blockout, streaming), scenario-unity-rendering-lighting (pipeline, lighting, post), scenario-unity-shaders (HLSL, Shader Graph, renderer features), scenario-unity-vfx (VFX Graph, particles), scenario-unity-animation (Animator, Timeline, Cinemachine), scenario-unity-2d (sprites, tilemaps, 2D lights), scenario-unity-ui (uGUI, UI Toolkit), scenario-unity-gameplay (physics, AI, netcode, audio), scenario-unity-pipeline-automation (imports, Addressables, CI, builds), scenario-unity-performance (profiling, memory, batching), scenario-unity-mobile (Android, iOS, stores), scenario-unity-web (Web builds, servers, WebGPU). Full keyword table, persona chains and the handoff packet: references/routing-handoffs.md. Items that fell between two skills in blind grading (PSO warm-up, GPU Resident Drawer, VFX shaders, NavMesh through doors, foot sliding, uGUI over UI Toolkit, web fonts, stripping, Addressables duplication, static state) have ONE owner and a proof in its seams table: put both in the brief. Every packet carries project and channel, brief in numbers, what was done with its Unity calls, evidence (audit JSON, contact sheet with flags, CSV verdict, NUnit totals, build summary), Verified, Assumed, open issues, fence. The receiver reproduces one piece of evidence before building on it.
Unity 6.3 notes (full list: references/unity-6.3-traps.md)
- Render Graph only (Compatibility Mode removed; the define dies in 6.4);
_CLUSTER_LIGHT_LOOPreplaces_FORWARD_PLUS. FindObjectsByType<T>(FindObjectsSortMode.None);Rigidbody.linearVelocity/linearDamping;[SerializeField]only on fields;Lightmapping.TryGetLightingSettings(the getter throws when unassigned, observed).- Templates run the Input System only; entering Play mode reloads the domain (6.6 flips the default: reset statics in
SubsystemRegistration, unsubscribe onExitingPlayMode);-createProjectwithout a template is Built-in. Object.Destroyalready callsOnDisableon every level of the hierarchy on 6000.3.21f1 (observed; the deltas say direct children only before 6.4): unregister inOnDisableand, idempotently, inOnDestroy.- Build Profiles replace Build Settings; no public API creates a profile before 6.5;
IPreprocessBuildWithContextover...WithReport; anEditorUserBuildSettingschange on an active platform profile hits all platform profiles; profile texture compression lives inLibrary/. - Pin package versions: the manifest lists Cinemachine 2.10.7 yet a bare
Client.Addinstalled 3.1.7 (observed); IAP 4.15.1 and LevelPlay 8.10.1 lag the docs (IAP 5, LevelPlay 9). - Web: the URP template ships Brotli, not the Manual's gzip (observed); WebGPU experimental (opt-in production from 6.6). Built-in RP deprecated from 6.5.
macOS specifics
- Editor
/Applications/Unity/Hub/Editor/6000.3.21f1/Unity.app/Contents/MacOS/Unity; Hub modules (Web, iOS, Android) in.../6000.3.21f1/PlaybackEngines/; Xcode 26.6 meets the App Store rule. - Metal only; no CPU lightmapper on Apple Silicon (GPU lightmapper needs graphics); no GPU timings in the VFX Graph panel; GPU Frame Time reads 0 on most Editor frames: measure GPU in a player.
- Personal license: sign in to Unity Hub every 30 days (
ut_env.license_ok()); the toolkit uses a portable, checksum-verified CLI (references/live-channels.md). - Paths with spaces worked; quote every path. Never ship
<Product>_BurstDebugInformation_DoNotShip. Never kill an editor you did not start.
Shared toolkit (scripts/; exact API in references/toolkit-map.md)
import sys; sys.path.insert(0, "<scenario-unity-expert>/scripts").
ut_env(editor, license,base_project,install_agentkit, lock rule,preflight);ut_run(run_method(project, method, args=None, timeout=1800, graphics=False, quit=True, build_target=None, grace=None, result_grace=None),run_tests,build,scan_log);ut_live(bridge, headless editor, CLI, Pipelineeval);ut_review(image_checks,compare,contact_sheet,review_capture);ut_stat(budget_check,summarize_csv,compare_frames,build_summary).- C# AgentKit (
scripts/AgentKit/):AgentJob(List/Dictnever null;TryList,ListOrNullfor missing keys),AgentCapture(warms materials and 2D lights),AgentAudit,AgentBuild,AgentProfile,AgentBridge,AgentJson.
References
references/toolkit-map.md: the Unity call behind every toolkit function; the report format (Verified, Assumed).references/writing-jobs.md: how domain skills write AgentKit jobs (skeleton, rules, async jobs, findings).references/live-channels.md: bridge, headless editor, Unity CLI andcom.unity.pipeline, MCP, sandbox and Safe Mode.references/unity-6.3-traps.md: traps observed by the lead and the writers, documented 6.3 facts, deltas corrections, first-run checklist.references/routing-handoffs.md: routing, seams, persona chains, handoff packet, acceptance checks, verification habits.references/procedures.md: the lead's nine procedures, each with its live test and result.references/gui-paths.md: windows and menus for the same tasks.references/sources.md: every source with credentials, URLs, timestamps; revision history.
Signals
- GitHub stars
- 681
- Forks
- 82
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
scenario-unity-expert- Source
- github.com/scenario-labs/skills