ThreeKingdomsSlingshot/CLAUDE.md

92 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 삼국지 슬링샷 (가제) — 에이전트 작업 지침
> 너드나비스 조직 프로젝트 ⑤ (조직 레포 `.claude/skills/너드나비스-코어룰/rules/P38.md` §P38-3 ⑤). 새 세션은 조직 레포 `공유/인계서/삼국지슬링샷_인계서.md` → 이 파일 → `README.md` 순서로만 읽고 시작한다. 조직 규칙 본문 전수 읽기 금지(C10-1).
## 프로젝트 정보
- Unity **6000.3.23f1** · 2D · 빌트인 RP · 레거시 Input(`activeInputHandler: 0`) · Portrait · 패키지 = 기본 모듈 + `com.unity.ugui 2.0.0` + `com.unity.test-framework 1.6.0` + `com.unity.pipeline 0.10.0-exp.1`(2026-11-04 P38-7 적용).
- git = 이 폴더가 레포 루트 · `main` · 원격 `origin` = `https://burning.i234.me/NerdNavis_AiDev/ThreeKingdomsSlingshot.git`(비공개 · 2026-11-09 · 다른 PC 와 함께 작업 = 시작 전 `git pull` · 커밋 후 `git push`). 미추적 = `Library/` `Temp/` `Logs/` `UserSettings/` `obj/`(Unity 표준 `.gitignore` · Pipeline 포트 파일 `Library/Pipeline/.unity-pipeline-port` 도 그 안).
- 기록 = 조직 레포 `공유/대화로그/삼국지슬링샷/` · 인계서 `공유/인계서/삼국지슬링샷_인계서.md` · PD 지시 로그 #1077.
## 에디터 제어 = Unity 공식 CLI + `com.unity.pipeline` (P38-7)
기본 호출은 MCP 가 아니라 **셸에서 `unity …` 직접 호출**이다(Unity 공식 권장 · 토큰 적음). `unity` 명령은 PowerShell 로 호출하되, C# 코드에 큰따옴표가 들어가는 `eval` 류는 Bash 도구로(K-DEV-026). 에디터가 둘 이상 열려 있으면 `--project-path E:\Nerdnavis\ThreeKingdomsSlingshot` 또는 환경 변수 `UNITY_PROJECT_PATH` 로 대상을 지정한다(K-DEV-024).
### 에디터가 열려 있을 때 (PD 플레이 중 반복 수정)
```
unity status --project-path . # ready 확인(포트·PID)
unity recompile --project-path . # 수정 후 재컴파일 · 컴파일 오류 0 이어야 통과
unity command --project-path . # 등록된 명령 목록(콘솔 읽기 · 메뉴 실행 등)
unity command --project-path . <명령> [인자] # 명령 실행 · --result-only 로 JSON 만
unity command --project-path . test run --mode playmode --async-tests --timeout 600 --result-only # 열린 에디터 안에서 테스트 시작(비동기 필수 · Play 진입의 도메인 리로드가 동기 요청을 끊음)
unity command --project-path . test status --result-only # 끝날 때까지 10초마다 폴링 · test list / cancel 도 있음
# ※ `unity test <proj> --mode PlayMode` 는 자체 배치 인스턴스를 띄우므로 에디터가 열려 있으면 거부된다(2026-11-04 실측 「이미 열려 있습니다」)
unity close . [--force] # 에디터 종료(저장 없이 종료 · PD 가 연 에디터는 닫지 않는다)
```
- **작업 전에 `unity command --project-path . editor status --result-only` 로 `playMode` 를 본다.** **PD 가 Play 중(`playing`·`paused`)이어도 기다리지 말고 `unity command --project-path . editor.playmode stop` 으로 Play 를 중단 → `stopped` 확인 → 바로 작업**한다(PD 지시 2026-11-04 · 2026-11-09 재확인 「내 PC 가 플레이 중이더라도 중단하고 작업해」 · 에디터 자체는 닫지 않음). 수정을 **모두** 끝낸 뒤 리컴파일 1회(편집 중 자동 새로고침으로 잠깐 컴파일 오류가 보일 수 있음). 열린 에디터가 있으면 배치 실행은 `Temp/UnityLockfile` 로 막히고, PD 가 연 에디터는 닫지 않는다 → 검증이 급하면 `Assets`·`Packages`·`ProjectSettings` 만 복사한 사본 폴더(예 `E:\Nerdnavis\_tks_verify`)에서 배치 실행(2026-11-04 실측 · 새 Library 임포트 포함 수 분).
- 수치 자산에 새 필드를 넣었으면(예 `art` · `archerRangeRatio`) 기존 `BalanceConfig.asset` 에는 비어 있다 → 자산이 git 에서 미수정이면 메뉴 `TKS/Reset Balance Defaults`(= `BuildFresh`)로 재시드 · PD 가 Inspector 로 손본 흔적(git diff)이 있으면 재시드 금지 · 필드만 보충.
- 판정 = 컴파일 오류 0 + 콘솔 error 0(Unity 자체 `SearchDatabase` 인덱싱 예외는 무해).
### 에디터가 닫혀 있을 때 (배치 · 재현 가능한 실측)
```
Unity.exe -batchmode -nographics -projectPath <proj> -executeMethod TKS.EditorTools.ProjectSetup.Build -quit -logFile <log> # 컴파일 + 설정 자산·씬 생성(BuildFresh = 기본값 재시드)
Unity.exe -batchmode -nographics -projectPath <proj> -runTests -testPlatform PlayMode -testResults <xml> -logFile <log> # 플레이모드 테스트(현재 17종)
Unity.exe -projectPath <proj> -executeMethod TKS.EditorTools.ProjectSetup.AutoPlayCapture -autoplayDir <dir> -autoplayTurns 3 # GUI 자동 플레이 캡처 · 스스로 종료
```
- `Unity.exe` = `C:\Program Files\Unity\Hub\Editor\6000.3.23f1\Editor\Unity.exe`. 에디터가 같은 프로젝트를 열고 있으면 배치 실행은 못 한다(`Temp/UnityLockfile`) — 먼저 `unity status` 로 확인.
- 배치 로그 판정 = `error CS` 0 · 테스트 XML `failed="0"`.
- **에디터가 열리면서 크래시하면 oCam(화면 녹화)을 의심**: `%LOCALAPPDATA%\Unity\Editor\Editor.log` 꼬리의 스택에 `HookSurfaceDLL` → `D3D12SwapChain::Present` 가 있으면 oCam 의 D3D12 훅이 원인(2026-11-04 15:04 × 2 실측). 이 프로젝트는 그래서 Windows Graphics API 를 **D3D11 우선**으로 고정했다(`ProjectSettings.asset` `m_BuildTargetGraphicsAPIs` · 에디터도 이 설정을 따름). 그래도 크래시하면 `-force-d3d11` 인수 또는 oCam 종료. 배치 `-nographics` 는 영향 없음.
## 코드 규칙
- 전투 수치 SOT = `Assets/Resources/BalanceConfig.asset`(Inspector 조정 · 최초 시드만 `Assets/Scripts/Editor/BalanceDefaults.cs`). 코드·테스트에 수치를 직접 적지 않는다(C45).
- **아웃게임 SOT = `Assets/Data/OutGame/*.csv`(12장 · 기획 `공유/기획팀_산출물/삼국지슬링샷/tables/` 사본 · utf-8-sig · 2026-11-05 #1093 A)** — 영웅·병사(`heroes.csv` · 구 `BalanceConfig.heroes[]` 대체) · 레벨·승급·진화·장비·뽑기·보상·재화·신규 유저 초기값(`economy.csv` start_*). 고친 뒤 메뉴 `TKS/Import OutGame Data (아웃게임 데이터 임포트)`(`Editor/DataImporter` · 오류 시 자산 불변) → `Resources/OutGame/` 9 자산. 표를 바꾸면 기획 `tables/` 도 같이.
- **스킬(2026-11-05 #1093 B)** — SOT = `Assets/Data/OutGame/skills.csv`(effect_key `;` 복수 · p1~p3 · lv_scale · needs_impl) + `skill_levels.csv` → 임포트 자산 `SkillInfoDb`(effect 열 포함 · 표시 + 전투 공용). 처리기 = `Runtime/Skills/`: `SkillKeys`(51종 · 레벨 계수 방식 · 팀 키 · 표에 없는 키 = 임포트 오류) · `SkillSystem`(유닛별 쿨·예약 · 즉시형 시한 효과 · 행동 훅 · 호위 피해 분담 · HUD 한 줄) · `SkillMods`(전투 코드가 수치 질의) · `SkillRules`(상한 = 등급 + ★3 · ★5 패시브 배율 = evolve.csv skill_unlock · 레벨업 · 스킬서 판매). 전투 코드에 스킬 수치를 직접 쓰지 않고 `SkillMods` 로 읽는다. 테스트 = `Tests/PlayMode/SkillTests`. 캡처 = `-executeMethod TKS.EditorTools.DataImporter.CaptureSkills -skillDir <dir>`.
- **아웃게임 2차(2026-11-05~06 #1093 C)** — 표 10장 추가(`training` · `training_ranks` · `patrol` · `dev_tree` · `treasures` · `treasure_sets` · `chests`(+ iron/buy 열) · `tower` · `dungeons`(+ reward_amount) · `arena`(+ 상점 키)) + `economy.csv` C 키 24 → 임포트 자산 18(A 9 + C 9 · `OutGameTables.C.cs` 검증). 코드 = `OutGame/Domestic/`(`TrainingRules` 훈련 · `PatrolRules` 순찰 · `DevRules` 개발 효과 키 → 적용 지점 · `DomesticScreen`) · `OutGame/Treasure/`(`TreasureRules` 보유·세트·강화·조건 카운터·상자 · `TreasureScreen`) · `OutGame/Arena/`(`ArenaRules` 탑·던전·투기장·적장 · `ArenaScreen`). 스탯 = §3-2 × (1 + 훈련(병종·항목)) × (1 + 보물(항목)) = `HeroRules.Compute(…, save)` · 보물 crit/range/splash = `BattleManager.SideEffectsProvider` → `SkillMods.Get` 진영 효과(`side_crit_pct` · `range_mult` · `splash_radius_mult`) · 조건 효과 카운터 = `Runtime/BattleEvents.Raise`(전투 코드 1줄씩 · 아군만) · 연무장 전투 = `BattleManager.StartMode(BattleMode)`(적 편성 · 제한 턴 · 결과 탭 = `onExit`) → `OutGameSession.OnModeEnd`. 테스트 = `Tests/PlayMode/OutGameCTests`. 캡처 = `-executeMethod TKS.EditorTools.DataImporter.CaptureC -cDir <dir>`(사본 · 10장).
- **영웅 56명(PD #1096 · 2026-11-06)** — heroes 71행(영웅 56 + 병사 15) · skills 112 · hero_gear 1344 · 신규 effect_key 4(`team_dmg_pct_turn` · `cooldown_add` · `charm` · `heal_self_pct`) + 기존 키 조건 열(받는 피해 p2 = HP 임계/2 보스/3 근접 · p3 = 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) = `SkillMods`·`SkillSystem` · 대장군 `pushed_ally_attack_on_push` 는 표에서 미사용(처리기 유지).
- **지형 기믹 레이어(PD #1097 · 2026-11-06 세션 D)** — SOT = `Assets/Data/Gimmicks/{gimmicks,stage_gimmicks,tutorial_tips}.csv`(기획 `tables/` 사본 · utf-8-sig) → 메뉴 `TKS/Import Gimmicks (기믹 임포트)`(`Editor/GimmickImporter` · 오류 시 자산 불변 · `ai_weights.csv` 도 함께) → `Resources/Gimmicks/GimmickDb`·`StageGimmickTable` · `Resources/AI/AiWeights`. 전투 시작 = `GimmickLayer.Build(stage)`(장-번호 배치 · 배경 위 · 유닛 아래 · 장애물 = 콜라이더 + `GimmickBody` · 지대 = 모양 스윕 · 강 = 늪처럼 통과하는 지대(강 안 ×p3 · 다리 구간 = 효과 없음 · 강가·다리에서 밀려 강물에 들면 빠짐 · PD 2026-11-06 개정) · 겹친 유닛은 빈 자리로). 전투 코드는 질의만: `Combat.FreeTravel`(장애물 = 벽 · 숨은 함정 정지) · `FirstUnitOnArrow`(pass/block/destroy) · `Combat.Damage`(군기 공 · 고지 근접 피격 · 안개 원거리 피격 ×p1 · 책사 광역이 그 반경 안개를 걷음) · `SkillMods.Range`(고지 사거리) · `BattleManager` 턴 시작 `TurnStart`(군기·중독·불·행동 불가). 충돌 반응·폭발·굴림·미끄러짐·지대 = `Gimmicks/ActionResolver.Gimmicks.cs`(partial). 배치 도구 = `TKS/Gimmick Painter (기믹 배치)`. 테스트 = `Tests/PlayMode/GimmickTests` · 캡처 = `-executeMethod TKS.EditorTools.GimmickImporter.Capture -gimmickDir <dir>`(사본).
- **병종 규칙 개정 + 예측 AI(PD #1098 · 2026-11-06)** — 보병 = 적과 싸운 뒤 튕겨나는 길의 다른 적과 연쇄 교환 1회(`ActionOutcome.Chains`) · 창병 = 상대만 밀리고 자신은 × `spearRecoilRatio` 반대로(밀며 전진 폐기). 적 턴 = `Runtime/AI/TacticalAI`(후보 방위 × 파워 3 × 액티브 → `Sim` 가상 실행(연출 없음 · 분산 0 · 상태 복원) → `ai_weights.csv` 점수 · 서포터 → 돌격 → 사격 · 1기마다 재평가) · 가중치 자산이 없으면 `EnemyAI.Decide`(기존 조준) · 적 액티브 = 1부 보스만(`boss_active`). 가상 실행 중(`Sim.Active`)에는 `Fx`·`UnitView`·`BattleEvents`·HUD 갱신이 꺼진다 — 새 연출 코드도 `Sim.Active` 면 건너뛸 것. 조준 미리보기도 같은 가상 실행(`GimmickPreview` · 숨은 함정 모름). 테스트 = `Tests/PlayMode/TacticalAITests`.
- 아웃게임 코드 = `Assets/Scripts/OutGame/**`(asmref → TKS.Runtime) — `OutGameSession`(표 + 세이브 · 바꾸는 동작은 즉시 저장) · 규칙 `HeroRules`·`EconomyRules`·`GachaRules` · 화면 `UI/`(로비·영웅·편성·거래·결과 패널 · `OutGameRoot` 가 전환). 세이브 = `persistentDataPath/save.json`(테스트·검증 캡처는 `SaveStore.UseMemory` / SessionState `TKS_SAVE_MEMORY` 로 메모리 전용). 출전 = `BattleManager.LineupProvider`(편성 프리셋 → `playerSpawnPoints` 자리 0~2 전열 · 3~4 후열). 화면 캡처 = `Unity.exe -projectPath <사본> -executeMethod TKS.EditorTools.DataImporter.CaptureOutGame -outgameDir <dir>`. **UI 수정 검증은 고친 화면만 찍는다**(PD 2026-11-09 「매번 모든 화면 체크 = 시간·토큰 낭비」): `Tools/mirror_verify.py` → `Unity.exe -projectPath E:/Nerdnavis/_tks_verify -force-d3d11 -executeMethod TKS.EditorTools.DataImporter.CaptureUI -uiDir <dir> -uiOnly hero_list,hero_detail*`(이름 = 정확히 또는 「접두*」 · 다 찍으면 바로 종료 · 1장 ≈ 15초 · 전체 21장 ≈ 3분 · 이름 목록 = `UICaptureDriver` 의 Shot 이름).
- **챕터·스테이지 SOT = `Assets/Data/Stages/*.csv`(utf-8-sig · 2026-11-05 145 스테이지 · 기획서 #1086 §4 전사)** — `stages.csv`(chapter·stage·title·type normal|elite|boss·place·faction·병종 4 수·generals `id:병종:true|false`·bg_theme·note) · `chapters.csv`(장 제목·연도·**월드맵 맵 id**·AI 출발값) · `generals.csv`(무장 id → 이름·배율 덮어쓰기) · `factions.csv`(세력 코드 → 이름·병사 아트 접두). 고친 뒤 메뉴 `TKS/Import Stages (스테이지 임포트)`(`StageImporter` · 형식 오류면 자산 미변경) → `BalanceConfig.chapters·stages`. 적 좌표 = `BalanceConfig.formation` 규칙(전위·중군·후군·장수 줄 · `StageTable.Enemies`) · 장수 배율 = `generals.csv` 가 비면 `boss/eliteGeneral*Mult`. 월드맵 노드 = 스테이지 표(챕터별 수 · 보스 = type boss) · 노드 자리 = `ChapterMap/Data/<맵>.json` `slots_N`(없으면 길 위 균등).
- 규칙 해석 변경 = `Assets/Scripts/Runtime/ActionResolver.cs`(병종 4종) · `BattleManager.cs`(턴·승패·창병 대기) · `Combat.cs`(피해 공식·경로 질의). 변경 시 `Assets/Tests/PlayMode/BattleFlowTests.cs` 에 테스트 추가 후 22종 이상 전부 통과 확인.
- 콜라이더 질의 전 `Physics2D.SyncTransforms()` · 유닛 제거는 `SetActive(false)` 후 `Destroy` · 자식 렌더러가 있는 표시물은 GameObject 통째로 끈다.
- **궁병 규칙(PD 2026-11-04 · #1080)**: 사거리 = `slingMaxDistance`(얇은 원). 조준 방향 사거리 안의 적(가장자리 간격 기준) = **제자리 사격** · 사거리 밖 ~ ×(1 + `archerRangeRatio`)(붉은 도넛) 안의 적 = 들어올 만큼만(최대 `ArcherMoveAllow` = 슬링 × 비율) 다가가 사격 · 더 멀면 **목표점까지 이동만**(#1079). 반격 없음 · 목표점 명중 크리. **근접 전투**(돌진·반격·관통·견제·밀려 닿은 공격)에서 궁병은 근접 유닛 상대 주는 피해 × `archerMeleeDealtRatio`(0.5) · 받는 피해 × `archerMeleeTakenRatio`(1.5) — `Combat.Damage(…, melee)` · 원거리 사격은 패널티 없음.
- **충돌 규칙(PD 2026-11-04 · #1080)** — `ActionResolver`: 보병 = 경로 첫 유닛(아군·적군)에 부딪히면 **알까기**(등속 접근 → 부딪힌 순간 피해·반격을 즉시 교환(`ImpactExchange`) → **양쪽이 동시에 튕김**: 상대는 접촉 방향으로 남은 거리 × `collisionPushRatio` · 자신은 반대 방향으로 남은 거리 × `collisionSelfBounceRatio` · PD #1081) · 기병 = 유닛 무시 관통(적 피해 · 아군은 지나칠 거리가 안 나오면 **그 아군 직전 정지** `StopBeforeUnclearedAllies`) · 창병 = 첫 유닛을 **밀며 함께 전진**(접촉 간격 유지 · 앞 유닛이 막히면 둘 다 정지 · 적이면 피해 · 아군은 밀기만) · **규칙 ⑤** = 밀린 같은 진영 유닛이 상대와 닿으면(`Combat.TouchingOpponent` · 간격 ≤ 0.12) 그 자리에서 근접 공격 1회(조작마다 · 자기 행동 소모 없음 · `ActionOutcome.Notes`). 적 AI 도 같은 코드(대칭). `UnitStats.attackRange` 는 창병 견제 반경만.
- **사거리 표시(PD 2026-11-04)**: 유닛 탭(아군·적군) → `RangeIndicator`(슬링샷 최대 거리 = 얇은 반투명 원 · 궁병 = 슬링샷 범위 ~ ×(1+비율) 채워진 붉은 도넛 · 범위 안 적이 없으면 회색).
- 보고는 실측·추정·미확인을 분리한다(C23 · C5). 커밋은 의도 변경 파일만.
## 스킬 이펙트 작업 (2026-11-09 PD 「WL 에서 스킬을 구현하던 방식과 동일하게 유닛별 스킬 구현」 · 이 PC = 연출 담당)
- **스킬 테스트 씬** = `Assets/Scenes/SkillTest.unity`(메뉴 `TKS/Skill Test/Build Scene` 로 다시 만듦 · 씬 = `SkillTestBootstrap` 하나) — 전투 코드 그대로 + 월드맵·아웃게임·세이브 없음 · 왼쪽 위 「유닛 선택」(heroes.csv 영웅 56 · 진영별) · 「이펙트 보기」(에디터 전용 · `Assets/VFX` 프리팹 검색 → 고른 영웅 몸에 띄움 · 크기 ×0.25~4 · 기울기 0/−45/−90 · 방향 · 경로 복사) · 왼쪽 아래 「다시 배치」(표 다시 읽기 + 처음 자리) · 쿨 무시 · 더미 행동 · 영웅 무적. 더미 = 죽지 않음(`Unit.Invincible` · 아군 턴마다 체력 회복) · 배치 표 `Resources/Skills/skill_test.csv`(hero 행 n_slot + 상대 좌표) · 더미 행동 끔 = `BattleManager.EnemyHold`. 패널 위 누름은 슬링샷이 무시(`SlingshotInput.BlockPress`).
- **이펙트 값 = 표 `Assets/Resources/Skills/skill_vfx.csv`**(머리 주석이 칸 설명) → `Runtime/Skills/SkillVfx`(전투마다 다시 읽음). 키 = 스킬 id(액티브) 또는 `basic_<병종>`(일반 슬링샷 공격 · 이동만인 추가 이동 제외) · 이벤트 = cast(발동) · travel(슬링샷 돌진 동안 · `f_every` 반복) · hit(그 행동의 피해 순간 · 대상마다 1번). 훅 = `SkillSystem.Announce`(cast) · `BeginAction`/`EndAction`(travel) · `Hit`(hit) · 즉시형 `Instant`. 크기 `f_scale` = 유닛 키 대비(WL 배율 ÷ 1.8 ≈ 같은 비율) · 입자 크기 모드는 띄울 때 전부 Hierarchy · 정렬 = 12 + n_order · 가상 실행(Sim) 중 없음.
- **이펙트 사본 규칙(WL `WL_Fx_*` 와 같음)**: 게임이 읽는 것 = `Assets/Resources/Fx/TKS_Fx_<이름>.prefab` 사본만(메뉴 `TKS/VFX/Make Skill Fx Copy` · 고른 프리팹 → 사본) · 원본 팩 `Assets/VFX/**` 는 고치지 않는다.
- **WL 리소스 옮기기** = `Tools/wl_vfx_import.py`(scan · copy · unpack · `--wl <WL 루트>` · GUID 유지 · URP 전용 셰이더 재질 프리팹은 자동 제외 · WL 원본 무수정) → 에디터 새로고침 → 메뉴 `TKS/VFX/Check Imported VFX`(분홍 재질 · 빠진 스크립트). 판정 표 = `Assets/VFX/_wl_import_report.csv`. 2026-11-09 옮긴 것 = VFX_Klaus(Hyper Casual 1·2 · Shoot&Hit 1·2 · Earth Skills · Element Splash 3) · Lana Casual RPG VFX · Slash Effects FREE · EricWang 4묶음 중 빌트인 통과분 · MasterStylizedProjectiles 통과분 · Kyeoms 폭발 · GAP 툰 투사체(빌트인 패키지 unpack) · WL 창 돌진 사본 3(`Assets/VFX/WL/`) = 약 640 MB.
- **URP 셰이더 이식판** = `Assets/VFX/_BuiltinPorts/*.shader`(원본과 같은 GUID · 셰이더 이름 · 속성 → 옮겨 온 재질이 그대로 사용 · 현재 Eric AdditiveFlow · AlphaBlendFlow · MasterStylizedFX EmissiveParticle · CommonMesh2Side). 새로 필요한 URP 셰이더는 같은 방식으로 이식하면 `wl_vfx_import.py` 가 통과(port)로 본다.
- **카메라 깊이 텍스처 켬**(`GameBootstrap` · `SkillTestBootstrap` `depthTextureMode |= Depth`): Hyper Casual 등 셰이더 그래프의 소프트 파티클이 `_CameraDepthTexture` 를 읽음 — 없으면 이펙트가 안 보이거나 불투명 하늘색 덩어리(2026-11-09 실측). 셰이더 그래프 재질은 `com.unity.shadergraph 17.3.0`(Built-In 타깃) 필요.
## 유닛 아트 반영 (다른 세션의 원화·애니 → 이 게임)
- 아트 위치 = `Assets/Resources/Units/<art>/<clip>/f000.png …` + `meta.json`(fps · ppu · pivot(발) · charHeightPx · clips). `<art>` 는 `BalanceConfig.asset` 의 `heroes[].art` / `stages[].enemies[].art`(비면 도형). 아군 = `liubei`·`guanyu`·`zhangfei`·`zhaoyun`·`huangzhong`(2026-11-04 재디자인 원화 · 스켈레탈 B단계 전까지 front_idle 정지 1장 · 납품 후 리그 v2 12 클립으로 교체) · 적 = `<세력>_<병종>`(`hgj_infantry` …) · 보스 = `<세력>_boss_<로마자>`(`hgj_boss_zhangjiao` …).
- 스켈레탈 리그 → 프레임: 조직 레포 스킬 `skeletal-anim-pipeline/files/scripts/rig_frames_export.py rig <리그 패키지> --parts <작업>/parts --out Assets/Resources/Units/<art> --clips back_idle,back_ready,back_action,back_damage,back_stop,front_idle,front_ready,front_action,front_damage,front_stop,front_victory,front_failed --px 180 --step 2`(ComfyUI venv python · 실측 482×470 · 15 fps · 3인 11 MB).
- 정지 그림(적 SD 전신 `_rgba.png`) → `rig_frames_export.py static <png> --out Assets/Resources/Units/<art>`(front_idle 1장 · 발 = 불투명 상자 아래). 원화 세션 납품 = `C:\ArtRnD\samguk_game\manifest.json` · 파일명 `<art>_rgba.png` → `<art>` 폴더(2026-11-04 1차 `hgj_*` 5종 + 2차 `dz_infantry·spear·cavalry·archer` 반영 완료 · 여포 `dz_boss_lvbu`·`cc_*`·`wei_*` 는 납품 시 같은 명령).
- 전장 배경 = `Assets/Resources/Board/<boardArt>.png`(자산 `boardArt` · 5:8 불투명 · PPU 100 = 전장 20×32) → `Board.Build` 가 있으면 바닥으로 쓰고 격자·진영 띠는 생략. 임포트 규칙(스프라이트 · 4096 · 압축)은 같은 `UnitArtImporter` 의 `Board/` 분기 · 재임포트 메뉴가 두 폴더 처리.
- 임포트 설정은 `Editor/UnitArtImporter.cs`(AssetPostprocessor · meta 의 pivot·ppu) 가 자동 적용 · 프레임을 넣거나 meta 를 바꾼 뒤 = 메뉴 `TKS/Reimport Unit Art`(Build/BuildFresh 가 함께 호출).
- 재생 = `UnitArt.cs`(Resources 로더 · 캐시) + `FrameAnimator`(대기 loop · 준비 = 조준 중 · 공격·피격 = 1프레임 자세 유지 후 대기 · 승리 = 끝 프레임 유지). **포즈 규칙(PD #1084 · 2026-11-05)**: 공격 애니 대신 1프레임 포즈 전환 — 조준(당기는) 중 = `ready`(출발 자세) · 슬링샷 이동(돌진) 중 = `action`(병종 돌진 포즈 · `UnitView.SetCharging` · `ActionResolver.Resolve` 가 루틴 전후로 켜고 끔) · 멈추면 `idle`. 돌진 중 피격은 색만 바뀜. **4방향 보기(PD #1082)**: `UnitView.SetFacing(dir)` — 마지막 이동 방향의 세로 성분으로 `back_*`(위 ↖↗) / `front_*`(아래 ↙↘) 세트를, 가로 성분으로 `flipX`(왼쪽 = 반전 · 오른쪽 = 기본)를 정한다. 시작 = 아군 ↗ · 적군 ↘. `ActionResolver.Face()` 가 이동·튕김·밀림·돌진·사격·밀린 아군 공격 때 호출(기병 복귀는 방향 유지). 없는 클립은 다른 보기 → 대기로 대체. 세로 크기 = `unitArtHeight`(자산) × 반지름 배율 · 발 = 원 중심 아래 0.55 R.
## 구조 (README 와 동일)
- `Assets/Scripts/Runtime/` — `GameBootstrap`(씬 구성) · `BattleManager` · `ActionResolver` · `Combat` · `SlingshotInput`(조작·스크롤·줌) · `RangeIndicator`(사거리 원·궁병 도넛) · `CameraController`(넓은 전장 카메라) · `EnemyAI`(교전 범위) · `BattleHUD`(HUD·미니맵) · `Unit`/`UnitView` · `UnitArt`(아트 로더·`FrameAnimator`) · `Board` · `Fx` · `AutoPlayDriver`
- `Assets/Scripts/Editor/` — `ProjectSetup`(CLI 진입점·씬 생성) · `BalanceDefaults`(최초 시드) · `StageImporter`(스테이지 표 → 자산) · `UnitArtImporter`(아트 임포트 설정) · `DataImporter`(아웃게임 표 → 자산 · 화면 캡처)
- `Assets/Scripts/OutGame/` — 아웃게임(세이브·로비·영웅·편성·거래·보상 · 내정 `Domestic/` · 보물 `Treasure/` · 연무장 `Arena/` · 위 「코드 규칙」)
- `Assets/Data/Stages/` — 챕터·스테이지 표 CSV 4장(위 「코드 규칙」) · 해석·배치 = `Runtime/StageTable`
- `Assets/Resources/Units/<art>/` — 유닛 아트 프레임 + meta.json(위 절)
- `Assets/Scripts/Runtime/Skills/` — 스킬 처리기(위 「코드 규칙」 스킬)
- `Assets/Scripts/Runtime/Gimmicks/` — 지형 기믹 레이어(위 「코드 규칙」) · `Assets/Scripts/Runtime/AI/` — 예측 시뮬레이션 적 AI(`Sim` · `TacticalAI` · `AiWeights`)
- `Assets/Data/Gimmicks/` · `Assets/Data/AI/` — 기믹 표 3장 · AI 가중치 표
- `Assets/Tests/PlayMode/` — `BattleFlowTests` · `OutGameTests` · `SkillTests` · `OutGameCTests` · `GimmickTests` · `TacticalAITests`
- `.claude/skills/` — Unity CLI 에이전트 스킬(`unity skill install claude-code --local` 산출물 · 재설치는 `unity skill refresh`)