Drive an already-open Unity editor from the command line so an AI coding agent (or you) can compile, enter/exit Play mode, screenshot the Game view, and set the time scale - with no port, no custom build, and no clicking around in the editor.
It works by dropping one small editor script (CompileWatcher.cs) into your project. That script watches a file under Temp/; thin shell scripts write a request and poll for the result. No networking, no auth.
When you iterate on a Unity game with an AI agent, the slow part is the human round-trip: switch to Unity, wait for the compile, click Play, watch, click Stop. These tools let the agent do all of that from the shell - and read a screenshot back - so it can verify its own changes without you in the loop.
unity-compile.sh- recompile and printOKor the list of C# errors (exits non-zero on errors). Compile-time errors only, not runtime bugs.unity-play.sh/unity-play.sh stop- compile and enter Play mode if clean / exit Play mode.unity-shot.sh- capture the Game view toTemp/shot.png(read it as an image).unity-speed.sh N- setTime.timeScale(fast-forward or slow the running game; clamped to [0.1, 20]).unity-click.sh Btn_Name- click a named UI Button in the running game (dispatched through the EventSystem - no screen coordinates, no window focus). A wrong name errors with the list of every Button in the scene. Requirescom.unity.ugui(present in default templates).
Driving the editor from the shell lets an agent verify its own work. Spend the least effort that still catches the bug - climb only when the cheaper layer can't see it:
- Pure logic, no editor (cheapest). Pull game math/decisions into plain, deterministic C# (pass
dt/time in; noTime.*or transforms) and cover it with Unity Test Framework EditMode tests. Fastest, runs without Play mode;unity-compile.shat least confirms it compiles. - Play mode + game state (mid). Enter Play (
unity-play.sh), fast-forward withunity-speed.sh, and assert on extracted state - positions, HP, counts - instead of pixels. - Screenshots (most expensive).
unity-shot.shto look at the screen. Reserve for layout and visual effects - anything spatial that state can't capture.
These tools cover layer 3 (and the Play/speed plumbing for layer 2); layers 1-2 are mostly a matter of how you structure your own code.
unity-speed.sh (fast-forward, or 0.1 for a near-pause, to reach a state quickly), unity-click.sh (press buttons by name), and unity-shot.sh (observe) let an agent reach and inspect most states without touching the editor. What you cannot do headlessly is send keystrokes, drags, or clicks at arbitrary positions - there's no input injection.
To exercise one of those interactions (a drag handler, a key handler), temporarily wire an auto-trigger in code: call the handler from a timed hook or a game event (e.g. "2s after game over, call Retry()"), verify the result with a screenshot, then remove the hook.
- Unity with the editor open on your project.
- Two one-time editor steps (the installer reminds you):
- Click the editor window once after install so it loads
CompileWatcher.cs. - Settings > General > Interaction Mode = "No Throttling", so the editor keeps ticking while unfocused. Without this the scripts time out when Unity isn't focused.
- ("Run In Background" is enabled for you by the watcher, so the game keeps simulating and rendering for screenshots while unfocused.)
- Click the editor window once after install so it loads
/plugin marketplace add ykdojo/unity-agent-tools
/plugin install unity-agent-tools@ykdojo
Then run the unity-setup skill, which finds your Unity project, runs the installer, and walks you through the one-time steps. Afterwards the unity-run skill compiles, plays, and screenshots the game for you.
bash install.sh /path/to/your/UnityProject
Copies CompileWatcher.cs into Assets/Editor/ and the shell scripts into ./tools/ (pass a second argument to put them elsewhere). With no project argument it walks up from the current directory looking for ProjectSettings/ProjectVersion.txt.
bash tools/unity-compile.sh # error-check only
bash tools/unity-play.sh # compile, then enter Play if clean
bash tools/unity-speed.sh 5 # 5x fast-forward
bash tools/unity-shot.sh # screenshot Game view -> Temp/shot.png
bash tools/unity-speed.sh 1 # back to normal
bash tools/unity-click.sh Btn_Retry # click a named UI Button
bash tools/unity-play.sh stop # exit PlayThe scripts auto-detect the project when run from inside it. From elsewhere, set UNITY_PROJECT=/path/to/UnityProject.
CompileWatcher.cs loads via [InitializeOnLoad] and watches Temp/compile-request. Each shell script writes a verb to that file and polls Temp/compile-result.txt (or Temp/shot.png):
| File content | Action |
|---|---|
| a timestamp | recompile, report OK / CS errors |
play |
recompile, then enter Play mode if clean |
stop |
exit Play mode |
replay |
exit Play (if running), recompile, re-enter Play |
shot |
capture the Game view to Temp/shot.png |
speed:N |
set Time.timeScale to N |
click:Name |
click the named UI Button (Play mode; active + interactable only) |
Entering Play triggers a domain reload that wipes the watcher's static state, so the "enter Play after a clean compile" intent is stashed in SessionState.
- Don't fire two commands within ~1-2s of entering Play. The domain reload re-arms the file watcher, so a command sent in that window can be dropped.
- Runtime-init settings only apply on a fresh Play start, not a recompile-and-continue. Stop and start Play to pick them up.
Run the offline smoke test any time with bash test.sh.
Verified against a live editor on an existing project:
install.shfile copying and project auto-detection- compile (clean and error-reporting), play, refusing to play when there are compile errors, stop
- screenshot (a real frame captured while Unity was unfocused) and time scale
- named-button clicks (
unity-click.sh): full start → pause → resume → exit flows, plus both error paths (unknown name lists the scene's buttons; inactive button refused) CompileWatcher.cscompiles cleanly in a real editor
Not yet verified (help welcome):
- A clean-room install into a brand-new Unity project - first-load behavior and the
runInBackgroundauto-enable on a project where it was off. - The Claude Code plugin distribution flow end to end (marketplace add -> install -> skills load ->
${CLAUDE_PLUGIN_ROOT}resolves). - Only tested on macOS so far; the scripts are plain Bash and should work on Linux, but that is unconfirmed.