---
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 정합 — 2026-05-07 정정)

### 12-1. 본 작업 영역 백업 정책 = 0건 (git 추적 영역)

본 작업 변경 대상 = `Assets/Character/Animations/`·`Assets/Prefabs/`·`Assets/Scripts/Mechanics/`·`Assets/Tests/Editor/`·`Assets/Character/Sprites/Hero1/` 전 영역.

이 영역은 **`D:/EerieVillage` 별도 git 레포 + GitAutoSync 자동 push** 추적 영역. 모든 파일·전 history가 git에 영구 보존.

→ **C6-1 백업 의무 영역 외** = 별도 백업 0건 정합. 롤백 안전망은 git이 100% 제공.

**근거**: `feedback_unity_backup_compile_pollution.md` (2026-05-07 신설) — Unity Assets/ 영역 백업 = git 영역과 100% 중복 + `.cs.bak_*.cs` 패턴 Unity 컴파일 대상 인식 → CS0101 직접 원인.

### 12-2. C6-1 백업 의무 영역 (참고 — 본 작업 영역 외)

C6-1 본래 적용 영역 = git이 추적하지 못하는 영역만:
- 밸런싱 xlsm·CSV·JSON 테이블 (binary 또는 git 추적 외)
- 프로덕션 DB 덤프
- `.gitignore` 영역 자산

본 작업은 위 영역 변경 0건 = C6-1 적용 대상 0건.

### 12-3. 롤백 절차 (git checkout)

```bash
# 단일 파일 롤백 (예: Health.cs)
cd D:/EerieVillage
git log -- Assets/Scripts/Mechanics/Health.cs    # 이전 commit hash 확인
git checkout <hash> -- Assets/Scripts/Mechanics/Health.cs

# 전체 영역 롤백 (Phase 2 직전 시점)
git checkout <Phase 2 직전 hash> -- Assets/Character/ Assets/Scripts/Mechanics/Health.cs Assets/Tests/Editor/PlayerAttackTests.cs Assets/Prefabs/Player.prefab
```

### 12-4. `_archive/` 영역 (§9-1 정합 — 백업과 별건)

`Assets/Character/Sprites/_archive/` = 폐기 자산 격리 영역 (Hero1 미커버 영역의 기존 Player*.png 9종 + meta 9건). 본 영역은 **폐기 자산 명시적 격리** 목적 (백업이 아님). git 영역 안 보존이므로 동일 정책 정합.

---

## §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) |
