13 KiB
13 KiB
WL 프로젝트 (What's your level?) — Unity 에디터 제어 가이드
Unity 6000.3.23f1 · URP 17.3 · 3D RPG. 조직 규칙·세션 절차의 SOT 는 NerdNavisAi 레포의 profile-wl 스킬이고,
이 파일은 이 프로젝트에서 에이전트가 Unity 에디터를 다루는 방법만 다룬다.
1. 에디터 제어 = Unity 공식 CLI + Pipeline 패키지 (커뮤니티 unity-mcp 사용 금지)
- 2026-09-03 부로 WL 은 CoplayDev
unity-mcp브릿지(Assets/unity-mcp/)를 제거하고 Unity CLI(unity) +com.unity.pipeline로 전환했다.mcp__unity-mcp__*도구는 이 프로젝트에서 호출하지 않는다 (.claude/settings.jsondeny). - 에디터가 켜져 있으면 Pipeline 서버가 자동 기동한다 (포트 7800~7849 · 설정 에셋
Assets/Settings/Pipeline/EditorPipelineManager.asset). - 호출 방식 2가지 — ①이 기본 (공식 권장 · MCP 왕복보다 빠르고 토큰이 적다):
unity command <이름> [--인자 값]을 Bash/PowerShell 도구로 직접 실행 (unity cmd동일)- MCP 서버
unity(=unity mcp, Claude 데스크톱 전역 등록) — 셸을 못 쓰는 클라이언트용 보조 경로. 🔴claude_desktop_config.json은 앱이 켜진 채 외부 편집하면 앱의 환경설정 저장 시 덮어써져 유실된다 (2026-09-03 실증 · 등록 후 사라짐). 앱 재시작 후unity mcp configure --list로claude ✓ configured를 확인하고, 없으면unity mcp configure claude --yes를 다시 실행한다. 앱 실행 중에는 그 파일을 손대지 않는다.
unity를 찾지 못하면(PATH 갱신 전에 켜진 앱) 전체 경로%LOCALAPPDATA%\Unity\bin\unity.exe로 호출한다.- Windows 경로 인자가 들어가는 명령은 PowerShell 로 실행한다 (Git Bash 는
D:\…를 현재 폴더와 섞어 버린다). 인자 없는 짧은 명령은 Bash 도 무방. - 명령 목록·인자 스키마 =
unity command(이름 없이) 또는unity command --query wl_. 정확한 문법은unity <명령> --help로 재확인 (CLI 는 베타). - 인자 안에 큰따옴표가 들어가는 C# (
eval --code "new GameObject(\"X\")"류)은 PowerShell 5.1 이 따옴표를 벗겨 깨진다 →.cs파일로 두고unity command eval_file --file <경로>를 쓰거나,python Tools/Pipeline/pipeline_client.py eval "<코드>"(구조화 JSON 전송 · 따옴표 안전) 를 쓴다.
1-1. 사용 규칙·함정 (2026-09-04 #738 — 타 조직 문서 검토 후 채택분 + 이 프로젝트 실측분 · 첫 CLI 조작 전 1회 Read)
- "Unity CLI" = 켜져 있는 에디터에 붙는
unity command …(com.unity.pipeline) 이다.-batchmode/-quit/unity run으로 새 Unity 프로세스를 띄우는 것이 아니다 — 그쪽은 CI 빌드 전용. eval/eval_file코드에는using을 쓸 수 없다(메서드 본문처럼 감싸여 실행) → 네임스페이스를 전부 풀어 쓴다(UnityEngine.·UnityEditor.·System.Collections.Generic.은 이미 들어와 있어 생략 가능하나, 안전하게 풀어 쓴다). 여러 줄 C# 도eval_file로 충분하다(이 프로젝트 실측 수십 회) ·run_script는 진입점(정적 메서드)이 있는 파일용.- 첫 Roslyn 컴파일은 느리다 → 기본 5초를 넘기면
--timeout 60을 준다(격자 스캔 등 무거운 eval 도 동일). - PowerShell → CLI 인자: 공백·따옴표가 든 문자열(경로·JSON)은 깨질 수 있다. 오브젝트 지정은 경로 문자열 대신
find_gameobjects로 얻은 숫자instanceId를 쓴다..ps1안에 한글 정규식·문자열을 두면 PowerShell 5.1 이 ANSI 로 읽어 깨진다 → 스크립트 안 문자열은 ASCII 로(한글은.cseval 파일에만). - PowerShell 함수 이름
R금지(Invoke-History별칭 충돌) · 리컴파일 직후Connection reset by server는 도메인 리로드 중 정상 → 폴링에서 ERR 문자열을 종료 조건으로 쓰지 말 것. - Play 중
recompile은 멈춘 것처럼 보인다(수 분간triggered) →editor_status가playing이면editor_stop(PD 승인) 후 실행. 새.cs파일 복사도 Play 중엔 하지 않는다(포커스 시 자동 리컴파일이 PD 테스트를 끊는다) — 스크래치에 스테이징 후 정지 뒤 복사. - 구조화 명령 한계:
remove_component/add_component가 "Could not resolve component type" 을 내면 재시도 대신eval_file로 우회 ·save_prefab_contents는 이름·활성 토글만(계층 구성은eval_file) ·apply_prefab_overrides --instance가 "not part of a prefab instance" 를 내면PrefabUtility.ApplyPrefabInstance(root, InteractionMode.AutomatedAction)로. - 프리팹 편집 정석 =
PrefabUtility.LoadPrefabContents→ 수정 →SaveAsPrefabAsset→UnloadPrefabContents(예외 시 저장 안 됨 = 반쯤 망가지지 않음). 씬 인스턴스를 프리팹에 반영 =IsPartOfPrefabInstance확인 후ApplyPrefabInstance. 씬 오브젝트를 새 프리팹으로 =SaveAsPrefabAssetAndConnect. - 리플렉션으로
[SerializeField] List<T>채우기 = 필드의 정확한 제네릭 타입(typeof(List<>).MakeGenericType(concrete)+Activator.CreateInstance)으로 만든다 —List<Component>같은 부정확한 타입은 런타임 에러. - 캡처:
capture_game_view --source screen은 Play 중만(오버레이 UI 포함) ·save_path는 항상Assets/아래에 저장되므로 캡처 후 프로젝트 루트Screenshots_WL/로 옮기고Assets/Screenshots_WL+.meta를 지운다(에셋 오염 방지). - 디바이스 시뮬레이터(게임 뷰 1080×2280 등)에서는
Pointer.current가 시뮬레이터 터치스크린이라 런타임simulate_pointer(마우스)가 무시된다 → 터치는InputSystem.QueueStateEvent(Touchscreen.current, new TouchState{…})로 주입(단, Began 만 보내면 곧 사라지므로 Moved 를 이어서). - SerializeField 이름 변경:
FormerlySerializedAs를 그대로 쓴다(씬·프리팹·브랜치 전반에 안전). CLIset_serialized_field재배선은 소수 오브젝트를 즉석에서 고칠 때의 보조 수단.
2. 세션 시작 체크 (순서대로)
unity status # WL 에디터가 ready 로 보이는지 (포트·PID)
unity command editor_status # playing / compiling / blocked_by_dialog 확인
unity command set_autotick --enable true # 포커스 없어도 컴파일·테스트가 진행되게 (필수)
editor_status가playing이면 PD 가 직접 테스트 중일 수 있다 → 리컴파일·Play 전환·씬 저장을 하지 말고 먼저 PD 에게 확인한다.blocked_by_dialog면 모달 다이얼로그가 메인 스레드를 막고 있다 → CLI 로는 못 닫는다. 제목·메시지를 PD 에게 보고.- 에디터가 여러 개면
--project-path D:\NerdNavis\IdleRPG\WLevelRPG로 대상을 고정한다. - 동시에 두 자동화 채널(다른 세션·사람)이 같은 에디터를 조작하지 않는다.
3. 코드 수정 → 검증 루프
unity command recompile
unity command recompile_status # completed | up_to_date 까지 반복 (failed 면 errors 배열 확인)
unity command get_console_logs --severity error --limit 20
unity command run_tests --mode editor --filter <TestClass>
- 컴파일 판정 =
recompile_status가 completed/up_to_date 이고 콘솔 error 0 (조직 C39 정합). - 도메인 리로드 중 연결 오류는 정상 — 잠시 후 재시도. 종료 코드 2 = 인자 오류(명령줄 수정), 6 = 에디터 쪽 실패(같은 명령 재시도 무의미).
4. 에디터 재실행 없이 런타임 변경 (PD 요구 핵심)
| 방법 | 언제 | 명령 |
|---|---|---|
| eval | 한 줄짜리 즉석 조작 (Play 중 값 확인·변경) | unity command eval --code "return UnityEngine.Time.timeScale;" |
| run_script | 여러 줄 · 대량 생성 · 반복 실행 — 도메인 리로드 없이 인메모리 컴파일(~2s) | AgentScripts/Build.cs 에 public static class Build { public static object Run() { … } } → unity command run_script --file AgentScripts/Build.cs --entry Build.Run |
| Hot Reload (파일) | Play 중 메서드 본문을 고쳐 즉시 반영 | 대상 메서드에 [HotReload] (public · 반환 void/IEnumerator) → 본문 수정 → unity command reload_file --filename Assets/WL/Scripts/….cs |
| Hot Reload (감시) | 저장만 하면 자동 반영 | 에디터 Window ▸ Pipeline ▸ Settings… ▸ Hot Reload Interpreter Watch ▸ Start Watching (감시 중엔 자동 리프레시가 꺼진다 · 끝나면 Stop) |
| WL 커스텀 명령 | 프로젝트 전용 동작 | unity command wl_info 등 (§6) |
[HotReload]=using Unity.Pipeline.HotReload;· public 메서드 · void/IEnumerator 만 · 본문이 만지는 멤버도 public 만 (private setter 프로퍼티·private 필드를 쓰면 리로드 컴파일 실패 — 2026-09-03 실측). 상태 재초기화는[OnHotReload]콜백. 예시 =Assets/WL/Scripts/Debug/WLHotReloadProbe.cs(실검증 2026-09-03: Play 중 본문 수정 →reload_file→ 다음 틱부터 새 문구, 도메인 리로드 0회).- Play 중에는
create_gameobject·add_component가 거부된다("cannot be used during play mode") → 임시 오브젝트는eval로 만든다:unity command eval --code "new UnityEngine.GameObject(\"Probe\").AddComponent<WL.Diagnostics.WLHotReloadProbe>(); return 1;"(Play 종료 시 사라짐). - 코드는 파일에 두고
eval_file(즉석 실행) 또는run_script(정적 진입점) 로 실행한다. 여러 줄 C# 을eval --code문자열로 밀어 넣지 않는다(§1-1). - 런타임 서버 (Play 모드·개발 빌드 · 포트 7900~7949 · 2026-09-03 실검증: 에디터 Play 진입 시 7900 자동 기동,
set_timescale·console·runtime_status동작) = Project Settings ▸ Pipeline ▸ Runtime ▸ Enable In Builds ON (ProjectSettings/Packages/com.unity.pipeline/RuntimePipelineConfig.json). 에디터 Play 진입 시 프로젝트 루트에.unity-pipeline-runtime-port가 생기며unity command --runtime-path D:\NerdNavis\IdleRPG\WLevelRPG runtime_status처럼 런타임 명령(set_timescale·simulate_key·simulate_pointer·log·console…)을 보낸다. - Android/IL2CPP 빌드에는 런타임 HTTP 서버가 컴파일되지 않는다 (Standalone 개발 빌드 만). 디바이스 핫리로드는
reload_file_player_interpreter(프로파일러 연결 경유).
5. Play 제어·관찰
unity command editor_play # / editor_pause / editor_stop
unity command capture_game_view --save_path Screenshots_WL/<이름>.png --source screen # Play 중 오버레이 UI 포함
unity command get_scene_hierarchy
unity command find_gameobjects --type WL.Player.PlayerController
unity command get_serialized_fields --target <핸들> --format value
6. WL 커스텀 명령 (Assets/WL/Scripts/Debug/WLPipelineCommands.cs)
| 명령 | 설명 |
|---|---|
wl_info |
씬 · Play 상태 · timeScale · FPS · 플레이어(이름/위치/HP/상태) · 적 수(생존/전체) |
wl_timescale --scale 0.2 |
에디터 서버에서 Time.timeScale 즉시 변경 (런타임 서버 없이도 Play 중 동작) |
wl_teleport_player --x 0 --y 1 --z 0 |
현재 플레이어를 월드 좌표로 이동 (CharacterController 안전 처리 · Play 중만) |
- 새 명령 =
static메서드에[CliCommand("wl_이름", "설명")]+ 인자에[CliArg("이름", "설명")]. 리컴파일 후unity command목록에 자동 등장 (등록 코드 없음). - 파괴적 명령은
confirm/dry_run인자를 두고 게이트한다. 데이터 값은 코드에 상수로 두지 말고 설정 에셋을 읽는다 (C45).
7. 커스텀 인터페이스 (HTTP + JSON)
- 서버 API =
http://127.0.0.1:<port>/api/{status, commands, exec, progress, dialog, job}· 헤더Authorization: Bearer <evalToken>. 포트·토큰 =Library/Pipeline/.unity-pipeline-port(런타임 =.unity-pipeline-runtime-port). 로컬 전용 · Origin 헤더가 있는 브라우저 직접 요청은 거부. POST /api/exec본문 ={"commandLine": "wl_timescale --scale 0.5"}또는{"command": "eval", "parameters": {"code": "…"}}→ 응답{"success": true, "result": …}.- 준비된 클라이언트 =
Tools/Pipeline/pipeline_client.py(표준 라이브러리만 · import 가능한PipelineClient+ CLI).python Tools/Pipeline/pipeline_client.py serve→http://127.0.0.1:7700브라우저 제어판(control_panel.html) — 프록시가 토큰을 주입하므로 CORS·토큰 입력이 없다. - 브라우저 페이지가 서버에 직접 붙어야 한다면
EditorPipelineManager.asset의 Allow Browser Clients 를 켜야 한다 (프록시 방식이면 불필요).
8. 커밋·기록
- 커밋은 PD 확인 후 파일 지정으로만 (조직 규칙).
Library/·Tools/Pipeline/__pycache__/·.unity-pipeline-runtime-port는 미추적. - 작업 결과·교훈은 NerdNavisAi 레포
공유/대화로그/WL프로젝트/와profile-wl교훈 인덱스에 남긴다.