| name | unity-editor |
| description | Remote control Unity Editor via CLI using unityctl. Activate when user mentions Unity Editor, play mode, asset compilation, Unity console logs, C# script debugging, Unity tests, scene loading, screenshots, or video recording. Use for launching/stopping editor, entering/exiting play mode, compiling scripts, viewing logs, loading scenes, running tests, capturing screenshots, recording video, or executing arbitrary C# in Unity context. |
unityctl - Unity Editor Remote Control
Control a running Unity Editor from the command line without batch mode.
Setup (Required First)
Run unityctl status first to check what's already running. If Unity is already connected, skip straight to commands.
unityctl status
unityctl bridge start
unityctl editor run
unityctl wait
Commands
unityctl status
unityctl bridge start/stop
unityctl editor run/stop
unityctl asset refresh
unityctl play enter/exit
unityctl play pause
unityctl play step
unityctl logs
unityctl logs -n 50
unityctl logs --stack
unityctl logs --full
unityctl scene list
unityctl scene load <path>
unityctl scene load <path> --additive
unityctl test run
unityctl test run --mode playmode
unityctl screenshot capture
unityctl screenshot list-windows
unityctl screenshot window <window>
unityctl screenshot window SceneView out.png
unityctl record start
unityctl record start --duration 10
unityctl record start --frames 300
unityctl record stop
unityctl snapshot
unityctl snapshot --depth 4
unityctl snapshot --id 14200 --components
unityctl snapshot --id 14200 --filter "type:Rigidbody"
unityctl snapshot --screen
unityctl snapshot --filter "type:Rigidbody"
unityctl snapshot --scene Assets/Scenes/Other.unity
unityctl snapshot --prefab Assets/Prefabs/Player.prefab
unityctl snapshot query 400 300
unityctl ui click --name "StartButton"
unityctl ui click --id 14200
unityctl ui click 400 300
unityctl prefab open Assets/Prefabs/Player.prefab
unityctl prefab open Assets/Prefabs/Player.prefab --context 14200
unityctl prefab close
unityctl prefab close --save
unityctl prefab close --discard
unityctl dialog list
unityctl dialog dismiss
unityctl dialog dismiss --button "OK"
Verifying Changes
Pick the cheapest observation that answers the question — screenshots are expensive (consume context, hard to diff across iterations) and imprecise (pixel details are unreliable). Prefer structured tools:
| What you need to verify | Tool |
|---|
| Scene hierarchy, components, properties | snapshot (with --components, --filter) |
| UI layout, visibility, screen positions | snapshot --screen |
| Runtime behavior, errors, warnings | logs |
| Specific value or state | script eval (query it directly) |
| Test correctness | test run |
| Visual appearance (art, shaders, layout polish) | screenshot capture (only when visuals are the point) |
Rule of thumb: if you can express the expected result as a value or property, verify with snapshot, logs, or script eval — not a screenshot.
When a screenshot is the right tool, crop it to the relevant region before reading it — a focused crop produces far better results than a full-screen capture and uses less context.
Scene Observation & Manipulation Workflow
Use snapshot to observe, ui click to interact, eval --id for custom actions, then snapshot to verify.
UI elements auto-show text content, interactable state, and RectTransform layout.
interactable means the element has pointer event handlers (Button, Toggle, Slider, or custom IPointerClickHandler/IPointerDownHandler). For standard Selectables it reflects Selectable.interactable; custom pointer handlers always report as interactable.
Use --screen to add screen-space bounds and visibility for UI elements. Hittability (blocked-by detection) is only available in play mode.
Use snapshot query <x> <y> to identify what UI element is at a screen coordinate. Response includes a mode field: play (accurate) or edit-approximate (hit ordering may be imprecise).
Use ui click --name <name> to find and click a UI element by name in one call (play mode). Uses GameObject.Find — supports both simple names ("StartButton") and hierarchy paths ("/Canvas/Panel/StartButton"). Prefer --name over --id since instance IDs are not stable across play mode transitions. Reports if the element is blocked by another.
unityctl snapshot --screen
unityctl ui click --name "StartButton"
unityctl snapshot
unityctl snapshot query 400 300
unityctl ui click 400 300
Multiple targets with --id (uses targets[] array):
unityctl script eval --id 14200,14210 'targets[0].transform.SetParent(targets[1].transform); return "done";'
Script Execution
Evaluate C# expressions directly (common usings like UnityEngine, UnityEditor, System auto-included; Object is aliased to UnityEngine.Object):
unityctl script eval 'Application.version'
unityctl script eval 'GameObject.FindObjectsOfType<Camera>().Length'
unityctl script eval --id -1290 'target.transform.position'
unityctl script eval -u UnityEngine.SceneManagement 'SceneManager.GetActiveScene().name'
unityctl script eval -u UnityEngine.UI,UnityEngine.SceneManagement 'SceneManager.GetActiveScene().name'
Pass arguments to the script with --:
unityctl script eval 'args[0]' -- hello
Async / waiting
Eval is async by default — use await directly. System.Threading.Tasks is in default usings:
unityctl script eval 'await Task.Delay(500); return GameObject.Find("Boss") != null;'
Task<T> returns are unwrapped — return Task.FromResult(x) gives you x, not the envelope.
Full Script Execution
For complex scripts with custom classes, multiple methods, or logic beyond a single expression. Use the Write tool to create a .cs file, then execute it. Define a class with a static Main() method — sync, async Task<T>, or async Task:
using UnityEngine;
public class Script
{
public static object Main()
{
var player = GameObject.Find("Player");
return player?.transform.position.ToString() ?? "not found";
}
}
unityctl script execute /tmp/MyScript.cs
unityctl script execute /tmp/SpawnObjects.cs -- Cube 5 'My Object'
Use Main(string[] args) to accept arguments passed after --.
Important: Always use the Write tool to create the .cs file rather than shell heredocs (cat << 'EOF'), which break on single quotes in C# code.
Use -t <seconds> on script eval/script execute for long-running operations (default 30s):
unityctl script eval -t 600 -u UnityEditor 'return BuildPipeline.BuildPlayer(opts).summary.result.ToString();'
Unknown type or member names
Compile errors include a Hint: line when the type or member exists somewhere loaded — read it and fix. Fallbacks: unityctl script lookup-type <Name> and unityctl script members <Type> [--filter X] [--static].
Typical Workflow
unityctl asset refresh
unityctl snapshot
unityctl play enter
unityctl snapshot
unityctl logs
unityctl play exit
Troubleshooting
Run unityctl status first to diagnose issues.
| Problem | Solution |
|---|
| Bridge not responding | unityctl bridge stop && unityctl bridge start |
| Editor not connected | Normal - exponential backoff, up to 15 seconds |
| Connection lost after compile | Normal - domain reload, auto-reconnects |
| "Project not found" | unityctl setup or unityctl config set project-path <path> |
| Can't tell when Unity is ready | unityctl wait --timeout 300 |
| Command timed out | A native dialog may be blocking Unity: unityctl dialog list |
| Progress bar stuck | Check with unityctl dialog list, wait or dismiss |
| Editor not found | Use --unity-path to specify Unity executable |