# Unity-MCP Workflow Patterns Common workflows and patterns for effective Unity-MCP usage. ## Table of Contents - [Setup & Verification](#setup--verification) - [Scene Creation Workflows](#scene-creation-workflows) - [Script Development Workflows](#script-development-workflows) - [Asset Management Workflows](#asset-management-workflows) - [Testing Workflows](#testing-workflows) - [Debugging Workflows](#debugging-workflows) - [Batch Operations](#batch-operations) --- ## Setup & Verification ### Initial Connection Verification ```python # 1. Check editor state # Read mcpforunity://editor/state # 2. Verify ready_for_tools == true # If false, wait for recommended_retry_after_ms # 3. Check active scene # Read mcpforunity://editor/state → active_scene # 4. List available instances (multi-instance) # Read mcpforunity://instances ``` ### Before Any Operation ```python # Quick readiness check pattern: editor_state = read_resource("mcpforunity://editor/state") if not editor_state["ready_for_tools"]: # Check blocking_reasons # Wait recommended_retry_after_ms pass if editor_state["is_compiling"]: # Wait for compilation to complete pass ``` --- ## Scene Creation Workflows ### Create Complete Scene from Scratch ```python # 1. Create new scene manage_scene(action="create", name="GameLevel", path="Assets/Scenes/") # 2. Batch create environment objects batch_execute(commands=[ {"tool": "manage_gameobject", "params": { "action": "create", "name": "Ground", "primitive_type": "Plane", "position": [0, 0, 0], "scale": [10, 1, 10] }}, {"tool": "manage_gameobject", "params": { "action": "create", "name": "Light", "primitive_type": "Cube" }}, {"tool": "manage_gameobject", "params": { "action": "create", "name": "Player", "primitive_type": "Capsule", "position": [0, 1, 0] }} ]) # 3. Add light component (delete cube mesh, add light) manage_components(action="remove", target="Light", component_type="MeshRenderer") manage_components(action="remove", target="Light", component_type="MeshFilter") manage_components(action="remove", target="Light", component_type="BoxCollider") manage_components(action="add", target="Light", component_type="Light") manage_components(action="set_property", target="Light", component_type="Light", property="type", value="Directional") # 4. Set up camera manage_gameobject(action="modify", target="Main Camera", position=[0, 5, -10], rotation=[30, 0, 0]) # 5. Verify with screenshot manage_scene(action="screenshot") # 6. Save scene manage_scene(action="save") ``` ### Populate Scene with Grid of Objects ```python # Create 5x5 grid of cubes using batch commands = [] for x in range(5): for z in range(5): commands.append({ "tool": "manage_gameobject", "params": { "action": "create", "name": f"Cube_{x}_{z}", "primitive_type": "Cube", "position": [x * 2, 0, z * 2] } }) # Execute in batches of 25 batch_execute(commands=commands[:25], parallel=True) ``` ### Clone and Arrange Objects ```python # Find template object result = find_gameobjects(search_term="Template", search_method="by_name") template_id = result["ids"][0] # Duplicate in a line for i in range(10): manage_gameobject( action="duplicate", target=template_id, new_name=f"Instance_{i}", offset=[i * 2, 0, 0] ) ``` --- ## Script Development Workflows ### Create New Script and Attach ```python # 1. Create script create_script( path="Assets/Scripts/EnemyAI.cs", contents='''using UnityEngine; public class EnemyAI : MonoBehaviour { public float speed = 5f; public Transform target; void Update() { if (target != null) { Vector3 direction = (target.position - transform.position).normalized; transform.position += direction * speed * Time.deltaTime; } } }''' ) # 2. CRITICAL: Refresh and compile refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True) # 3. Check for errors console = read_console(types=["error"], count=10) if console["messages"]: # Handle compilation errors print("Compilation errors:", console["messages"]) else: # 4. Attach to GameObject manage_gameobject(action="modify", target="Enemy", components_to_add=["EnemyAI"]) # 5. Set component properties manage_components( action="set_property", target="Enemy", component_type="EnemyAI", properties={ "speed": 10.0 } ) ``` ### Edit Existing Script Safely ```python # 1. Get current SHA sha_info = get_sha(uri="mcpforunity://path/Assets/Scripts/PlayerController.cs") # 2. Find the method to edit matches = find_in_file( uri="mcpforunity://path/Assets/Scripts/PlayerController.cs", pattern="void Update\\(\\)" ) # 3. Apply structured edit script_apply_edits( name="PlayerController", path="Assets/Scripts", edits=[{ "op": "replace_method", "methodName": "Update", "replacement": '''void Update() { float h = Input.GetAxis("Horizontal"); float v = Input.GetAxis("Vertical"); transform.Translate(new Vector3(h, 0, v) * speed * Time.deltaTime); }''' }] ) # 4. Validate validate_script( uri="mcpforunity://path/Assets/Scripts/PlayerController.cs", level="standard" ) # 5. Refresh refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True) # 6. Check console read_console(types=["error"], count=10) ``` ### Add Method to Existing Class ```python script_apply_edits( name="GameManager", path="Assets/Scripts", edits=[ { "op": "insert_method", "afterMethod": "Start", "code": ''' public void ResetGame() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); }''' }, { "op": "anchor_insert", "anchor": "using UnityEngine;", "position": "after", "text": "\nusing UnityEngine.SceneManagement;" } ] ) ``` --- ## Asset Management Workflows ### Create and Apply Material ```python # 1. Create material manage_material( action="create", material_path="Assets/Materials/PlayerMaterial.mat", shader="Standard", properties={ "_Color": [0.2, 0.5, 1.0, 1.0], "_Metallic": 0.5, "_Glossiness": 0.8 } ) # 2. Assign to renderer manage_material( action="assign_material_to_renderer", target="Player", material_path="Assets/Materials/PlayerMaterial.mat", slot=0 ) # 3. Verify visually manage_scene(action="screenshot") ``` ### Create Procedural Texture ```python # 1. Create base texture manage_texture( action="create", path="Assets/Textures/Checkerboard.png", width=256, height=256, fill_color=[255, 255, 255, 255] ) # 2. Apply checkerboard pattern manage_texture( action="apply_pattern", path="Assets/Textures/Checkerboard.png", pattern="checkerboard", palette=[[0, 0, 0, 255], [255, 255, 255, 255]], pattern_size=32 ) # 3. Create material with texture manage_material( action="create", material_path="Assets/Materials/CheckerMaterial.mat", shader="Standard" ) # 4. Assign texture to material (via manage_material set_material_shader_property) ``` ### Organize Assets into Folders ```python # 1. Create folder structure batch_execute(commands=[ {"tool": "manage_asset", "params": {"action": "create_folder", "path": "Assets/Prefabs"}}, {"tool": "manage_asset", "params": {"action": "create_folder", "path": "Assets/Materials"}}, {"tool": "manage_asset", "params": {"action": "create_folder", "path": "Assets/Scripts"}}, {"tool": "manage_asset", "params": {"action": "create_folder", "path": "Assets/Textures"}} ]) # 2. Move existing assets manage_asset(action="move", path="Assets/MyMaterial.mat", destination="Assets/Materials/MyMaterial.mat") manage_asset(action="move", path="Assets/MyScript.cs", destination="Assets/Scripts/MyScript.cs") ``` ### Search and Process Assets ```python # Find all prefabs result = manage_asset( action="search", path="Assets", search_pattern="*.prefab", page_size=50, generate_preview=False ) # Process each prefab for asset in result["assets"]: prefab_path = asset["path"] # Get prefab info info = manage_prefabs(action="get_info", prefab_path=prefab_path) print(f"Prefab: {prefab_path}, Children: {info['childCount']}") ``` --- ## Testing Workflows ### Run Specific Tests ```python # 1. List available tests # Read mcpforunity://tests/EditMode # 2. Run specific tests result = run_tests( mode="EditMode", test_names=["MyTests.TestPlayerMovement", "MyTests.TestEnemySpawn"], include_failed_tests=True ) job_id = result["job_id"] # 3. Wait for results final_result = get_test_job( job_id=job_id, wait_timeout=60, include_failed_tests=True ) # 4. Check results if final_result["status"] == "complete": for test in final_result.get("failed_tests", []): print(f"FAILED: {test['name']}: {test['message']}") ``` ### Run Tests by Category ```python # Run all unit tests result = run_tests( mode="EditMode", category_names=["Unit"], include_failed_tests=True ) # Poll until complete while True: status = get_test_job(job_id=result["job_id"], wait_timeout=30) if status["status"] in ["complete", "failed"]: break ``` ### Test-Driven Development Pattern ```python # 1. Write test first create_script( path="Assets/Tests/Editor/PlayerTests.cs", contents='''using NUnit.Framework; using UnityEngine; public class PlayerTests { [Test] public void TestPlayerStartsAtOrigin() { var player = new GameObject("TestPlayer"); Assert.AreEqual(Vector3.zero, player.transform.position); Object.DestroyImmediate(player); } }''' ) # 2. Refresh refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True) # 3. Run test (expect pass for this simple test) result = run_tests(mode="EditMode", test_names=["PlayerTests.TestPlayerStartsAtOrigin"]) get_test_job(job_id=result["job_id"], wait_timeout=30) ``` --- ## Debugging Workflows ### Diagnose Compilation Errors ```python # 1. Check console for errors errors = read_console( types=["error"], count=20, include_stacktrace=True, format="detailed" ) # 2. For each error, find the file and line for error in errors["messages"]: # Parse error message for file:line info # Use find_in_file to locate the problematic code pass # 3. After fixing, refresh and check again refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True) read_console(types=["error"], count=10) ``` ### Investigate Missing References ```python # 1. Find the GameObject result = find_gameobjects(search_term="Player", search_method="by_name") # 2. Get all components # Read mcpforunity://scene/gameobject/{id}/components # 3. Check for null references in serialized fields # Look for fields with null/missing values # 4. Find the referenced object result = find_gameobjects(search_term="Target", search_method="by_name") # 5. Set the reference manage_components( action="set_property", target="Player", component_type="PlayerController", property="target", value={"instanceID": result["ids"][0]} # Reference by ID ) ``` ### Check Scene State ```python # 1. Get hierarchy hierarchy = manage_scene(action="get_hierarchy", page_size=100, include_transform=True) # 2. Find objects at unexpected positions for item in hierarchy["data"]["items"]: if item.get("transform", {}).get("position", [0,0,0])[1] < -100: print(f"Object {item['name']} fell through floor!") # 3. Visual verification manage_scene(action="screenshot") ``` --- ## Batch Operations ### Mass Property Update ```python # Find all enemies enemies = find_gameobjects(search_term="Enemy", search_method="by_tag") # Update health on all enemies commands = [] for enemy_id in enemies["ids"]: commands.append({ "tool": "manage_components", "params": { "action": "set_property", "target": enemy_id, "component_type": "EnemyHealth", "property": "maxHealth", "value": 100 } }) # Execute in batches for i in range(0, len(commands), 25): batch_execute(commands=commands[i:i+25], parallel=True) ``` ### Mass Object Creation with Variations ```python import random commands = [] for i in range(20): commands.append({ "tool": "manage_gameobject", "params": { "action": "create", "name": f"Tree_{i}", "primitive_type": "Capsule", "position": [random.uniform(-50, 50), 0, random.uniform(-50, 50)], "scale": [1, random.uniform(2, 5), 1] } }) batch_execute(commands=commands, parallel=True) ``` ### Cleanup Pattern ```python # Find all temporary objects temps = find_gameobjects(search_term="Temp_", search_method="by_name") # Delete in batch commands = [ {"tool": "manage_gameobject", "params": {"action": "delete", "target": id}} for id in temps["ids"] ] batch_execute(commands=commands, fail_fast=False) ``` --- ## Error Recovery Patterns ### Stale File Recovery ```python try: apply_text_edits(uri=script_uri, edits=[...], precondition_sha256=old_sha) except Exception as e: if "stale_file" in str(e): # Re-fetch SHA new_sha = get_sha(uri=script_uri) # Retry with new SHA apply_text_edits(uri=script_uri, edits=[...], precondition_sha256=new_sha["sha256"]) ``` ### Domain Reload Recovery ```python # After domain reload, connection may be lost # Wait and retry pattern: import time max_retries = 5 for attempt in range(max_retries): try: editor_state = read_resource("mcpforunity://editor/state") if editor_state["ready_for_tools"]: break except: time.sleep(2 ** attempt) # Exponential backoff ``` ### Compilation Block Recovery ```python # If tools fail due to compilation: # 1. Check console for errors errors = read_console(types=["error"], count=20) # 2. Fix the script errors # ... edit scripts ... # 3. Force refresh refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True) # 4. Verify clean console errors = read_console(types=["error"], count=5) if not errors["messages"]: # Safe to proceed with tools pass ```