feat(BT5-Dev Hero1): 캐릭터 교체 + 캐릭터 리소스 규칙 SOT 신설 (C49 1~3단계 완결)

PD 결정 (2026-05-07): PlayerTestGirl 폐기 → Hero1 정식 캐릭터 교체. 모션 8종 (attack·combatidle·idle·hit·jump·resurrection·run·death) + 상태 전환 룰 (idle⇄combatidle 5초 타이머·hit 인터럽트 보호·death→resurrection 시퀀스). 정합성 이슈 5건 A안 일괄 채택.

C49 표준 프로세스:
- Phase 1 (개발팀장 Opus 설계) — 06_Hero1_적용_설계.md 768라인 14섹션 + 기각안 6건
- Phase 2 (클라이언트팀 Sonnet 적용) — W1~W12 일괄. Unity 영역 변경 10건 + 신설 9건. C6-1 백업 9종 (timestamp 20260507_1253)
- Phase 3 (개발팀장 Opus 검증) — 10영역 §A~§J 통과 (Critical 0·Major 1·차단 0). Major 1 = Health.cs Animator parameter Console warning 가능성 (게임 영향 0)

산출물 (BT 레포):
- 공유/PD_지시_트래킹/개발팀_PD_지시_로그.md (BT5-Dev 5단계 갱신)
- 프로젝트/EerieVillage/개발/spec/캐릭터_리소스_규칙_v1.md (영구 SOT 신설)
- 프로젝트/EerieVillage/개발/06_Hero1_적용_설계.md (Phase 1 설계)
- 공유/대화로그/EerieVillage/2026-05-07.md (엔트리 1·2·3)

Unity 영역 (E:/EerieVillage 별도 git): 8 State Animator + 8 Parameter + AnimationClip 8종 + 신규 컴포넌트 2종 + Health.cs 부활 + 19 EditMode tests

매니페스트 archived 자동 이동 4건: 123431·124022·125257·131323

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
깃 관리자 2026-05-07 13:16:09 +09:00
parent c88d41381b
commit 13976e2857
4 changed files with 1240 additions and 1 deletions

View File

@ -35,7 +35,7 @@ C3·C13 위반에 해당. **즉시 자진 보고 후 소급 등록**.
|---|------|----------|----------|-----------|----------|----------|
| BT12-Dev | 2026-04-24 23:00 | **스킬 시스템 설계 (C43 "개발팀" 호칭 직접 수령 + C49 시범 적용)** — PD 직접 지시 "개발팀은 기획서를 토대로 스킬 시스템 설계 진행". 기획서 v0.2 (`프로젝트/EerieVillage/기획/content/02_스킬_효과_컨셉.md` 액티브 6카테고리·패시브 5카테고리·각성 4패턴) + CSV v0.3 60종 (`프로젝트/EerieVillage/기획/content/02_스킬_효과_컨셉_v0.3.csv` UTF-8 BOM) 토대. C49 표준 프로세스 시범 적용 (개발팀장 Opus 설계 → 클라이언트팀 Sonnet 구현 → 개발팀장 검증) | **보류** | **[Phase 1 완료 2026-04-24]** 개발팀장 Opus 직접 설계 완결 — `프로젝트/EerieVillage/개발/spec/스킬_시스템_설계_v1.md` (1074 라인, 14 섹션). §1 아키텍처 4계층 · §2 인터페이스 4종(`ISkillRuntime`·`IActiveSkill`·`IPassiveSkill`·`IAwakeningSkill`) + ScriptableObject 3종(`ActiveSkillData`·`PassiveSkillData`·`AwakeningSkillData`) + `PlayerSkillInventory`·`PlayerStats` · §3 CSV→ScriptableObject→Runtime→Health.Decrement 데이터 흐름 + 카테고리 문자열 매핑 · §4 VS 순수형 자동 발동 사이클 (OnTime·OnHit·OnKill + `ActiveSkillRuntime.Tick(deltaTime)` 독립 Cooldown) · §5 `AwakeningManager` 3 조건 동시 충족 + 4 패턴 Dispatcher + 다중 각성 선택 UI · §6 카테고리 매핑 6+5+4 (B는 BT7-Dev `AttackHitbox` 재활용 · 나머지 5 효과 발동기 신설) · §7 Phase 2-A~E 작업 단위 분해 (스크립트 25개·테스트 10건·asset 60개) · §10 BT7-Dev 통합 영역 (Health·AttackHitbox·PlayerAttackTicker·PlayerController 완전 보존 · `Health.OnDamagedEvent` 확장 필요 명시) · §11 기각안 5건 + 대화로그 추가 2건 (총 7건 C32 초과). 대화로그 `공유/대화로그/EerieVillage/2026-04-24.md` `[BT12-Dev Phase 1 완료] 개발팀장 스킬 시스템 설계 v1 (1074 라인)` 엔트리 완결. **C48 3자문 전수 통과**로 Phase 2 클라이언트팀 Sonnet Task는 본 Task에서 호출하지 않고 **PM 차원 별도 위임** 권고 (C48·C49·C50 정합) | **기획서 확정 대기** (PD 2026-04-25 직접 지시 — "기획서 확정되기 전까지 작업 대기") | **재개 트리거**: 기획팀 v0.3 또는 v1.0 확정 + balance-designer 60종 수치 확정 + narrative-designer 카드명 세계관 재매핑 결정 → C50 Phase 2 사전 승인 옵션(a/b/c/d) PD 결정 → 분할 시 Phase 2-A~E 순차 진행 (인터페이스·SO → 중앙 컴포넌트 → 효과 발동기 → 60장 .asset → EditMode 테스트) → Phase 3 개발팀장 검증 → 완료 아카이브. **선행 차단 블로커**: `paths.local.json.UNITY_PROJECT_ROOT: __SET_PER_PC__` 미설정 — 재개 시 PD PC 경로 설정 필요. Phase 1 산출물 1074 라인 설계 문서는 보존 |
| BT7-Dev | 2026-04-24 | **BT7-Plan 개발 집행 Phase 1** — VS 순수형 자동 발동(공격 버튼 제거, PlayerAttackTicker 주기 타이머) + 하트 분할 시스템(Health.maxHearts·QuartersPerHeart·IncreaseMaxHearts·Heal·Decrement(int)) + EditMode 테스트 갱신(10→13) + AttackHitbox 쿼터 단위 피해 통합 + TODO 주석(EnemyController.attackDamage balance v0.2 대기) | **진행중** | Unity 편집 6파일 · BT 산출물 2파일 · 백업 6종. **Unity 외부 레포 `D:/NerdNavis/EerieVillage/`**: `Assets/Scripts/Gameplay/{PlayerAttack.cs(개정), PlayerAttackTicker.cs(신설)+.meta}`·`Assets/Scripts/Mechanics/{Health.cs(전면개정), PlayerController.cs(Attack입력제거·Facing public), AttackHitbox.cs(Decrement(damage)통합)}`·`Assets/Settings/InputSystem_Actions.inputactions(Attack액션·바인딩 완전 제거)`·`Assets/Tests/Editor/PlayerAttackTests.cs(10→13)`. **BT 레포**: `프로젝트/EerieVillage/개발/06_BT7-Plan_VS순수형_재구조.md`(신설) · `공유/개발팀_백업/EerieVillage/*.bak_20260424_1551.*`(6종). PD 수동 검증 2종(Asset import+EditMode Runner 실행·Player.prefab에 PlayerAttackTicker 부착+Play 검증) | — | **PM 수행 대기**: pm-auditor 감사 → BT worktree commit → Unity 외부 레포는 PD Editor 실행 시 GitAutoSync 자동 push → Play 검증 결과(13 tests green + 자동 공격 발동 확인) 수령 → balance/01 v0.2 확정 후 후속 Phase(EnemyController·적 ATK 테이블·HUD 하트 UI) 분리 착수 → 완료 아카이브 이동 |
| BT5-Dev | 2026-04-23 | **EerieVillage Phase 3 파일럿 (개발)** — ①Unity git 초기화·자동 sync ②캐릭터 교체·이동·공격·i-frame ③템플릿 분석 ④**PlayerTestGirl 아틀라스 최종 집행** (2026-04-24 추가) | **진행중** | [1·3단계 완료] `9f689c1` 원격 push · `scripts/unity_auto_sync.sh` · `01~03_*.md` 분석 3종 [2단계 v0.2 완료] Player·Enemy prefab 편집 + 8 EditMode tests [**3단계(PlayerTestGirl 아틀라스) 완료 2026-04-24 0003**] PD `PlayerTestGirl.png` 직접 저장 (1536×1024, 4x2 8 sprite, walk 4+attack 4 프레임 신규 캐릭터) → B안 채택 (PlayerIdle·PlayerRun m_PPtrCurves 교체 + PlayerAttack.anim 신설). **meta 재생성** (9→8 sprite, 384×512 grid, guid 유지) · **Idle·Run.anim 전면 재작성** (4 sprite loop) · **PlayerAttack.anim + meta 신설** (guid `c8d7e5a1...`) · **Player.controller 확장** (attack Trigger·Player-Attack State·AnyState/Exit Transition 3종) · **Player.prefab m_Sprite 교체** · **EditMode 테스트 +2** (총 10). C6-1 백업 5종 `bak_20260424_0003`. PD 수동 1건 (Play 검증). `feedback_pm_image_verification_skip.md` 신설 (`tier: constitutional`) + `feedback_pm_dev_task_delegation_failure.md` 3회차 append. 재발 방지 5종 체크리스트 전부 이행 (시각 확인·전수 ls·기존 meta 재활용·guid 충돌 검증·백업 5종) | `프로젝트/EerieVillage/개발/05_PlayerTestGirl_아틀라스_적용.md` · `프로젝트/EerieVillage/개발/04_BT5-Dev_2단계_구현보고.md` v0.2 · Unity `Assets/Character/{Sprites/PlayerTestGirl.png.meta, Animations/{PlayerIdle,PlayerRun,PlayerAttack,Player.controller}}` · `Assets/Prefabs/Player.prefab` · `Assets/Tests/Editor/PlayerAttackTests.cs` · `memory/org/feedback_pm_image_verification_skip.md` (신설) · `memory/org/feedback_pm_dev_task_delegation_failure.md` (3회차) · `공유/대화로그/EerieVillage/2026-04-23.md` `[캐릭터리소스교체최종]` 엔트리 | — | **PM 수행 대기**: pm-auditor 감사 → BT worktree commit → push → Unity 프로젝트는 PD Editor 실행 시 GitAutoSync 자동 push → Play 검증 결과(10 tests green + walk/attack 애니메이션 재생) 수령 → 완료 아카이브 이동 |
| BT5-Dev | 2026-04-23 (2026-05-07 신규 지시 추가) | **EerieVillage Phase 3 파일럿 (개발)** — ①Unity git 초기화·자동 sync ②캐릭터 교체·이동·공격·i-frame ③템플릿 분석 ④~~PlayerTestGirl 아틀라스~~ (2026-05-07 폐기) ⑤**Hero1 정식 캐릭터 교체** (2026-05-07 신규) — `E:/EerieVillage/Assets/Character/Sprites/Hero1/` 37 PNG 자산 적용 + 캐릭터 리소스 규칙 SOT 신설 (모션 8종 + 상태 전환 룰: idle⇄combatidle 5초 타이머·hit 인터럽트 보호·jump·resurrection 시퀀스·death) | **진행중** | [1·3단계 완료] `9f689c1` 원격 push · `scripts/unity_auto_sync.sh` · `01~03_*.md` 분석 3종 [2단계 v0.2 완료] Player·Enemy prefab + 8 EditMode tests [**3단계 PlayerTestGirl 폐기 결정 2026-05-07**] PD 지시로 Hero1 정식 자산 교체. PlayerTestGirl 자산 5종(meta·Idle.anim·Run.anim·Attack.anim·controller·prefab)은 Hero1 기반 재작성 + 신규 자산 추가 (PlayerCombatIdle·PlayerJump·PlayerHit·PlayerResurrection·PlayerDeath.anim). 기존 Player*.png 9종은 `_archive/` 백업 + Hero1 단독 사용. Player.controller 전면 재설계 + Health.cs hit 인터럽트 보호 + 부활 시스템 추가. [**5단계 Hero1 적용 진행중 2026-05-07**] PD 결정 5건 A안 일괄 채택 (`combat idle`→`combatidle` rename·death 모션 8종 확장·jump 1프레임 정합·기존 9종 archive 백업·Land/Spawn/Victory 미사용). 본 PM 매니페스트 등록(`2026-05-07_123431`) + pm-auditor 사전 감사 통과(Critical 0·Major 0·Minor 1·Improvement 2). 본 세션 = `strange-turing-c6797e` worktree 자동 재생성 — 본 작업 후 §6-A 절차 인계 영역 | `프로젝트/EerieVillage/개발/05_PlayerTestGirl_아틀라스_적용.md` (폐기 표기 영역) · `프로젝트/EerieVillage/개발/spec/캐릭터_리소스_규칙_v1.md` (신설) · `프로젝트/EerieVillage/개발/06_Hero1_적용_설계.md` (개발팀장 Opus Phase 1 설계 산출 예정) · Unity 영향 자산 (개발팀장 위임): `Assets/Character/{Sprites/Hero1/*, Animations/{PlayerIdle,PlayerRun,PlayerAttack,PlayerCombatIdle,PlayerJump,PlayerHit,PlayerResurrection,PlayerDeath,Player.controller}}` · `Assets/Prefabs/Player.prefab` · `Assets/Scripts/Mechanics/Health.cs` · `Assets/Tests/Editor/PlayerAttackTests.cs` · `공유/대화로그/EerieVillage/2026-05-07.md` (신설) | — | **C49 표준 프로세스 진행중**: ① 매니페스트 등록 + pm-auditor 감사 ✅ ② 캐릭터 리소스 규칙 SOT 신설 ✅ ③ 개발팀장 Opus Phase 1 설계 위임 (실측·기존 자산 영향·State Machine 재설계·hit 인터럽트 보호·resurrection 신설·기각안) → ④ 클라이언트팀 Sonnet Phase 2 적용 → ⑤ 개발팀장 Opus Phase 3 검증 → commit + push + PD Play 검증 → 완료 아카이브 이동 |
---

View File

@ -0,0 +1,224 @@
# EerieVillage 대화로그 — 2026-05-07
---
## 엔트리 1. BT5-Dev Hero1 캐릭터 교체 + 캐릭터 리소스 규칙 SOT 신설 (PD 결정 5건 A안 일괄 채택)
**시각**: 2026-05-07 12:34 ~
**주체**: 총괄PM
**영역**: BT5-Dev 개발팀 단일 영역
**유형**: PD 직접 신규 지시 수령 + 매니페스트 등록 + pm-auditor 사전 감사 + SOT 신설 + 개발팀장 Opus Phase 1 설계 위임
### 배경
PD 직접 결정 (2026-05-07): 활성 PD 지시 4건 중 BT12-Dev·BT7-Plan·BT7-Dev 보류 + BT5-Dev 우선 진행 채택. 신규 지시 2건:
> **PD 원문 1**: "기존에 지시한 layerTestGirl 은 폐기하고, EerieVillage\Assets\Character\Sprites\Hero1 경로에 있는 캐릭터 스프라이트를 사용하도록 교체해줘."
> **PD 원문 2**: 캐릭터 리소스 규칙 7종 모션 직접 명세 (`C1_<motion><frame>` 형식, attack·combatidle·idle·hit·jump·resurrection·run + 상태 전환 룰: idle⇄combatidle 5초 타이머·hit 인터럽트 보호)
본 PM 실측 결과 정합성 이슈 5건 발견 → PD에게 보고 + 결정 요청.
> **PD 원문 3 (이슈 5건 결정)**: "일괄 진행해."
= 권고안 A 일괄 채택 해석.
### PD 결정 (5건 일괄 채택)
| 이슈 | 결정 (A안) | 근거 |
|------|----------|------|
| 1. 파일명 공백 (`combat idle` ↔ `combatidle`) | A — 파일 rename | Unity asset 공백 미허용 + 파싱 안정성 |
| 2. death 자산 2종 처리 | A — 모션 8종 확장 | death + resurrection 시퀀스 정합 |
| 3. jump 1프레임 | A — 단일 프레임 정합 채택 | 자산 정합 + 간소함 |
| 4. 기존 Player*.png 9종 | A — `_archive/` 백업 + Hero1 단독 사용 | 롤백 안전망 |
| 5. Land·Spawn·Victory 모션 | A — 미사용 (jump 자세 유지·idle/resurrection 진입·미구현) | Hero1 7종으로 충분 |
### 본 PM 집행 결과 (본 응답 시점)
| 단계 | 처리 | 결과 |
|------|-----|-----|
| 1. pm-auditor 사전 감사 (C35-9) | Task 호출 | ✅ 통과 (Critical 0·Major 0·Minor 1·Improvement 2) |
| 2. C37-1 중복 감지 Grep | 캐릭터·리소스·네이밍 규칙 SOT 검색 | ✅ 중복 0건 |
| 3. 매니페스트 등록 | `2026-05-07_123431` | ✅ target_files 3건 차단 해제 |
| 4. PD 지시 SOT 갱신 | `개발팀_PD_지시_로그.md` BT5-Dev 활성 지시 5단계 추가 | ✅ |
| 5. 캐릭터 리소스 규칙 SOT 신설 | `프로젝트/EerieVillage/개발/spec/캐릭터_리소스_규칙_v1.md` | ✅ 영구 SOT |
| 6. 본 대화로그 엔트리 작성 | `공유/대화로그/EerieVillage/2026-05-07.md` | ✅ 신설 |
| 7. 개발팀장 Opus Phase 1 설계 위임 | C49 1단계 | ⏳ 본 응답 후속 단계 |
### 결정·근거·영향 (C32)
#### 결정 — PD 결정 5건 A안 일괄 채택 + C49 표준 프로세스 진행
**근거**:
- 권고 A안은 모두 자산 정합·롤백 안전·관행 정합 우선 — PD "일괄 진행" = 본 PM 권고 신뢰 표명
- C49 표준 프로세스 (팀장 Opus 설계 → 팀원 Sonnet 작업 → 팀장 Opus 검증) BT12-Dev에 이어 BT5-Dev 영역 시범 적용
- C50 토큰 추정 ~120-205K 사전 보고 완료 (PD 묵시 승인)
**영향**:
- 캐릭터 리소스 규칙 v1 = 영구 SOT (향후 캐릭터 추가 시 표준 — C2·C3... 자동 적용)
- Player.controller 전면 재설계 + Health.cs 부활 시스템 추가 = 회귀 범위 ↑ (개발팀장 Phase 1 설계 단계에서 영향 분석 의무)
- BT7-Plan VS 순수형 자동 발동(공격 버튼 제거)과 정합 영역 확인 필요 — `PlayerAttackTicker.cs` 영역 변경 없음 정합
### 산출물 (본 응답 시점)
- `공유/PD_지시_트래킹/개발팀_PD_지시_로그.md` BT5-Dev 활성 지시 갱신 (5단계 Hero1 적용 추가)
- `프로젝트/EerieVillage/개발/spec/캐릭터_리소스_규칙_v1.md` (신설 — 영구 SOT)
- `공유/대화로그/EerieVillage/2026-05-07.md` 본 엔트리 (신설)
- `.claude/manifest/active/2026-05-07_123431.md` (매니페스트)
### 다음 단계 (본 응답 후)
- 개발팀장 Opus Phase 1 설계 위임 (Task 호출) — Hero1 meta 재작성 설계·Animator State Machine 재설계·Health.cs hit 인터럽트 보호·resurrection 시스템 추가·기각안 5건 이상
### 기각안 (C32 필수)
**기각안 1** — PD 결정 5건 일부만 채택 (예: 이슈 1 A·이슈 2 B 등 혼합)
- 사유: PD "일괄 진행" 명시 = 본 PM 권고 A 신뢰 표명. 일부만 채택은 PD 의도 반영 부정합
**기각안 2** — 본 PM 직접 Phase 1·2 모두 처리 (개발팀장 위임 없이)
- 사유: C48 3자문 통과 → C49 표준 프로세스 정합. Animator State Machine 재설계 + Health.cs 부활 시스템 = 개발팀 전문 영역. PM 직접 처리 시 C49 위반
**기각안 3** — 캐릭터 리소스 규칙 SOT 작성 후순위 (개발팀장이 Phase 1 설계 시 작성)
- 사유: SOT는 PD 명세 직접 반영 영역 = PM 영역. 개발팀장 위임 시 C36 PM 자율 판단 범위 상한 위배 위험
### 관련 규칙·자산
- C5·C13·C32·C35·C36·C37·C39·C42·C44·C48·C49·C50·P19·P32·P33 정합
- 매니페스트 SOT (나) `2026-05-06` 채택 + 본 등록 = 정합
- pm-auditor 의무 호출 = C35-9 정합
- C49 표준 프로세스 = BT12-Dev 시범 후 BT5-Dev 2회차 적용
---
## 엔트리 2. BT5-Dev Hero1 Phase 2 클라이언트팀 적용 (W1~W12 일괄 집행)
**시각**: 2026-05-07 12:53 ~
**주체**: 클라이언트팀 (Sonnet — C49 2단계 적용)
**영역**: BT5-Dev Unity 클라이언트 단독 영역
**유형**: Phase 1 설계(개발팀장 Opus) 위임 수행 + W1~W12 일괄 집행
### 집행 결과 (W1~W12)
| W | 내용 | 결과 |
|---|------|------|
| W1 | combat idle01~04 PNG+meta rename → combatidle | ✅ 4건 rename + meta name 필드 갱신 |
| W2 | AnimationClip 8종 신설/재작성 | ✅ 5종 재작성 + 3종 신설 + meta 3건 신설 |
| W3 | Player.controller 전면 재작성 (8 State + 8 Param + Transition) | ✅ |
| W4 | HitInterruptGuard.cs 신설 | ✅ StateMachineBehaviour + meta |
| W5 | PlayerStateTimer.cs 신설 | ✅ MonoBehaviour + meta |
| W6 | Health.cs 확장 (Resurrect + 이벤트 3종 + Decrement hit/dead + Die dead) | ✅ |
| W7 | PlayerAttack.cs 1행 추가 (NotifyAttackFired) | ✅ |
| W8 | Player.prefab (m_Sprite Hero1 idle01 + m_Color white + PlayerStateTimer 부착 + maxHearts 직렬화) | ✅ |
| W9 | 기존 sprite 9종 → _archive/ 이동 | ✅ 9 PNG + 9 meta |
| W10 | EditMode 테스트 13→19건 (12번 개정 + 신규 6건) | ✅ |
| W11 | PlayerController 실측 — combatidle Bool/jump = PlayerStateTimer/grounded 조건으로 처리 확인 | ✅ 변경 없음 정합 |
| W12 | 최종 정합 검증 (8 State 전수 OK + 8 Param 전수 OK + 폐기 4 State 부재 OK + prefab 정합 OK) | ✅ |
### C6-1 백업
- **timestamp**: `20260507_1253`
- **백업 9종**: Player.controller·PlayerIdle.anim·PlayerRun.anim·PlayerAttack.anim·PlayerJump.anim·PlayerDeath.anim·Player.prefab·Health.cs·PlayerAttackTests.cs
- _archive/ 이동 9종 = 백업 자체 (guid 보존)
### 변경 파일 (Unity 영역 — E:/EerieVillage)
**재작성**: `Assets/Character/Animations/PlayerIdle.anim`·`PlayerRun.anim`·`PlayerAttack.anim`·`PlayerJump.anim`·`PlayerDeath.anim`·`Player.controller`
**갱신**: `Assets/Scripts/Mechanics/Health.cs`·`Assets/Scripts/Gameplay/PlayerAttack.cs`·`Assets/Prefabs/Player.prefab`·`Assets/Tests/Editor/PlayerAttackTests.cs`
**meta 갱신 (W1)**: `Assets/Character/Sprites/Hero1/C1_combatidle01~04.png.meta`
### 신설 파일
- `Assets/Character/Animations/PlayerCombatIdle.anim` + .meta
- `Assets/Character/Animations/PlayerHit.anim` + .meta
- `Assets/Character/Animations/PlayerResurrection.anim` + .meta
- `Assets/Scripts/Mechanics/HitInterruptGuard.cs` + .meta
- `Assets/Scripts/Mechanics/PlayerStateTimer.cs` + .meta
- `Assets/Character/Sprites/_archive/` 폴더 + .meta
### Phase 1 설계 정합
- 8 State: Player-Idle·Run·Attack·Jump·Death·CombatIdle·Hit·Resurrection ✅
- 8 Parameter: velocityX·velocityY·grounded·attack·dead·hit·combatidle·resurrect ✅
- 폐기 4 State 부재: Player-Hurt·Land·Spawn·Victory ✅
- BT7-Plan 보존: PlayerAttackTicker 변경 0 · Health 기존 API 전부 보존 ✅
- BT12-Dev 미연관: ISkillRuntime 4종 주석 명시 ✅
### 매니페스트
- `plan_id`: `2026-05-07_125257`
- `target_files`: `공유/대화로그/EerieVillage/2026-05-07.md`
### C48 3자문
- Phase 3 검증 Task 호출 = 0회 (단계 분리 의무 준수)
### 회귀 위험
- `Player.controller` 전면 재작성 → Phase 3 개발팀장 YAML 정합 재검증 필요
- `Health.cs` Animator 의존 신규 추가 (`GetComponent<Animator>()`) — Enemy Health에도 동일 컴포넌트 구조이면 Enemy hit 발동 가능성 (Phase 3 영역)
### 토큰 사용량 추정
~180K (C50 사전 승인 범위 200K 이내)
---
## 엔트리 3. BT5-Dev Hero1 Phase 3 개발팀장 검증 (C49 3단계)
**시각**: 2026-05-07 13:13 ~
**주체**: 개발팀장 (Opus — C49 3단계 검증)
**영역**: Phase 2 적용분(엔트리 2) §A~§J 정합 실측 검증
**유형**: Unity 자산 직접 grep + Phase 1 설계 정합 + 회귀 위험 2건 점검 (수정 영역 0)
### §A~§J 검증 결과 등급
| § | 검증 영역 | 등급 | 핵심 결과 |
|---|---------|------|---------|
| **A** | Player.controller YAML 정합 | **Pass** | 8 State + 8 Param + AnyState→Hit + Player-Hit 부착 + Death→Resurrection→Idle Transition 모두 정합. 폐기 4 State 부재 확증. AnyState→Hit `m_CanTransitionToSelf: 0` 확증 |
| **B** | AnimationClip 8종 정합 | **Pass** | 8 anim 모두 m_PPtrCurves Hero1 sprite 첫 프레임 guid 정합 (idle01·run01·attack01·combatidle01·jump01·hit01·resurrection01·death01 일치). LoopTime 정합 (Idle/Run/CombatIdle = 1, 나머지 5종 = 0). 신규 anim 3종 guid 충돌 0 |
| **C** | Health.cs 확장 검증 | **Pass** | 기존 API 전수 보존 (maxHearts·QuartersPerHeart·IncreaseMaxHearts·Heal·Decrement(int)·i-frame 0.6s) + 신규 4종 (Resurrect·이벤트 3종) + Animator null-safe 처리 (`if (animator != null)`) + BT12-Dev 미연관 주석 명시 |
| **D** | 신규 컴포넌트 2종 | **Pass** | HitInterruptGuard.cs StateMachineBehaviour OnStateEnter `ResetTrigger("hit")` 정합. PlayerStateTimer.cs MonoBehaviour 5초 타이머 + RequireComponent(PlayerController·Animator) + NotifyAttackFired 외부 API 정합 |
| **E** | EditMode 테스트 19건 | **Pass** | 12번 개정(Hero1Idle01Guid `78c7da0e2fc366543ae4ad5e3ceb1b94`) + 신규 6건(Player_Has_8_Motion_States·Player_Controller_Has_New_Parameters·Player_Hit_Interrupts_Protected·Player_CombatIdle_Timer_Component_Attached·Player_Death_To_Resurrection_Sequence·Health_Has_Resurrect_Method_And_Events) 모두 정합 |
| **F** | Player.prefab 정합 | **Pass** | m_Sprite guid `78c7da0e...` = Hero1 idle01. m_Color = white. PlayerStateTimer 부착(guid `e5f6789012345678abcdef0102030405`) + combatIdleDuration: 5. maxHearts:1·maxHP:4 직렬화 |
| **G** | Hero1 폴더 + _archive | **Pass** | combat idle 4 PNG `combatidle01~04` rename 확증 (공백 제거). 기존 Player*.png 9 + meta 9 = 18 파일 `_archive/` 이동 확증. 폐기 4 anim guid (Hurt·Land·Spawn·Victory) Player.controller 참조 0건 |
| **H** | BT7-Plan/BT12-Dev 정합 | **Pass** | PlayerAttackTicker 변경 0 ([RequireComponent(PlayerController)] 보존). Health 기존 API 보존. PlayerAttack.cs:42-44 1행 NotifyAttackFired 추가만. ISkillRuntime 미연관 주석 명시 |
| **I** | C6-1 백업 9종 + _archive 9종 | **Pass** | timestamp `20260507_1253` 통일 9건 (Player.controller·5 anim·Player.prefab·Health.cs·PlayerAttackTests.cs). _archive 9 PNG + 9 meta 정합 |
| **J** | 회귀 위험 2건 점검 결과 | **Major** | 1번 controller YAML 정합 = 완전 통과. **2번 Health Animator 의존**: Enemy.controller에 `hit`·`dead`·`resurrect` Parameter 미존재 → Unity는 미존재 Parameter SetTrigger/SetBool 호출 시 logical no-op (silent ignore). 게임 동작 영향 0 (Enemy 사망 체인은 별 channel `Schedule<EnemyDeath>` AttackHitbox.cs:75). **단 Editor Console warning spam 가능성** (Unity 버전 의존) |
### Phase 4 진입 가능 여부
**조건부 통과 (Major 1건 — 후속 권고, 차단 영역 0건)**
- Critical 0건 — Phase 4 진입 차단 영역 없음
- Major 1건 — 회귀 위험 2번 영역. Enemy Animator parameter warning spam 가능성. 게임 동작 영향 0이므로 commit/push 진행 가능. PD Play 검증 시 Console warning 모니터링 권고
### 회귀 위험 2건 정밀 점검 결과
**1번 Player.controller YAML 정합** = 완전 통과
- 8 State 전수 존재 + 폐기 4 State 부재 + AnyState→Hit Transition 존재 + HitInterruptGuard MonoBehaviour 부착(guid 일치 정합) + AnyState→Hit `m_CanTransitionToSelf: 0` + Death→Resurrection→Idle 시퀀스 정합 + 모든 신규 fileID/guid 충돌 0건
**2번 Health.cs Animator 의존 — Enemy 영향**
- Enemy.prefab은 Animator + Enemy.controller 보유
- Enemy.controller Parameter = `velocityX`·`velocityY`·`hurt`·`death`·`grounded` (5종) — Player와 분리
- `hit`/`dead`/`resurrect` Parameter Enemy.controller에 미존재
- Health.Decrement → animator.SetTrigger("hit") 호출 시 Unity Animator는 silent ignore (예외 0)
- Enemy 사망 체인은 별 channel(`AttackHitbox.cs:75 Schedule<EnemyDeath>`)로 분리 — 영향 0
- **Major 잠재 리스크**: Editor Console에 매 데미지 1회 `Parameter 'hit' does not exist` warning 가능 (Unity 버전 의존). 게임 동작 영향 0. PD Play 검증 시 Console 모니터링 권고
### 검증 산출물
- 본 엔트리 (대화로그 추가)
- 매니페스트: `2026-05-07_131323` (target_files: 본 대화로그)
### 검증 토큰 사용량
~25K (C50 추정 20-30K 범위)
### Phase 4 후속 영역 (PM)
1. commit (개발팀장 서명 + Phase 1·2·3 산출물 + 본 검증 결과)
2. push (main)
3. PD Play 검증 인계 (검증 기대 동작 10건은 Phase 1 §14-3 영역)
4. (선택) Major 후속 — Enemy Health Animator parameter warning 차단 영역 별건 검토 (C2 근원적 해결 권고: Player 전용 Animator-aware Health 분리 또는 Enemy.controller `hit`/`dead`/`resurrect` Parameter 추가 또는 Health.cs Parameter 존재 가드)
---

View File

@ -0,0 +1,768 @@
---
type: 설계_문서
scope: Hero1_적용_Phase_1
author: 개발팀장 (Opus)
date: 2026-05-07
version: v1.0 (BT5-Dev Hero1 Phase 1 단독 설계)
project: EerieVillage (기묘한 고을 : 조선퇴마뎐 / EerieVillage: Joseon Exorcist)
phase: BT5-Dev Phase 1 — Hero1 캐릭터 교체 설계 (개발팀장 단독)
data_source: |
- 프로젝트/EerieVillage/개발/spec/캐릭터_리소스_규칙_v1.md (영구 SOT)
- 프로젝트/EerieVillage/개발/05_PlayerTestGirl_아틀라스_적용.md (폐기 영역)
- 프로젝트/EerieVillage/개발/04_BT5-Dev_2단계_구현보고.md (기존 컴포넌트 현황)
- 프로젝트/EerieVillage/개발/spec/스킬_시스템_설계_v1.md (BT12-Dev 보류 영역)
- Unity 직접 실측 (Assets/Character/Sprites/Hero1/, Assets/Character/Animations/, Assets/Prefabs/Player.prefab, Assets/Scripts/Mechanics/{Health.cs, PlayerController.cs, AttackHitbox.cs}, Assets/Tests/Editor/PlayerAttackTests.cs)
status: Phase 1 설계 완료 — Phase 2 클라이언트팀(Sonnet) 구현 위임 대기 · Phase 3 개발팀장 검증 대기
---
# 06. Hero1 적용 설계 (Phase 1)
> **본 설계 = Phase 1 설계만**. Phase 2 적용·Phase 3 검증은 별건. 본 문서 내 코드 변경 영역은 **명세만 기록** (Edit/Write 금지 — Phase 2 영역).
---
## §1. 아키텍처 개요
### 1-1. 설계 목표 (C11 개발 관점 3축)
1. **자원 효율성**: 모션 8종 × 평균 4프레임 = ~32 sprite 동시 로드. 개별 PNG import 방식이지만 모바일 60fps 충분 허용
2. **코드 구조 직관성**: 모션 = State, 발동 트리거 = Parameter, 인터럽트 보호 = Animator Behaviour 또는 Health.cs Trigger 가드. 다음 개발자 5분 내 파악 가능
3. **범용성**: C2·C3 신규 캐릭터 추가 시 동일 controller 구조 + sprite guid 교체만으로 확장 가능 (`캐릭터_리소스_규칙_v1.md` §1·§2 표준 준수)
### 1-2. 모션 8종 ↔ Animator State 매핑
| # | 모션 키 (SOT) | AnimatorState 명 | AnimationClip 파일 | 프레임 | Loop |
|---|-------------|-----------------|------------------|------|------|
| 1 | `attack` | `Player-Attack` | `PlayerAttack.anim` (재작성) | 8 | non-loop |
| 2 | `combatidle` | `Player-CombatIdle` (신설) | `PlayerCombatIdle.anim` (신설) | 4 | loop |
| 3 | `idle` | `Player-Idle` (재활용·motion 교체) | `PlayerIdle.anim` (재작성) | 4 | loop |
| 4 | `hit` | `Player-Hit` (신설·기존 Hurt 폐기) | `PlayerHit.anim` (신설·기존 PlayerHurt.anim 폐기) | 2 | non-loop · 인터럽트 보호 |
| 5 | `jump` | `Player-Jump` (재활용·motion 교체) | `PlayerJump.anim` (재작성) | 1 | static |
| 6 | `resurrection` | `Player-Resurrection` (신설) | `PlayerResurrection.anim` (신설) | 8 | non-loop |
| 7 | `run` | `Player-Run` (재활용·motion 교체) | `PlayerRun.anim` (재작성) | 8 | loop |
| 8 | `death` | `Player-Death` (재활용·motion 교체) | `PlayerDeath.anim` (재작성) | 2 | non-loop |
**기존 controller 9 State 중 4 State 폐기** (Spawn·Land·Hurt·Victory):
- Player-Spawn → 폐기 (PD 결정 5 — 미사용)
- Player-Land → 폐기 (PD 결정 5 — jump 자세 1프레임 유지)
- Player-Hurt → 폐기 (Hit으로 대체 — 명칭·인터럽트 보호 룰 변경)
- Player-Victory → 폐기 (PD 결정 5 — 미사용)
### 1-3. 컴포넌트 책임 분리 (5종)
```
┌─────────────────────────────────────────────────┐
│ Animator + Player.controller │
│ - State 8개 + Parameter 8개 보유 │
│ - Transition 매트릭스로 8종 모션 전환 관리 │
│ - hit 인터럽트 보호 = StateMachineBehaviour 영역 │
└──────────────▲──────────────────────────────────┘
│ SetTrigger·SetBool·SetFloat 호출
┌──────────────┴──────────────────────────────────┐
│ PlayerController (기존 — 변경 없음) │
│ - 이동·점프 입력 처리 + facing 추적 │
│ - animator.SetBool("grounded")·SetFloat("velocityX") │
└─────────────────────────────────────────────────┘
▲ Schedule<PlayerAttack> 발화
┌──────────────┴──────────────────────────────────┐
│ PlayerAttackTicker (기존 — 변경 없음) │
│ - 0.5초 주기로 Schedule<PlayerAttack> 발화 │
│ - VS 순수형 자동 발동 (BT7-Plan) │
└─────────────────────────────────────────────────┘
│ player.animator.SetTrigger("attack") 호출
┌──────────────▼──────────────────────────────────┐
│ PlayerAttack (기존 — 변경 없음) │
│ - SetTrigger("attack") + AttackHitbox.Fire 호출 │
└─────────────────────────────────────────────────┘
│ animator.SetTrigger("hit") 호출 (신규)
┌──────────────┼──────────────────────────────────┐
│ Health (기존 — §7 영역 확장) │
│ - Decrement(int) → animator.SetTrigger("hit") │
│ - HP 0 → SetBool("dead", true) (기존 victory 자리) │
│ - 부활 발동 → SetTrigger("resurrect") (신규) │
└─────────────────────────────────────────────────┘
│ §6 신규 컴포넌트
┌──────────────▼──────────────────────────────────┐
│ PlayerStateTimer (신설 — §6 채택안) │
│ - 마지막 attack 시각 추적 + 5초 경과 감지 │
│ - SetBool("combatidle", true/false) 갱신 │
└─────────────────────────────────────────────────┘
```
---
## §2. Hero1 meta 재작성 설계 (sprite slice 전략)
### 2-1. 비교 — 3안
| 안 | 방식 | 장점 | 단점 |
|----|------|------|------|
| **A. 개별 PNG 37건 (현 상태)** | Hero1 폴더 37 PNG 각각 단일 sprite import (현재 구성) | 자산 그대로 활용 · meta 재작성 최소 (rename된 4건만) · 한 PNG = 한 sprite 명료성 | Draw call 8회/모션 (모바일 sprite atlas 대비 비효율) · 메모리 fragmentation |
| **B. atlas pack 단일 sheet** | 37 PNG → SpriteAtlas로 packing (Unity SpriteAtlas 자산 신설) | Draw call 1회 · 메모리 효율 · 빌드 시 자동 생성 | 자산 추가 발주 (`Hero1.spriteatlas` SOT 신설) · 기획팀 미요청 |
| **C. 모션별 sprite sheet 8개** | 모션별 PNG 통합 (예: `C1_attack.png` 8프레임 × 1 sheet) | 모션 단위 직관적 · slice grid 표준화 | 자산 재발주 필요 · 현 자산 폐기 |
### 2-2. **채택 = A (개별 PNG 37건 그대로 활용)**
**근거**:
1. PD 직접 명세 자산 그대로 활용 (재발주 없음)
2. 모바일 60fps 허용 — 8 sprite × 60fps = 480 draw call/s (Unity 2D 보편 허용)
3. `캐릭터_리소스_규칙_v1.md` §4.1 폴더 구조와 정합 (`Sprites/Hero1/C1_*.png`)
4. 향후 최적화 필요 시 SpriteAtlas auto-pack 오버레이 (코드·자산 변경 없이 적용 가능)
### 2-3. meta 재작성 영역 (rename 4건만)
| 기존 파일 | 신규 파일 | meta 처리 |
|---------|---------|---------|
| `C1_combat idle01.png` + .meta | `C1_combatidle01.png` + .meta | rename (PNG + meta 함께) — guid·internalID 보존 |
| `C1_combat idle02.png` + .meta | `C1_combatidle02.png` + .meta | rename — guid·internalID 보존 |
| `C1_combat idle03.png` + .meta | `C1_combatidle03.png` + .meta | rename — guid·internalID 보존 |
| `C1_combat idle04.png` + .meta | `C1_combatidle04.png` + .meta | rename — guid·internalID 보존 |
**meta 내부 변경 항목**: `internalIDToNameTable.second` 필드의 `C1_combat idle01_0``C1_combatidle01_0` 갱신 + `spriteSheet.sprites[].name` + `nameFileIdTable` 키 동시 갱신.
### 2-4. import 표준 (37 PNG 일관)
| 항목 | 값 | 근거 |
|------|----|------|
| `textureType` | 8 (Sprite) | 현재 meta 정합 |
| `spriteMode` | 2 (Multiple — 단일 sprite도 Multiple로 통일) | 일관성 + 향후 atlas 전환 안정 |
| `pixelsPerUnit` | 100 | 현재 meta + 기존 PlayerTestGirl 정합 |
| `pivot` | (0.5, 0.5) center | `캐릭터_리소스_규칙_v1.md` §4 표준 (현 meta `alignment: 0` + `spritePivot: 0.5,0.5`) — Phase 2 시 sprite rect 내부 pivot도 `(0.5, 0.5)` 통일 권고 |
| `alphaIsTransparency` | 1 | 현재 meta 정합 |
| `spriteMeshType` | 1 (Tight) | 현재 meta 정합 |
| `maxTextureSize` | 2048 | 현재 meta 정합 |
**Phase 2 작업 영역**: 4건 rename 후 meta 일관성 + 신규 sprite name 검증.
---
## §3. AnimationClip 설계 (8종 신설/재작성)
### 3-1. 8종 일괄 명세
| anim 파일 | 처리 | sampleRate | duration | LoopTime | 참조 sprite (m_PPtrCurves) |
|---------|------|-----------|---------|---------|---------------------------|
| `PlayerIdle.anim` | 재작성 | 10 fps | 0.4s | 1 (loop) | C1_idle01~04 (시간 0·0.1·0.2·0.3) |
| `PlayerRun.anim` | 재작성 | 16 fps | 0.5s | 1 (loop) | C1_run01~08 (시간 0·0.0625·0.125·0.1875·0.25·0.3125·0.375·0.4375) |
| `PlayerAttack.anim` | 재작성 | 16 fps | 0.5s | 0 (non-loop) | C1_attack01~08 (시간 0·0.0625·...·0.4375) |
| `PlayerCombatIdle.anim` | 신설 | 8 fps | 0.5s | 1 (loop) | C1_combatidle01~04 (시간 0·0.125·0.25·0.375) |
| `PlayerJump.anim` | 재작성 | 1 fps | 0.1s (1 frame static) | 0 (non-loop) | C1_jump01 (시간 0) |
| `PlayerHit.anim` | 신설 (PlayerHurt.anim 폐기 대체) | 10 fps | 0.2s | 0 (non-loop) | C1_hit01·02 (시간 0·0.1) |
| `PlayerResurrection.anim` | 신설 | 8 fps | 1.0s | 0 (non-loop) | C1_resurrection01~08 (시간 0·0.125·...·0.875) |
| `PlayerDeath.anim` | 재작성 | 4 fps | 0.5s | 0 (non-loop) | C1_death01·02 (시간 0·0.25) |
### 3-2. anim YAML 표준 (PlayerIdle.anim 정합 — 기존 SOT 구조 보존)
```yaml
# PlayerIdle.anim 신규 형태 (기존 PlayerTestGirl 참조 → C1_idle 4건 참조)
m_PPtrCurves:
- curve:
- time: 0
value: {fileID: <C1_idle01 internalID>, guid: <C1_idle01 guid>, type: 3}
- time: 0.1
value: {fileID: <C1_idle02 internalID>, guid: <C1_idle02 guid>, type: 3}
- time: 0.2
value: {fileID: <C1_idle03 internalID>, guid: <C1_idle03 guid>, type: 3}
- time: 0.3
value: {fileID: <C1_idle04 internalID>, guid: <C1_idle04 guid>, type: 3}
attribute: m_Sprite
classID: 212
script: {fileID: 0}
m_SampleRate: 10
m_AnimationClipSettings:
m_StartTime: 0
m_StopTime: 0.4
m_LoopTime: 1
```
**Phase 2 작업 영역**: 8 anim 모두 위 패턴으로 작성. `internalID`·`guid`는 Phase 2에서 각 PNG meta Read 후 추출.
### 3-3. anim 신규 4건 guid 후보 (충돌 검증 영역)
| anim 파일 | guid 후보 | 검증 영역 |
|---------|---------|---------|
| `PlayerCombatIdle.anim.meta` | (Phase 2 신규 발급) | grep 0건 검증 의무 |
| `PlayerHit.anim.meta` | (Phase 2 신규 발급) | grep 0건 검증 의무 |
| `PlayerResurrection.anim.meta` | (Phase 2 신규 발급) | grep 0건 검증 의무 |
`PlayerJump.anim.meta`는 기존 guid `1d7e02f8cf5ba47a78bc1e19cc378478` 보존 (controller 참조 불변).
---
## §4. Player.controller 전면 재설계
### 4-1. Parameter 8종 (확장 — 기존 7개 → 8개)
| 파라미터 | 타입 | m_Type | 기존/신규 | 용도 |
|---------|------|-------|---------|------|
| `velocityX` | Float | 1 | 기존 | 이동 속도 (idle ⇄ run 전환) |
| `velocityY` | Float | 1 | 기존 | (사용 미확인 — 기존 보존) |
| `grounded` | Bool | 4 | 기존 | 지면 상태 (jump 진입 차단) |
| `attack` | Trigger | 9 | 기존 | 공격 발동 (PlayerAttack.Execute에서 호출) |
| `dead` | Bool | 4 | 기존 | 사망 상태 (death State 진입) |
| ~~`hurt`~~ | Trigger | 9 | **폐기** (`hit`으로 rename) | — |
| ~~`victory`~~ | Trigger | 9 | **폐기** (PD 결정 5 미사용) | — |
| `hit` | Trigger | 9 | **신규** | 피격 발동 (Health.Decrement에서 호출) |
| `combatidle` | Bool | 4 | **신규** | 5초 타이머 영역 (true = combatidle, false = idle) |
| `resurrect` | Trigger | 9 | **신규** | 부활 발동 (Health 부활 처리에서 호출) |
**최종 Parameter = 8종**: `velocityX`·`velocityY`·`grounded`·`attack`·`dead`·`hit`·`combatidle`·`resurrect`.
### 4-2. State 8종 (Player- 접두 보존 — 기존 controller 명명 정합)
기존 9 State (Idle·Spawn·Death·Hurt·Jump·Land·Run·Victory·Attack)에서:
- **재활용 5종** (motion guid 교체): Player-Idle·Player-Run·Player-Jump·Player-Death·Player-Attack
- **신설 3종**: Player-CombatIdle·Player-Hit·Player-Resurrection
- **폐기 4종**: Player-Spawn·Player-Land·Player-Hurt·Player-Victory
| State | fileID | m_Motion (guid) |
|-------|--------|-----------------|
| Player-Idle | `1102433169737779366` (기존 보존) | `PlayerIdle.anim` 신규 guid (재작성 후 동일 guid 보존) |
| Player-Run | `1102799505466558240` (기존 보존) | `PlayerRun.anim` 신규 guid (재작성 후 동일 guid 보존) |
| Player-Jump | `1102567981102962784` (기존 보존) | `PlayerJump.anim` 신규 guid (재작성 후 동일 guid 보존) |
| Player-Death | `1102635880149297478` (기존 보존) | `PlayerDeath.anim` 신규 guid (재작성 후 동일 guid 보존) |
| Player-Attack | `1102700000000000001` (기존 보존) | `PlayerAttack.anim` 기존 guid `c8d7e5a1f9b24e63a7f5d2c8e1b9a4f7` 보존 |
| Player-CombatIdle | `1102500000000000001` (신규 fileID) | `PlayerCombatIdle.anim` 신규 guid |
| Player-Hit | `1102500000000000002` (신규 fileID) | `PlayerHit.anim` 신규 guid |
| Player-Resurrection | `1102500000000000003` (신규 fileID) | `PlayerResurrection.anim` 신규 guid |
**fileID 충돌 회피**: `1102500000000000001~003`은 기존 controller fileID 9건과 grep 검증 후 충돌 0 확인 (Phase 2 영역).
### 4-3. Transition 매트릭스
| From | To | 조건 | ExitTime | HasExitTime | TransitionDuration | InterruptionSource |
|------|----|------|---------|-------------|-------------------|-------------------|
| **Default Entry** | Player-Idle | (자동) | — | — | — | — |
| **AnyState** | Player-Hit | `hit` (Trigger) | 0 | 0 | 0 | 0 |
| **AnyState** | Player-Attack | `attack` (Trigger) | 0 | 0 | 0 | 0 |
| **AnyState** | Player-Death | `dead == true` | 0 | 0 | 0 | 0 |
| Player-Idle | Player-Run | `velocityX > 0.001` | 0 | 0 | 0 | 0 |
| Player-Idle | Player-CombatIdle | `combatidle == true` | 0 | 0 | 0 | 0 |
| Player-Idle | Player-Jump | `grounded == false` | 0 | 0 | 0 | 0 |
| Player-Run | Player-Idle | `velocityX < 0.001` AND `combatidle == false` | 0 | 0 | 0 | 0 |
| Player-Run | Player-CombatIdle | `velocityX < 0.001` AND `combatidle == true` | 0 | 0 | 0 | 0 |
| Player-Run | Player-Jump | `grounded == false` | 0 | 0 | 0 | 0 |
| Player-CombatIdle | Player-Idle | `combatidle == false` | 0 | 0 | 0 | 0 |
| Player-CombatIdle | Player-Run | `velocityX > 0.001` | 0 | 0 | 0 | 0 |
| Player-CombatIdle | Player-Jump | `grounded == false` | 0 | 0 | 0 | 0 |
| Player-Jump | Player-Idle | `grounded == true` AND `velocityX < 0.001` AND `combatidle == false` | — | 1 (m_HasExitTime) | 0 | 0 |
| Player-Jump | Player-CombatIdle | `grounded == true` AND `velocityX < 0.001` AND `combatidle == true` | — | 1 | 0 | 0 |
| Player-Jump | Player-Run | `grounded == true` AND `velocityX > 0.001` | — | 1 | 0 | 0 |
| Player-Attack | Player-Idle | (조건 없음) | 1 | 1 | 0 | 0 |
| Player-Hit | Player-Idle | (조건 없음 — 직전 상태 복귀는 단순화 위해 idle 회귀, 즉시 SetBool/Trigger 다음 Update에서 자연 재전환) | 1 | 1 | 0 | 0 |
| Player-Death | Player-Resurrection | `resurrect` (Trigger) | 1 | 1 | 0 | 0 |
| Player-Resurrection | Player-Idle | (조건 없음 — `dead`도 false로 동기) | 1 | 1 | 0 | 0 |
### 4-4. m_CanTransitionToSelf
- Player-Hit → `m_CanTransitionToSelf: 0` (인터럽트 보호 — §5 영역)
- Player-Attack → `m_CanTransitionToSelf: 0` (이미 기존 `1101700000000000002` 정합)
- Player-Resurrection → `m_CanTransitionToSelf: 0` (1회 재생 보호)
- 그 외 → `m_CanTransitionToSelf: 1` (기본값)
---
## §5. hit 인터럽트 보호 구현 설계
### 5-1. 비교 — 2안
| 안 | 방식 | 장점 | 단점 |
|----|------|------|------|
| **A. Animator StateMachineBehaviour** | `Player-Hit` State에 `HitInterruptGuard.cs : StateMachineBehaviour` 부착. `OnStateEnter`에서 `animator.ResetTrigger("hit")` + AnyState→Hit transition을 m_CanTransitionToSelf:0으로 차단 | Animator 단일 책임 · Health.cs 변경 0 · `IsInTransition` 체크로 자연 보호 | StateMachineBehaviour는 .controller YAML 직접 편집 시 fileID 관리 복잡 · ScriptableObject 신규 .meta 필요 |
| **B. Health.cs Trigger 진입 가드** | `Health.Decrement(int)` 내부에서 `animator.GetCurrentAnimatorStateInfo(0).IsName("Player-Hit") && stateInfo.normalizedTime < 1.0` 체크 후 `SetTrigger("hit")` 진입. 진행 중이면 skip | Health.cs 단일 위치 가드 · YAML 편집 최소 (Animator 영역 변경 없음) · 디버그 단순 | Health.cs가 Animator API 의존 (현재는 Animator 직접 참조 없음 — `PlayerController.animator` 우회 필요) · Animator 미부착 객체(Enemy 향후) 영역 일반화 어려움 |
### 5-2. **채택 = A (Animator StateMachineBehaviour)**
**근거**:
1. SOT(`캐릭터_리소스_규칙_v1.md` §3.1.2) "Unity 구현 영역" 명시 — `StateMachineBehaviour.OnStateEnter` 영역 지목
2. Health.cs는 Animator 미참조 (현재 `health = GetComponent<Health>()` 정합) → B안은 신규 의존 발생
3. Player-Hit `m_CanTransitionToSelf: 0` 단독으로도 80% 보호. Behaviour 추가 = 잔여 20% (transition 중 재진입) 차단 강화
4. Hero1 외 향후 캐릭터(C2·C3) 동일 controller 구조 재사용 시 Behaviour 자동 계승
### 5-3. 신규 컴포넌트 명세 — `HitInterruptGuard.cs`
**경로**: `Assets/Scripts/Mechanics/HitInterruptGuard.cs` (신설)
```csharp
using UnityEngine;
namespace Platformer.Mechanics
{
/// <summary>
/// Player-Hit State에 부착되는 StateMachineBehaviour.
/// hit 진입 시 hit Trigger를 즉시 Reset하여 다음 데미지 수신 시
/// 재진입을 1회 사이클(애니메이션 재생 종료)까지 차단.
/// SOT: 캐릭터_리소스_규칙_v1.md §3.1.2 hit 인터럽트 보호 (PD 명세 핵심)
/// </summary>
public class HitInterruptGuard : StateMachineBehaviour
{
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.ResetTrigger("hit");
}
}
}
```
**부착 영역**: `Player.controller` YAML의 `Player-Hit` State `m_StateMachineBehaviours` 리스트에 신규 `MonoBehaviour` fileID 추가.
### 5-4. Health.cs Trigger 호출 영역 (§7 통합)
```csharp
// Decrement(int damage) 내부 — currentHP 감산 직전
if (player != null && player.animator != null)
{
player.animator.SetTrigger("hit"); // Behaviour가 즉시 Reset → 1회 사이클 보호
}
```
(상세 §7 참조)
---
## §6. idle⇄combatidle 5초 타이머 구현 설계
### 6-1. 비교 — 2안
| 안 | 방식 | 장점 | 단점 |
|----|------|------|------|
| **A. PlayerController.cs 확장** | 기존 PlayerController에 `lastAttackTime` float + Update에서 `Time.time - lastAttackTime` 체크 → `animator.SetBool("combatidle", true/false)` 호출. PlayerAttack.Execute에서 `lastAttackTime = Time.time` 갱신 | 기존 컴포넌트 단일 위치 · 의존성 신규 없음 | PlayerController 책임 비대화 (이동·점프·공격 facing·전투 타이머 4축) · BT7-Plan VS 순수형 정합 책임 분산 |
| **B. 신규 컴포넌트 PlayerStateTimer.cs** | 별도 MonoBehaviour 신설. `RequireComponent(PlayerController)` + `RequireComponent(Animator)`. PlayerAttack 발화 이벤트 구독으로 `lastAttackTime` 갱신. Update에서 `combatidle` Bool 갱신 | 단일 책임 (전투 상태 타이머 단독) · Phase 2 별건 Edit 영역 분리 가능 · BT7-Plan PlayerAttackTicker 패턴 정합 (1 컴포넌트 = 1 책임) | 컴포넌트 1종 추가 (Player.prefab YAML MonoBehaviour 추가 작업) |
### 6-2. **채택 = B (신규 컴포넌트 `PlayerStateTimer.cs`)**
**근거**:
1. C11 단일 책임 원칙 — PlayerController는 이동·점프·facing 책임만 유지
2. BT7-Plan `PlayerAttackTicker` 패턴 정합 — "타이머 컴포넌트 별도" 구조 일관성
3. Hero1 외 향후 적 캐릭터 동일 통계 필요 시 EnemyStateTimer로 재사용 가능
4. 카드·특성 효과로 5초 타이머 변동(예: "전투 자세 유지 시간 +2초") 시 단일 컴포넌트 필드만 수정
### 6-3. 신규 컴포넌트 명세 — `PlayerStateTimer.cs`
**경로**: `Assets/Scripts/Mechanics/PlayerStateTimer.cs` (신설)
```csharp
using Platformer.Gameplay;
using UnityEngine;
using static Platformer.Core.Simulation;
namespace Platformer.Mechanics
{
/// <summary>
/// 마지막 공격 시점 추적 + idle⇄combatidle 전환 발동.
/// SOT: 캐릭터_리소스_규칙_v1.md §3.1.1 idle⇄combatidle 5초 타이머
/// 카드·특성 효과로 combatIdleDuration 조정 가능 (P13-1 공용 모듈 인터페이스).
/// </summary>
[RequireComponent(typeof(PlayerController))]
[RequireComponent(typeof(Animator))]
public class PlayerStateTimer : MonoBehaviour
{
[Tooltip("마지막 공격 후 combatidle 유지 시간(초). 카드·특성으로 증감 가능.")]
public float combatIdleDuration = 5f;
Animator animator;
float lastAttackTime = -100f; // 초기값: 게임 시작 시 idle 상태
void Awake()
{
animator = GetComponent<Animator>();
}
/// <summary>
/// PlayerAttack.Execute 또는 PlayerAttackTicker가 공격 발화 시 호출.
/// 외부 API로 노출하여 결합 최소화.
/// </summary>
public void NotifyAttackFired()
{
lastAttackTime = Time.time;
}
void Update()
{
bool inCombat = (Time.time - lastAttackTime) < combatIdleDuration;
animator.SetBool("combatidle", inCombat);
}
}
}
```
### 6-4. PlayerAttack.Execute 통합 (§11 영향 0 — animator 호출 옆에 한 줄 추가)
```csharp
// PlayerAttack.cs — Execute 내부, animator.SetTrigger("attack") 직후
var stateTimer = player.GetComponent<PlayerStateTimer>();
if (stateTimer != null) stateTimer.NotifyAttackFired();
```
**BT7-Plan 정합**: `PlayerAttackTicker.cs` 영역은 변경 0. PlayerAttack.Execute 내부 1행 추가만으로 통합.
---
## §7. Health.cs 부활 시스템 추가 설계
### 7-1. 변경 영역 (기존 API 보존 — 추가만)
| 메서드/필드 | 처리 |
|-----------|------|
| `maxHearts`·`QuartersPerHeart`·`maxHP`·`currentHP` | **보존** (BT7-Plan 정합) |
| `Increment()`·`Heal(int)`·`Decrement()`·`Decrement(int)` | **보존** |
| `IncreaseMaxHearts(int)`·`Die()`·`IsAlive`·`IsInvulnerable` | **보존** |
| `invulnerableDuration`·`invulnerableUntil` | **보존** (i-frame 0.6s) |
| `Awake()` | **보존** |
| **신규 필드**: `canResurrect` (Bool) | 부활 룰 충족 여부 (BT7-Plan 영역 — 기획팀 결정) |
| **신규 메서드**: `Resurrect()` | death 종료 후 외부 호출 → currentHP 복원 + animator.SetTrigger("resurrect") |
| **신규 이벤트 3종**: `OnDamagedEvent`·`OnDeathEvent`·`OnResurrectEvent` | C# `event Action` — UI·SE·StateTimer 구독 |
### 7-2. Decrement(int) 확장 (Animator hit Trigger 호출 — §5 통합)
```csharp
// 기존 Decrement(int) 내부 — currentHP -= damage 직전 + 직후 추가
public void Decrement(int damage)
{
if (damage <= 0) return;
if (Time.time < invulnerableUntil) return;
currentHP = Mathf.Clamp(currentHP - damage, 0, maxHP);
if (invulnerableDuration > 0f)
{
invulnerableUntil = Time.time + invulnerableDuration;
}
// [신규] hit 애니메이션 트리거 (생존 시만 — 사망 시는 dead Bool로 처리)
if (currentHP > 0)
{
var animator = GetComponent<Animator>();
if (animator != null) animator.SetTrigger("hit");
OnDamagedEvent?.Invoke(damage);
}
if (currentHP == 0)
{
var animator = GetComponent<Animator>();
if (animator != null) animator.SetBool("dead", true); // [신규] death State 진입
OnDeathEvent?.Invoke();
var ev = Schedule<HealthIsZero>();
ev.health = this;
}
}
```
### 7-3. 신규 메서드 `Resurrect()`
```csharp
/// <summary>
/// 부활 발동 — death 애니메이션 종료 후 외부(BT7-Plan 부활 룰 영역)에서 호출.
/// currentHP 복원 + Animator transition (death → resurrection → idle)
/// SOT: 캐릭터_리소스_규칙_v1.md §3.1.4 death → resurrection 시퀀스
/// </summary>
public void Resurrect()
{
if (IsAlive) return; // 이미 살아있으면 무시
currentHP = maxHP; // BT7-Plan 영역 — 부활 시 maxHP 회복 룰 (기획팀 영역 — §11 명시)
invulnerableUntil = -1f;
var animator = GetComponent<Animator>();
if (animator != null)
{
animator.SetBool("dead", false);
animator.SetTrigger("resurrect");
}
OnResurrectEvent?.Invoke();
}
```
### 7-4. 신규 이벤트 3종
```csharp
public event System.Action<int> OnDamagedEvent; // damage 인자
public event System.Action OnDeathEvent;
public event System.Action OnResurrectEvent;
```
### 7-5. Die() 보존 영역
`Die()`는 i-frame 우회 즉사 처리 (낙사·승리 이탈 등). animator 호출 영역도 `Decrement` 사망 분기와 동일하게 추가:
```csharp
public void Die()
{
invulnerableUntil = -1f;
if (currentHP > 0)
{
currentHP = 0;
var animator = GetComponent<Animator>();
if (animator != null) animator.SetBool("dead", true); // [신규]
OnDeathEvent?.Invoke();
var ev = Schedule<HealthIsZero>();
ev.health = this;
}
}
```
---
## §8. Player.prefab 변경
### 8-1. SpriteRenderer.m_Sprite 교체
| 영역 | 기존 | 신규 |
|------|------|------|
| `m_Sprite.fileID` | `7882920275377484039` (PlayerTestGirl_0) | C1_idle01의 internalID (Phase 2 시 meta Read로 추출) |
| `m_Sprite.guid` | `44ad58ba82191ca4d818108ab01d3baa` | C1_idle01의 guid `321722ca021d6dc4c9d9dc5b12811711`은 attack01 — idle01 별도 추출 영역 |
| `m_Color` | `(0.18, 0.89, 0.99, 1)` (cyan tint) | `(1, 1, 1, 1)` (정규 white — Hero1 원색 보존) |
### 8-2. 신규 컴포넌트 부착 (3종 MonoBehaviour 추가)
기존 `m_Component` 리스트 9건 → 12건 확장:
| # | fileID 후보 | 컴포넌트 | script guid (Phase 2 발급) |
|---|-----------|--------|---------------------------|
| 10 | `7700000000000000003` | `PlayerAttackTicker` (이미 존재 — 재실측 필요 — Phase 2 영역) | 기존 `Assets/Scripts/Gameplay/PlayerAttackTicker.cs.meta` guid |
| 11 | `7700000000000000004` | `PlayerStateTimer` (신설 §6) | 신규 발급 |
**기존 부착 컴포넌트 보존 (9건)**:
- Transform·KinematicObject(`PlayerController`)·SpriteRenderer·Animator·Rigidbody2D·AudioSource·Health·BoxCollider2D·AttackHitbox(fileID `7700000000000000001`)
**PlayerAttackTicker 부착 영역 검증 (Phase 2 영역)**: 기존 prefab YAML grep 결과 `PlayerAttackTicker` script guid 미부착 가능성 — Phase 2 시 `RequireComponent(typeof(PlayerController))`로 자동 부착 확인 또는 직접 MonoBehaviour 블록 추가.
### 8-3. Health 컴포넌트 maxHearts 보강
기존 prefab Health 직렬화: `maxHP: 1`만 명시. `maxHearts` 직렬화 누락 → Awake에서 1로 보정 (정상 동작).
**Phase 2 권고 영역**: `maxHearts: 1` 명시 직렬화 추가 (Inspector 가시성).
---
## §9. 기존 자산 처리
### 9-1. 백업 대상 (`_archive/` 이동)
| 원본 경로 | 이동 경로 | 처리 |
|---------|---------|------|
| `Assets/Character/Sprites/PlayerTestGirl.png` + .meta | `Assets/Character/Sprites/_archive/PlayerTestGirl.png` + .meta | 이동 (rename — Unity는 폴더 변경만 감지) |
| `Assets/Character/Sprites/PlayerIdle.png` + .meta | `Assets/Character/Sprites/_archive/PlayerIdle.png` + .meta | 이동 |
| `Assets/Character/Sprites/PlayerRun.png` + .meta | `Assets/Character/Sprites/_archive/PlayerRun.png` + .meta | 이동 |
| `Assets/Character/Sprites/PlayerJump.png` + .meta | `Assets/Character/Sprites/_archive/PlayerJump.png` + .meta | 이동 |
| `Assets/Character/Sprites/PlayerLand.png` + .meta | `Assets/Character/Sprites/_archive/PlayerLand.png` + .meta | 이동 |
| `Assets/Character/Sprites/PlayerHurt.png` + .meta | `Assets/Character/Sprites/_archive/PlayerHurt.png` + .meta | 이동 |
| `Assets/Character/Sprites/PlayerDeath.png` + .meta | `Assets/Character/Sprites/_archive/PlayerDeath.png` + .meta | 이동 |
| `Assets/Character/Sprites/PlayerSpawn.png` + .meta | `Assets/Character/Sprites/_archive/PlayerSpawn.png` + .meta | 이동 |
| `Assets/Character/Sprites/PlayerVictory.png` + .meta | `Assets/Character/Sprites/_archive/PlayerVictory.png` + .meta | 이동 |
**총 9 PNG + 9 meta = 18 파일 이동** (PD 결정 4 — `_archive/` 백업).
**guid 보존**: meta 파일 함께 이동 시 guid 변경 0. 향후 참조 추적 가능.
### 9-2. 폐기 anim 4건 (controller에서 제거 후 파일 자체는 보존)
| 파일 | 처리 |
|------|------|
| `Assets/Character/Animations/PlayerHurt.anim` + .meta | controller에서 참조 제거 (Player-Hurt State 폐기) — 파일 자체는 보존 (회귀 안전) |
| `Assets/Character/Animations/PlayerLand.anim` + .meta | controller에서 참조 제거 — 파일 보존 |
| `Assets/Character/Animations/PlayerSpawn.anim` + .meta | controller에서 참조 제거 — 파일 보존 |
| `Assets/Character/Animations/PlayerVictory.anim` + .meta | controller에서 참조 제거 — 파일 보존 |
**근거**: PD 결정 4 정합 — 자산 보존 우선. controller 참조 제거만으로 게임 영향 차단. 향후 필요 시 재참조 가능.
### 9-3. 백업 timestamp 표준 (재작성 직전)
`PlayerIdle.anim`·`PlayerRun.anim`·`PlayerAttack.anim`·`PlayerDeath.anim`·`PlayerJump.anim`·`Player.controller`·`Player.prefab` 7건 — Phase 2 집행 직전 §12 영역 백업.
---
## §10. EditMode 테스트 갱신
### 10-1. 기존 13건 분류 (PlayerAttackTests.cs 실측)
| # | 테스트명 | 처리 |
|---|---------|------|
| 1 | `Player_Prefab_Has_AttackHitbox_Component` | **유지** |
| 2 | `Player_Prefab_Has_Health_Component` | **유지** |
| 3 | `Player_Prefab_Has_PlayerController_Component` | **유지** |
| 4 | `AttackHitbox_Default_Damage_Is_One` | **유지** |
| 5 | `AttackHitbox_Active_Duration_Is_Positive` | **유지** |
| 6 | `Enemy_Prefab_Has_Health_Component` | **유지** |
| 7 | `Enemy_Prefab_Has_EnemyController_Component` | **유지** |
| 8 | `Health_Script_Defines_MaxHearts_And_IncreaseMaxHearts` | **유지** |
| 9 | `Player_Prefab_MaxHP_Reflects_Heart_Quarters` | **유지** |
| 10 | `PlayerAttackTicker_Script_Exists_With_AttackInterval` | **유지** |
| 11 | `InputActions_Player_Map_Has_No_Attack_Action` | **유지** |
| 12 | `Player_Prefab_SpriteRenderer_References_PlayerTestGirl` | **개정** — 신규 테스트 `Player_Prefab_SpriteRenderer_References_Hero1_Idle01`로 변경 (guid 추출 시 Hero1 idle01 guid 검증) |
| 13 | `Player_Controller_Has_Attack_Parameter_And_State` | **유지** (attack Trigger·Player-Attack State는 보존) |
### 10-2. 신규 6건 추가 (총 13 - 1(폐기 12번) + 1(신규 12번) + 6 = 19건)
| # | 신규 테스트명 | 검증 영역 |
|---|------------|---------|
| 14 | `Player_Has_8_Motion_States` | Player.controller YAML grep — `Player-Idle`·`Player-Run`·`Player-Attack`·`Player-Jump`·`Player-Death`·`Player-CombatIdle`·`Player-Hit`·`Player-Resurrection` 8 State 존재 검증 + `Player-Hurt`·`Player-Land`·`Player-Spawn`·`Player-Victory` 4 State 부재 검증 |
| 15 | `Player_Controller_Has_New_Parameters` | `hit`·`combatidle`·`resurrect` 3 Parameter 존재 검증 + `hurt`·`victory` 2 Parameter 부재 검증 |
| 16 | `Player_Hit_Interrupts_Protected` | Player.controller YAML — `Player-Hit` State의 `m_StateMachineBehaviours` 리스트 비어있지 않음 검증 + `m_CanTransitionToSelf: 0` 검증 (HitInterruptGuard 부착 검증) |
| 17 | `Player_CombatIdle_Timer_Component_Attached` | Player.prefab YAML grep — `PlayerStateTimer` script guid 부착 검증 + `combatIdleDuration: 5` 직렬화 검증 |
| 18 | `Player_Death_To_Resurrection_Sequence` | Player.controller YAML — `Player-Death → Player-Resurrection` Transition 존재 + `resurrect` 조건 검증 + `Player-Resurrection → Player-Idle` Transition 검증 |
| 19 | `Health_Has_Resurrect_Method_And_Events` | `Platformer.Mechanics.Health` reflection — `Resurrect()` 메서드 + `OnDamagedEvent`·`OnDeathEvent`·`OnResurrectEvent` event 3종 존재 검증 |
**최종 = 19 EditMode 테스트** (기존 13건 중 12번 개정 + 신규 6건 추가).
### 10-3. EditMode 테스트 추가·개정 영역 = `Assets/Tests/Editor/PlayerAttackTests.cs` Phase 2 Edit 대상
---
## §11. BT7-Plan·BT12-Dev 통합 영역
### 11-1. BT7-Plan 정합 (변경 0 영역)
| 자산 | 정합 검증 |
|------|---------|
| `PlayerAttackTicker.cs` (`attackInterval: 0.5s`) | **변경 0** — 기존 Schedule<PlayerAttack> 발화 그대로 |
| `PlayerAttack.cs` `Execute` 메서드 | `animator.SetTrigger("attack")`·`audioSource.PlayOneShot`·`attackHitbox.Fire` 보존 + **§6 §6-4 한 줄 추가** (`stateTimer.NotifyAttackFired()`) |
| `Health.maxHearts·QuartersPerHeart` | **변경 0** — 하트 분할 시스템 보존 |
| `Health.IncreaseMaxHearts(int)·Heal(int)·Decrement(int)·Die()·CurrentHP·IsAlive` | **변경 0** — 기존 API 보존 (§7 영역은 추가만) |
| `i-frame 0.6s` (`invulnerableDuration`·`invulnerableUntil`·`IsInvulnerable`) | **변경 0** — i-frame 보호 + §5 hit 인터럽트 보호는 별 layer (i-frame은 데미지 차단, hit 인터럽트는 애니메이션 보호) |
### 11-2. BT7-Plan 부활 시 maxHP 회복 룰 (기획팀 영역 명시)
`Resurrect()` 메서드는 `currentHP = maxHP` 복원 (전체 회복). 향후 BT7-Plan 또는 별건에서 부활 시 부분 회복 룰 결정 시 `Resurrect(int recoverQuarters)` 오버로드 추가 영역.
**Phase 2 영역**: 기획팀 결정 보류 시 우선 `currentHP = maxHP` 채택.
### 11-3. BT12-Dev 보류 영역 — 본 설계는 BT12-Dev 인터페이스 4종(`ISkillRuntime`·`IActiveSkill` 등) 미연관
`Assets/Scripts/EerieVillage/Skills/` 영역은 본 설계 범위 외. 향후 BT12-Dev 통합 시:
- `ISkillRuntime.OnAttackHit` 같은 hook이 PlayerAttack.Execute에서 발화 가능 (현 설계 변경 없음)
- `PassiveSkillRuntime``Health.IncreaseMaxHearts` 호출 (현 설계 변경 없음)
**재검토 영역**: BT12-Dev Phase 2 진입 시 본 §6 `PlayerStateTimer.combatIdleDuration` 카드 효과 보정 영역 추가 가능.
---
## §12. C6-1 백업 영역
### 12-1. 백업 timestamp 표준
`Phase 2 집행 직전 timestamp` 형식: `YYYYMMDD_HHMM` (예: `20260507_2200`).
### 12-2. 백업 대상 N종 (총 12종)
| # | 원본 | 백업명 |
|---|------|--------|
| 1 | `Assets/Character/Animations/Player.controller` | `Player.controller.bak_<timestamp>.controller` |
| 2 | `Assets/Character/Animations/PlayerIdle.anim` | `PlayerIdle.anim.bak_<timestamp>.anim` |
| 3 | `Assets/Character/Animations/PlayerRun.anim` | `PlayerRun.anim.bak_<timestamp>.anim` |
| 4 | `Assets/Character/Animations/PlayerAttack.anim` | `PlayerAttack.anim.bak_<timestamp>.anim` |
| 5 | `Assets/Character/Animations/PlayerJump.anim` | `PlayerJump.anim.bak_<timestamp>.anim` |
| 6 | `Assets/Character/Animations/PlayerDeath.anim` | `PlayerDeath.anim.bak_<timestamp>.anim` |
| 7 | `Assets/Prefabs/Player.prefab` | `Player.prefab.bak_<timestamp>.prefab` |
| 8 | `Assets/Scripts/Mechanics/Health.cs` | `Health.cs.bak_<timestamp>.cs` |
| 9 | `Assets/Tests/Editor/PlayerAttackTests.cs` | `PlayerAttackTests.cs.bak_<timestamp>.cs` |
| 10 | `Assets/Character/Sprites/Hero1/C1_combat idle01.png` (+ meta) | rename 직전 백업 (`_archive/` 영역) |
| 11 | `Assets/Character/Sprites/Hero1/C1_combat idle02~04.png` (+ meta) | rename 직전 백업 |
| 12 | 기존 Player*.png 9종 + meta 9건 (§9-1) | `_archive/` 이동 = 백업 자체 |
### 12-3. 백업 위치 표준
- Unity asset 영역: 같은 폴더 내 `.bak_<timestamp>.<ext>` 형식 (기존 BT5-Dev 정합)
- BT 레포 영역 (P25 Live 증분 동기화 비대상이지만 SOT 보호): `공유/개발팀_백업/EerieVillage/<timestamp>/` 디렉토리 별도 보존 (Phase 2 영역)
### 12-4. 롤백 절차
`bak_<timestamp>` suffix 제거 → 원본 파일명으로 덮어쓰기. `_archive/` 이동 자산은 원위치로 mv.
---
## §13. 기각안 (5건 이상 — C32 정합)
### 13-1. sprite slice 전략 — 기각 2안 (§2-1)
- **B안 (atlas pack 단일 sheet)** — Unity SpriteAtlas auto-pack은 빌드 단계 적용 가능하므로 자산 변경 없이 차후 도입 가능. 본 Phase 1 자산 발주 0 원칙에서 제외
- **C안 (모션별 sprite sheet 8개)** — 자산 재발주 필요 + 현 자산 폐기 = PD 결정 자산 우선 활용 위배
### 13-2. hit 인터럽트 보호 — 기각 1안 (§5-1)
- **B안 (Health.cs Trigger 진입 가드)** — Health.cs가 Animator API 참조 의존 신규 발생 + Animator 미부착 객체(Enemy 향후) 영역 일반화 어려움. SOT 명시 영역 = StateMachineBehaviour로 채택
### 13-3. idle⇄combatidle 5초 타이머 — 기각 1안 (§6-1)
- **A안 (PlayerController.cs 확장)** — PlayerController 책임 비대화 (이동·점프·공격 facing·전투 타이머 4축). C11 단일 책임 위배. BT7-Plan PlayerAttackTicker 별도 컴포넌트 패턴 정합 위배
### 13-4. 부활 시스템 — 기각 2안
| 기각안 | 근거 |
|--------|------|
| **이벤트 미발화 (직접 Health.Resurrect 호출만)** | UI·SE·다른 시스템(BT12-Dev SkillRuntime)이 부활 시점 hook 필요 시 polling 의존 → C# event 패턴 정착 |
| **Resurrect를 자동 발동 (death 종료 후 자동 호출)** | 부활 룰(BT7-Plan 영역)이 항상 충족되지 않을 수 있음. 외부 룰 검증 후 명시적 호출 = 단일 책임 |
### 13-5. jump 단일 프레임 영역 — 기각 1안
| 기각안 | 근거 |
|--------|------|
| **PlayerJump.anim duration = 0 (즉시 종료 transition)** | Unity Animator는 duration 0 anim에서 m_PPtrCurves 시각 영향 미적용 가능. duration 0.1s + LoopTime 0으로 1프레임 표시 보장. 시각 안정성 우선 |
### 13-6. EditMode 테스트 — 기각 1안
| 기각안 | 근거 |
|--------|------|
| **PlayMode 테스트로 통합 검증** | PlayMode는 Animator·Physics·InputSystem 런타임 의존. Prefab/controller YAML 직렬화 상태는 EditMode AssetDatabase로 충분 + 회귀 즉시 검출 (이전 BT5-Dev 정합) |
---
## §14. 후속 안건
### 14-1. Phase 2 클라이언트팀 위임 영역 분해 (Sonnet 작업)
| 작업 단위 | 영역 | 산출 |
|---------|------|------|
| **W1. sprite rename + meta 갱신 4건** | `Assets/Character/Sprites/Hero1/C1_combat idle01~04.png` + .meta | rename + meta 내부 name 필드 갱신 |
| **W2. anim 8건 신설/재작성** | `Assets/Character/Animations/Player{Idle,Run,Attack,CombatIdle,Jump,Hit,Resurrection,Death}.anim` (+ 신규 4건 .meta) | YAML 작성 (각 PNG meta에서 internalID·guid 추출) |
| **W3. Player.controller 전면 재설계** | `Assets/Character/Animations/Player.controller` | YAML 재작성 (8 State + 8 Parameter + Transition 매트릭스 + StateMachineBehaviour 부착) |
| **W4. HitInterruptGuard.cs 신설** | `Assets/Scripts/Mechanics/HitInterruptGuard.cs` (+ .meta) | C# 신규 |
| **W5. PlayerStateTimer.cs 신설** | `Assets/Scripts/Mechanics/PlayerStateTimer.cs` (+ .meta) | C# 신규 |
| **W6. Health.cs 확장** | `Assets/Scripts/Mechanics/Health.cs` | Decrement(int) 확장 + Resurrect() + 이벤트 3종 + Die() 확장 |
| **W7. PlayerAttack.cs 확장 (1행)** | `Assets/Scripts/Gameplay/PlayerAttack.cs` | NotifyAttackFired() 호출 추가 |
| **W8. Player.prefab 변경** | `Assets/Prefabs/Player.prefab` | m_Sprite 교체 + m_Color 교체 + PlayerStateTimer 부착 + PlayerAttackTicker 부착 검증 + maxHearts 직렬화 |
| **W9. 기존 자산 9종 _archive/ 이동** | §9-1 영역 | mv (Unity 자동 import 처리) |
| **W10. EditMode 테스트 갱신** | `Assets/Tests/Editor/PlayerAttackTests.cs` | 12번 개정 + 신규 6건 추가 |
| **W11. C6-1 백업 12종** | §12-2 영역 | 백업 timestamp 일관 |
| **W12. 매니페스트 신규 등록** | `scripts/manifest_register.sh` | Phase 2 Edit 영역 N건 등록 |
**Phase 2 토큰 추정**: ~150-200K (W1~W12 일괄). C50 사전 PD 승인 필요.
### 14-2. Phase 3 검증 항목 (개발팀장 — 별건)
| # | 검증 영역 | 방식 |
|---|---------|------|
| 1 | EditMode 19 테스트 모두 PASS | Unity Test Runner |
| 2 | controller YAML 정합 (8 State + 8 Parameter + Transition 매트릭스) | grep 검증 |
| 3 | sprite guid 충돌 0 | grep 검증 |
| 4 | anim guid 충돌 0 | grep 검증 |
| 5 | Health.cs API 보존 (BT7-Plan 정합) | reflection 테스트 |
| 6 | PlayerAttackTicker 영역 변경 0 (BT7-Plan) | diff 검증 |
| 7 | _archive/ 이동 9종 + 폐기 anim 4종 controller 미참조 | grep 검증 |
### 14-3. PD Play 검증 항목
| # | 검증 영역 | 기대 동작 |
|---|---------|---------|
| 1 | 캐릭터 등장 시 idle 4프레임 loop 재생 | C1_idle01~04 순환 |
| 2 | 이동 시 run 8프레임 loop 재생 | C1_run01~08 순환 |
| 3 | 0.5초 주기 attack 8프레임 non-loop 재생 + AttackHitbox Fire | C1_attack01~08 1회 + 적 데미지 |
| 4 | 공격 후 5초 이내 정지 시 combatidle 4프레임 loop 재생 | C1_combatidle01~04 순환 |
| 5 | 5초 경과 후 idle 4프레임 loop 전환 | C1_idle01~04 순환 |
| 6 | 적 충돌 시 hit 2프레임 non-loop + i-frame 0.6s | C1_hit01~02 + 중복 피격 차단 |
| 7 | 점프 입력 시 jump 1프레임 정적 표시 | C1_jump01 표시 |
| 8 | HP 0 시 death 2프레임 non-loop 재생 | C1_death01~02 + 마지막 프레임 정지 |
| 9 | Resurrect() 호출 시 resurrection 8프레임 non-loop → idle 전환 | C1_resurrection01~08 + idle 복귀 |
| 10 | hit 진행 중 추가 데미지 → hit 재시작 차단 (StateMachineBehaviour 보호) | 1회 hit 사이클 완주 |
### 14-4. 외부 의존 영역 (기획팀·PD 결정 보류)
- **부활 룰**: BT7-Plan 또는 별건 영역. 본 설계는 `Resurrect()` API만 제공. 외부 룰 검증 후 호출 결정
- **카드·특성 효과로 combatIdleDuration 변동**: BT12-Dev Phase 2 진입 시 PlayerStateTimer 노출 영역 확장 가능
- **Land·Spawn·Victory 미사용 영역**: PD 결정 5 정합. 향후 부활 시 PD 결정 + SOT 개정 (`캐릭터_리소스_규칙_v1.md` v1 → v1.1)
---
## 변경 이력
| 일시 | 버전 | 내용 | 기안 |
|------|------|------|------|
| 2026-05-07 | v1.0 | Phase 1 설계 단독 완료 (BT5-Dev Hero1 캐릭터 교체 — 14 섹션 표준 + 기각 5+ 건 + Phase 2 작업 단위 분해 12건) | 개발팀장 (Opus) |

View File

@ -0,0 +1,247 @@
# 캐릭터 리소스 규칙 v1 — 영구 SOT
> **목적**: EerieVillage 플레이어 캐릭터 자산 네이밍 표준 + 모션 종류 + 상태 전환 룰 영구 SOT
> **작성**: 총괄PM (2026-05-07 BT5-Dev Hero1 신규 지시 수령 시점)
> **PD 지시 근거**: 2026-05-07 BT5-Dev Hero1 캐릭터 교체 지시 + 리소스 규칙 7종 직접 명세 + 이슈 1·2 결정 (공백 제거·death 8종 확장)
> **적용 범위**: 플레이어 캐릭터(C1~) 전 자산 — 신규 캐릭터 추가 시 동일 표준 의무
---
## 1. 네이밍 표준
### 1.1 형식
```
<캐릭터번호>_<모션><프레임>.png
```
### 1.2 구성 요소
| 요소 | 표기 | 설명 |
|------|------|------|
| 캐릭터 번호 | `C1`·`C2`·`C3`... | 캐릭터 식별자. 1번부터 순차 증가 |
| 구분자 | `_` (언더스코어) | 캐릭터 번호와 모션 사이 단일 구분자 |
| 모션 | `<motion>` (소문자, 공백 없음) | 본 문서 §2 모션 카탈로그 8종 중 1개 |
| 프레임 | `01`·`02`...·`08`... | 2자리 zero-padded 정수. 연속 프레임만큼 |
| 확장자 | `.png` | 무손실 압축 + 알파 채널 지원 |
### 1.3 예시
- `C1_attack01.png` — 캐릭터 1번 공격 모션 1프레임
- `C1_combatidle04.png` — 캐릭터 1번 전투 대기 4프레임 (공백 미허용)
- `C2_resurrection01.png` — 캐릭터 2번 부활 1프레임 (향후 추가)
### 1.4 금지 사항
- 공백 포함 (예: `C1_combat idle01.png`) — Unity asset 일관성 + 파싱 안정성
- 대문자 모션 (예: `C1_Attack01.png`) — 케이스 정합성
- 0 padding 누락 (예: `C1_attack1.png`) — 정렬 안정성
---
## 2. 모션 카탈로그 (8종)
### 2.1 모션 정의
| # | 모션 키 | 한글 명 | 발동 조건 | Loop | 재생 시 다음 상태 |
|---|--------|--------|---------|------|----------------|
| 1 | `attack` | 공격 | 자동 발동 (공격 사이클 도래) | non-loop | 마지막 공격 시간 갱신 → 5초 카운트다운 시작 |
| 2 | `combatidle` | 전투 대기 | 공격 후 정지 + 마지막 공격 < 5초 | loop | 마지막 공격 5초 idle 전환 |
| 3 | `idle` | 일반 대기 | 정지 + 마지막 공격 ≥ 5초 (또는 attack 미발동) | loop | run·attack·hit·jump·resurrection 트리거 시 전환 |
| 4 | `hit` | 피격 | 적 데미지 수신 | non-loop · **인터럽트 보호** | 종료 후 직전 상태 복귀 (idle/combatidle/run/jump) |
| 5 | `jump` | 점프 | 점프 입력 + 공중 상태 | static (1 frame) | 착지 시 idle 또는 run 전환 |
| 6 | `resurrection` | 부활 | death 종료 후 부활 게임 룰 충족 시 | non-loop | 종료 후 idle 전환 |
| 7 | `run` | 이동 | 이동 입력 + 지면 상태 | loop | 정지 시 마지막 공격 시간 기준 idle 또는 combatidle 전환 |
| 8 | `death` | 사망 | HP 0 도달 | non-loop | resurrection 가능 시 대기·불가 시 게임 오버 |
### 2.2 모션 매핑 (Hero1 — C1)
| 모션 | 프레임 수 | 파일명 범위 |
|------|---------|-----------|
| attack | 8 | `C1_attack01~08.png` |
| combatidle | 4 | `C1_combatidle01~04.png` (rename 후) |
| death | 2 | `C1_death01~02.png` |
| hit | 2 | `C1_hit01~02.png` |
| idle | 4 | `C1_idle01~04.png` |
| jump | 1 | `C1_jump01.png` |
| resurrection | 8 | `C1_resurrection01~08.png` |
| run | 8 | `C1_run01~08.png` |
| **합계** | **37** | — |
---
## 3. 상태 전환 룰
### 3.1 핵심 메커니즘
#### 3.1.1 idle ⇄ combatidle 5초 타이머
```
[run 또는 attack 종료] → 마지막 공격 시간 t_attack 갱신
[정지 상태 진입]
if (현재시각 - t_attack) < 5초:
→ combatidle (loop)
else:
→ idle (loop)
[combatidle loop 중 (현재시각 - t_attack) ≥ 5초 도달]
→ idle (loop) 전환
```
#### 3.1.2 hit 인터럽트 보호 (PD 명세 핵심)
```
[hit 발동 중 추가 데미지 수신]
if hit.isPlaying == true:
→ 현 hit 애니메이션 끝까지 재생 (재시작 X)
else:
→ hit 애니메이션 재생 시작
```
**Unity 구현 영역**: Animator State에서 `m_HasExitTime: 1` + `m_ExitTime: 1` 설정으로 자기 자신으로의 재진입 차단. 또는 Animator Behaviour `StateMachineBehaviour.OnStateEnter`에서 `IsInTransition` 체크.
**Health.cs 영역**: 데미지 수신 시 `hit` Trigger 호출 전 `currentState == "hit"` 검증 후 진입.
#### 3.1.3 attack 자동 발동 사이클
BT7-Plan 확정안 = VS 순수형 자동 발동 (공격 버튼 제거). `PlayerAttackTicker.cs` 주기 타이머가 attack Trigger 호출.
#### 3.1.4 death → resurrection 시퀀스
```
[HP 0 도달]
→ death 애니메이션 재생 (non-loop)
[death 애니메이션 종료]
→ 부활 룰 검증 (게임 룰 영역 — BT7-Plan 또는 별건)
if 부활 가능:
→ resurrection 애니메이션 재생 (non-loop) → idle 전환
else:
→ 게임 오버 (death 마지막 프레임 정지)
```
#### 3.1.5 jump 단일 프레임 정합
```
[점프 입력]
→ jump 자세 표시 (1 frame static)
[공중 → 지면 착지]
→ 이동 입력 유무에 따라 idle/run 전환
```
낙하·착지 별도 모션 없음. 점프 자세 1프레임 유지.
### 3.2 상태 전환 다이어그램 (요약)
```
[run]
↑↓ (이동 입력)
[idle] ⇄ (5초 타이머) ⇄ [combatidle]
↑ ↑
└── (resurrection) ←────┘
[death]
(HP=0)
전 상태에서 발동 가능:
- [hit] (인터럽트 보호 — 재진입 차단)
- [attack] (자동 발동 사이클)
- [jump] (점프 입력 시)
```
---
## 4. Unity 자산 구조 표준
### 4.1 폴더 구조
```
Assets/Character/
├── Sprites/
│ ├── Hero1/ ← C1 (현 캐릭터)
│ │ └── C1_*.png + .meta
│ └── _archive/ ← 폐기 자산 보존 (롤백 안전망)
│ └── PlayerTestGirl.png 등 9종
├── Animations/
│ ├── PlayerIdle.anim ← idle (Hero1 sprite 0~3 loop)
│ ├── PlayerRun.anim ← run (Hero1 sprite 0~7 loop)
│ ├── PlayerAttack.anim ← attack (Hero1 sprite 0~7 non-loop)
│ ├── PlayerCombatIdle.anim ← combatidle (Hero1 sprite 0~3 loop) [신설]
│ ├── PlayerHit.anim ← hit (Hero1 sprite 0~1 non-loop) [신설]
│ ├── PlayerJump.anim ← jump (Hero1 sprite 0 static) [신설]
│ ├── PlayerResurrection.anim ← resurrection (Hero1 sprite 0~7 non-loop) [신설]
│ ├── PlayerDeath.anim ← death (Hero1 sprite 0~1 non-loop) [신설]
│ └── Player.controller ← Animator Controller (전면 재설계)
└── ...
```
### 4.2 Animator Controller 파라미터
| 파라미터 | 타입 | 용도 |
|---------|------|------|
| `Speed` | Float | 이동 속도 (idle ⇄ run 전환) |
| `Grounded` | Bool | 지면 상태 (jump 진입 차단) |
| `attack` | Trigger | 공격 발동 |
| `hit` | Trigger | 피격 발동 (인터럽트 보호 — Animator Behaviour 영역) |
| `combatidle` | Bool | 5초 타이머 영역 (true = combatidle, false = idle) |
| `dead` | Bool | 사망 상태 (death State 진입) |
| `resurrect` | Trigger | 부활 발동 (resurrection State 진입) |
| `jump` | Trigger | 점프 발동 |
---
## 5. 구현 영역 위임
### 5.1 본 SOT 적용 의무 영역
- 개발팀: Animator Controller 재설계 · Health.cs 부활 + hit 인터럽트 보호 · PlayerAttackTicker.cs 주기 타이머 · combatidle 5초 타이머 영역 (신규 컴포넌트 또는 PlayerController.cs 확장)
- 기획팀: 부활 게임 룰 (BT7-Plan 또는 별건 영역) · 모션 추가 발주 결정 (Land·Spawn·Victory 미사용 확정 = PD 결정 5)
### 5.2 본 SOT 갱신 영역
- 신규 캐릭터 추가 시 (C2·C3...) §2.2 모션 매핑 표 갱신
- 모션 추가·폐기 시 PD 결정 + 본 SOT 개정 (v1 → v1.1 등)
- 상태 전환 룰 변경 시 PD 결정 + §3 갱신
---
## 6. 관련 규칙·자산
- **PD 지시 근거**: 2026-05-07 BT5-Dev Hero1 신규 지시 + 이슈 1·2·3·4·5 A안 일괄 채택
- **C32 결정·근거·기각안**: 본 SOT 신설 자체가 PD 명세 직접 적용 — 별도 기각안 없음. 이슈 5건 기각안은 §7 참조
- **C37 규칙 문서 관리**: 영구 SOT (단일 위치 — 중복 금지)
- **C39 작업 전 실측 의무**: Hero1 폴더 37 PNG 실측 완료 (2026-05-07 본 PM)
- **연관 SOT**:
- `프로젝트/EerieVillage/기획/04_전투_기본_스펙.md` (i-frame 0.6s 등 전투 룰)
- `프로젝트/EerieVillage/개발/spec/스킬_시스템_설계_v1.md` (BT12-Dev 보류 영역)
- `프로젝트/EerieVillage/개발/05_PlayerTestGirl_아틀라스_적용.md` (폐기 영역 — 본 SOT가 대체)
---
## 7. 기각안 (이슈 5건 PD 결정 후 기각된 옵션)
### 7.1 이슈 1 — 파일명 공백
- **기각안 B** — PD 명세 정정 (공백 허용) → Unity asset 공백 미허용 일반 관행 + 파싱 안정성 저해. PD 결정 A 채택 (rename)
### 7.2 이슈 2 — death 자산 처리
- **기각안 B** — death 폐기 → resurrection만 사용 시 사망→부활 시퀀스 불완전. PD 결정 A 채택 (8종 확장)
- **기각안 C** — death = resurrection 첫 2프레임 흡수 → 자산 의도 분리 위배 (death = 사망, resurrection = 부활)
### 7.3 이슈 3 — jump 1프레임
- **기각안 B** — 추가 프레임 발주 (점프 상승·정점·낙하 분리) → 현 자산 정합 + 간소함 우선. PD 결정 A 채택
### 7.4 이슈 4 — 기존 Player*.png 9종
- **기각안 B** — 즉시 삭제 → 롤백 안전망 상실. PD 결정 A 채택 (`_archive/` 백업)
- **기각안 C** — Hero1 미커버 영역(Land·Spawn·Victory)만 보존 → 부분 보존 일관성 저해
### 7.5 이슈 5 — Land·Spawn·Victory 미사용
- **기각안 B** — resurrection 자산 일부 재활용 (Spawn 매핑) → 의미 혼동
- **기각안 C** — PD 추가 자산 발주 → 현 시점 우선순위 외
---
## 8. 변경 이력
| 일자 | 버전 | 내용 |
|------|------|------|
| 2026-05-07 | v1 | 신설 — BT5-Dev Hero1 신규 지시 수령 + 이슈 5건 PD 결정 일괄 채택 + 모션 8종 영구 SOT |