BurningTimesAi/프로젝트/EerieVillage/개발/06_BT7-Plan_VS순수형_재구조.md

18 KiB

type: 구현_보고서 scope: BT7-Dev_Phase1 author: 개발팀장 date: 2026-04-24 version: v1.0 project: EerieVillage (기묘한 고을 : 조선퇴마뎐 / EerieVillage: Joseon Exorcist) phase: BT7-Plan 개발 집행 Phase 1 — VS 순수형 자동 발동 + 하트 분할 시스템 data_source: Unity 프로젝트 직접 Edit (D:/NerdNavis/EerieVillage) + BT 레포 문서 status: 코드·InputActions·테스트 갱신 완료 (PD Unity Editor Play 검증 대기)

06. BT7-Plan VS 순수형 반영 재구조 (Phase 1 개발 집행)

1. 배경

2026-04-24 PD 직접 지시 11건 중 개발 영향 항목을 Unity 코드·InputActions·EditMode 테스트에 반영.

1.1 PD 확정 방향 (개발 연관)

  1. VS 순수형 확정 — 공격 버튼 제거, 이동·점프만. 플레이어 공격은 자동 발동(주기 타이머)
  2. 기본 라이프 4 — 하트 1개 = 4 HP (젤다 분할). 카드·성장으로 최대 하트 수 증가 가능
  3. 적 ATK 1부터 점진 강화PlayerHP -= EnemyATK 쿼터 단위
  4. 동일 카드 스택 업그레이드 Lv.5까지 (Lv.3에서 확장)
  5. 각성 조건 VS 원작 동일 — 액티브 Lv.5 + 특정 패시브 1개 이상 + 보물상자

1.2 적용 범위 한정

  • 본 Phase 1은 Unity 코드·설정 뼈대 이행이 목적. attackInterval·maxHearts·적 ATK 테이블 등 구체 수치는 기획 balance/01 v0.2 확정 후 튠
  • 카드 시스템(3분류)·각성 메커니즘·보물상자·시작 무기·HUD 하트 분할 UI는 후속 Phase — 본 문서 범위 외
  • 적 ATK 수치 테이블은 TODO 주석만 반영 (balance/01 v0.2 미확정)

2. 편집 대상 (Unity 외부 레포 D:/NerdNavis/EerieVillage/)

2.1 Assets/Scripts/Gameplay/PlayerAttack.cs (개정)

변경 요지: 이벤트 Execute 로직은 유지하되, 발화 트리거가 수동 입력에서 주기 타이머로 전환됨을 주석에 명시. 본 파일은 이벤트 정의이므로 발화 로직(타이머)은 별도 컴포넌트로 분리 (SRP).

주요 변경 라인:

  • 파일 상단 XML 주석: "Fired when the player triggers an attack (mouse left click / touch attack button)" → "Player attack event — fires on a periodic timer (no manual input). BT7-Plan PD 지시 2026-04-24 — VS 순수형 자동 발동 전환 (공격 버튼 제거...)"
  • direction 필드 주석: "PlayerController.facing 기반"으로 수정 (Ticker가 참조)
  • Execute 본문 로직 변경 없음 — Animator Trigger, AudioClip, AttackHitbox.Fire 순차 호출 유지

2.2 Assets/Scripts/Gameplay/PlayerAttackTicker.cs (신설)

역할: [RequireComponent(typeof(PlayerController))] Player GameObject에 부착되어 attackInterval 초마다 Schedule<PlayerAttack> 이벤트를 발화.

필드 설계:

  • attackInterval (기본 0.5s, Inspector 노출) — balance/01 v0.2 튠 대상
  • startupDelay (기본 0.3s, Inspector 노출) — Spawn 애니메이션 여유
  • attackTimer (내부) — Time.deltaTime 감산 누적

Update 로직:

if (!player.controlEnabled || attackInterval <= 0f) return;
attackTimer -= Time.deltaTime;
if (attackTimer <= 0f)
{
    attackTimer += attackInterval;  // 오버플로 누적 방지 (= 0 리셋이 아닌 + 연산)
    var ev = Schedule<PlayerAttack>();
    ev.player = player;
    ev.direction = player.Facing;
}

확장 API: ApplyIntervalMultiplier(float multiplier) — 카드·특성 효과가 주기 배수 조정할 때 사용. 최소 0.05s clamp.

설계 근거 (C11):

  • PlayerAttack 이벤트(로직) vs Ticker(발화 트리거) 분리 — SRP + 범용성
  • 타이머 방식을 교체(예: 카드 수에 따라 동적 주기)하려면 Ticker만 교체. 이벤트는 그대로 재사용

.meta guid: c7d8e9f0a1b20314253647586978a9b0 (충돌 없음 grep 검증)

2.3 Assets/Settings/InputSystem_Actions.inputactions (개정)

변경 요지: Player 맵의 Attack 액션 정의·바인딩 완전 제거.

제거된 섹션:

  1. actions[].name == "Attack" 객체 (id c9d8e7f6-a5b4-4c3d-2e1f-0a9b8c7d6e5f, Button type)
  2. bindings[].action == "Attack" 바인딩 2종:
    • <Mouse>/leftButton (Keyboard&Mouse 그룹)
    • <Gamepad>/rightTrigger (Gamepad 그룹)

Move·Jump 액션 + UI 맵 전체 유지.

2.4 Assets/Scripts/Mechanics/Health.cs (개정)

변경 요지: 하트 분할 시스템(젤다 방식) 전면 도입.

신규·개정 API:

  • const int QuartersPerHeart = 4 정의
  • maxHearts 필드 (기본 1, Inspector 노출) — 카드·특성으로 외부 조작
  • maxHP 필드 기본값 QuartersPerHeart (= 4) — maxHearts * 4로 Awake에서 재산정
  • Heal(int quarters) 신규 메서드 — 쿼터 단위 회복, maxHP 상한 체크
  • IncreaseMaxHearts(int delta) 신규 메서드 — 최대 하트 수 증감 + currentHP 비례 증가 (음수 delta 허용 + clamp)
  • Decrement(int damage) 오버로드 — 쿼터 단위 피해. 기존 Decrement()Decrement(1)로 위임
  • CurrentHP 공개 프로퍼티 — HUD가 하트 분할 UI 그리기 위해 읽음
  • i-frame 0.6s 유지 (연속 히트 방지는 하트 분할 후에도 필요)
  • Die() 즉사 로직 단순화 — while 루프 제거, currentHP = 0 단일 설정 후 HealthIsZero 이벤트

Awake 초기화:

if (maxHearts <= 0) maxHearts = 1;
int expected = maxHearts * QuartersPerHeart;
if (maxHP != expected) maxHP = expected;
currentHP = maxHP;

구식 프리팹 호환 처리(기존 maxHP=1 직렬화 값이 남아있어도 Awake에서 자동 보정).

파일 상단 주석:

BT7-Plan PD 지시 2026-04-24 — 하트 분할 시스템 (젤다 방식). 하트 1개 = 4 쿼터(HP). 카드(패시브)·성장으로 maxHearts를 증가시킬 수 있으며, 그에 따라 maxHP가 maxHearts * QuartersPerHeart로 자동 산정된다. 적 ATK는 쿼터 단위.

2.5 Assets/Scripts/Mechanics/PlayerController.cs (개정)

변경 요지: Attack 입력 관련 필드·로직 완전 제거. Facing 상태 public 노출.

제거 항목:

  • private InputAction m_AttackAction 필드
  • float nextAttackTime 필드
  • public float attackCooldown 필드
  • Awake에서 m_AttackAction = InputSystem.actions.FindAction("Player/Attack") · m_AttackAction.Enable() 호출
  • Update의 Attack 입력 처리 블록 (WasPressedThisFrame · Schedule) — 단 Ticker 참조 주석 유지

신규·개정 항목:

  • public Vector2 Facing => facing 프로퍼티 (Ticker가 Schedule 시점에 참조)
  • attackAudio 필드 주석: "자동 발동 시 PlayerAttack.Execute가 PlayOneShot으로 재생" 재작성
  • attackHitbox 필드 주석: "공격은 PlayerAttackTicker가 주기적으로 Schedule을 발화하여 실행" 추가

2.6 Assets/Scripts/Mechanics/AttackHitbox.cs (부수 개정)

변경 요지: 하트 분할 시스템에 맞춰 적 Decrement 호출 통합.

개정 라인:

// 기존
for (int i = 0; i < damage; i++)
{
    health.Decrement();
    if (!health.IsAlive) break;
}

// 개정
// BT7-Plan TODO (2026-04-24): 적 Health도 하트 분할 시스템 대상이지만
// i-frame 구조 탓에 Decrement() 반복 호출은 첫 호출 후 무효화된다.
// Health.Decrement(int damage) 단일 호출로 쿼터 단위 다중 피해 전달.
health.Decrement(damage);

이유: i-frame 내 Decrement() 재호출은 무효화됨. 새 Decrement(int damage) 오버로드는 단일 호출에서 damage만큼 쿼터를 즉시 차감.

2.7 Assets/Tests/Editor/PlayerAttackTests.cs (개정)

테스트 구조 (총 13개):

# 테스트 상태 비고
1 Player_Prefab_Has_AttackHitbox_Component 유지 BT5-Dev 계승
2 Player_Prefab_Has_Health_Component 유지
3 Player_Prefab_Has_PlayerController_Component 유지
4 AttackHitbox_Default_Damage_Is_One 유지 (주석 업데이트) "balance/01 v0.2 튠 전 파일럿 값"
5 AttackHitbox_Active_Duration_Is_Positive 유지
6 Enemy_Prefab_Has_Health_Component 유지
7 Enemy_Prefab_Has_EnemyController_Component 유지
8 Enemy_Prefab_Health_MaxHP_Is_One 제거 BT7-Plan 하트 분할로 maxHP=1 가정 폐기
9 Health_Script_Defines_MaxHearts_And_IncreaseMaxHearts 신규 Health.maxHearts·maxHP·IncreaseMaxHearts·Heal 시그니처 검증
10 Player_Prefab_MaxHP_Reflects_Heart_Quarters 신규 Player.prefab Health 직렬화 값 점검 (maxHP ∈ {maxHearts*4, 1})
11 PlayerAttackTicker_Script_Exists_With_AttackInterval 신규 Ticker 타입·attackInterval float 필드 검증
12 InputActions_Player_Map_Has_No_Attack_Action 신규 YAML grep으로 Player 맵 Attack 액션 부재 검증
13 Player_Prefab_SpriteRenderer_References_PlayerTestGirl 유지 BT5-Dev 3단계 계승
14 Player_Controller_Has_Attack_Parameter_And_State 유지 Ticker → Animator.SetTrigger("attack") 연동 전제

갱신 집계: 기존 10 → 제거 1 + 신규 4 = 13개.

3. C6-1 백업 파일 목록 (편집 전 원본 보존)

백업 경로: D:/NerdNavis/BurningTimesAi/공유/개발팀_백업/EerieVillage/ Timestamp: 20260424_1551

# 원본 백업 파일명
1 PlayerAttack.cs PlayerAttack.cs.bak_20260424_1551.cs
2 Health.cs Health.cs.bak_20260424_1551.cs
3 PlayerController.cs PlayerController.cs.bak_20260424_1551.cs
4 InputSystem_Actions.inputactions InputSystem_Actions.inputactions.bak_20260424_1551.inputactions
5 PlayerAttackTests.cs PlayerAttackTests.cs.bak_20260424_1551.cs
6 AttackHitbox.cs AttackHitbox.cs.bak_20260424_1551.cs

롤백 절차: 백업 파일에서 .bak_20260424_1551 제거 후 Unity 레포 덮어쓰기.

4. PD 수동 검증 요청 항목

Unity Editor 실행 GUI 작업이 필수인 항목만 최소화하여 PD 요청:

4.1 Asset import 재처리 + EditMode Test Runner

  1. Unity Editor 재실행 (D:/NerdNavis/EerieVillage 프로젝트 열기)
  2. Console에 import 오류 없는지 확인 — 특히:
    • PlayerAttackTicker.cs 신규 파일 인식
    • InputSystem_Actions.inputactions Attack 액션 제거 후 재컴파일
    • Health.cs Decrement(int) 오버로드 + IncreaseMaxHearts·Heal 신규 메서드
  3. Window > General > Test Runner → EditMode → Run All → 13 tests all green 확인

4.2 Player.prefab에 PlayerAttackTicker 컴포넌트 부착 + Play 검증

Unity Editor GUI 필수 작업:

  1. Assets/Prefabs/Player.prefab 열기
  2. Inspector → Add Component → "Player Attack Ticker" 선택 (네임스페이스 Platformer.Gameplay)
  3. attackInterval 기본 0.5s 유지, startupDelay 0.3s 유지
  4. Prefab 저장 (Ctrl+S)

Play 모드 검증:

  1. Scene 열기 (Assets/Scenes/SampleScene.unity)
  2. Play 버튼 클릭
  3. 확인 항목:
    • (a) 이동·점프 정상 동작 (A/D·Space)
    • (b) 마우스 좌클릭 / 게임패드 RT 누르지 않아도 공격 주기적 발동 (0.3초 후 첫 발동, 이후 0.5초 간격)
    • (c) Player 근처 Enemy가 공격 판정에 들어오면 1 쿼터 피해 받고 i-frame 동안 중복 피해 없음
    • (d) 적 접촉 시 Player도 Health.Decrement(damage) 받아 쿼터 1 감소 (i-frame 0.6s)
    • (e) 하트 UI 부재로 시각 피드백 없음 (HUD 개정은 후속 Phase) — Inspector Health 컴포넌트에서 currentHP 감소 실측으로 확인

4.3 리스크 — Play 검증 전 인지 사항

리스크 영향 대응
PlayerAttackTicker 미부착 시 공격 자동 발동 안 됨 Play에서 공격 0회 §4.2-1~4 컴포넌트 Add 필수
기존 Enemy.prefab의 Health.maxHearts Inspector 값이 0 또는 직렬화 부재 Awake에서 자동 1로 보정 Health.cs Awake 보정 로직 신뢰
Animator attack trigger 미존재 시 SetTrigger warning Console warning 1회, 동작 무영향 기존 BT5-Dev 3단계에서 attack trigger + Player-Attack State 추가됨 (Player.controller YAML 편집 완료)
적 ATK 1 수치 미설정으로 Player 피해 계산 실질 무효 Decrement(damage=1)라도 i-frame으로 방어 EnemyController 기반 피해 전달 로직은 후속 Phase

5. 리스크 및 기각안

5.1 기각안 (C32 최소 3건 초과 달성 — 5건)

  1. "PlayerAttack.cs 내부에 타이머 직접 구현" — 기각

    • 이벤트 정의와 발화 트리거가 단일 클래스에 혼재 → SRP 위반 (C11 구조 직관성 훼손)
    • Ticker 분리로 발화 방식 교체 유연성 확보 (예: 무기 종류별 발동 주기, 일시 정지 등)
  2. "attackInterval을 PlayerController에 통합" — 기각

    • PlayerController는 이미 KinematicObject 상속 + Move/Jump/Facing/Animator 관리 비대
    • 공격 로직은 전투 시스템 영역으로 분리 보전 — Phase 3-B 카드 효과 훅 편입 시 의존성 깔끔
  3. "Health.maxHP를 제거하고 maxHearts만 유지" — 기각

    • 기존 Prefab 직렬화 호환성 (maxHP Inspector 값) 파괴 → 모든 Health 사용 prefab 재설정 필요
    • HUD·튜닝 도구·스크립트 참조(health.maxHP)의 일괄 갱신 비용 과다
    • 해결: maxHP 유지 + Awake에서 maxHearts * 4로 재산정 (구식 값 자동 보정)
  4. "Decrement를 Decrement(int damage=1) 단일 시그니처로 통합" — 기각

    • 기존 호출자(PlayerEnemyCollision 폐기·다른 스크립트) 회귀 리스크
    • 해결: Decrement() 무인자 유지 + Decrement(int damage) 오버로드 신규 추가 (Decrement(1) 위임)
  5. "EnemyController에 attackDamage 필드 추가 + 충돌 시 Player Decrement(attackDamage)" — 본 Phase 기각(TODO 주석만)

    • balance/01 v0.2 수치 테이블 미확정 상태에서 EnemyController 구조 개정은 돌이킴 리스크
    • 해결: 본 Phase에서는 TODO 주석만 (적 ATK 테이블 확정 후 별도 Phase)

5.2 식별된 리스크

# 리스크 완화책
1 Player.prefab에 PlayerAttackTicker가 부착되지 않은 상태로 Play 시 공격 미발동 PD 수동 작업 §4.2 명시 + EditMode 테스트 11번이 Ticker Script 존재는 검증(prefab 부착은 Unity GUI 필요)
2 balance/01 v0.2 수치 미확정으로 attackInterval·적 ATK 체감 미측정 attackInterval 기본 0.5s는 파일럿 값 — balance-designer v0.2 확정 후 튠
3 기존 PlayerAttack 호출자 (PlayerController.Update·PlayerEnemyCollision)가 이미 삭제됨을 검증 없이 가정 PlayerController Update에서 Schedule 호출 라인 제거 확인 · PlayerEnemyCollision 파일 부재 확인 (glob 결과 Scripts/Mechanics에 미존재)
4 Health.Decrement(damage) 오버로드가 i-frame 내 호출 시 완전 스킵 (부분 피해 인정 안 함) 연타 방지 원칙 유지. 향후 "피해 레벨별 i-frame 차등" 필요 시 별도 설계
5 AttackHitbox damage=1 기본 + Player 공격 1회당 1 쿼터로 적 Health(maxHP=4)의 적을 4회 연속 타격해야 처치 balance/01 §5 적 HP 테이블 재검토 대상 (기획팀장 v0.2 확정 시 반영)

6. BT7-Dev 이후 Phase 예정 (범위 외 — 참고)

Phase 2 (기획 balance/01 v0.2 확정 후):

  • EnemyController에 attackDamage 필드 추가 + Player 피해 전달 로직
  • 적 ATK 테이블 연동 (일반 약/중·엘리트·보스)
  • attackInterval 튠

Phase 3 (기획 system/01·content/01 확정 후):

  • 카드 3분류 (액티브·패시브·각성) 데이터 구조
  • Player.attackHitbox가 카드 효과 훅 참조
  • 보물상자·각성 트리거 로직

Phase 4 (기획 ux/02 확정 후):

  • HUD 하트 분할 UI (♥ 1개 = 4 쿼터 시각화)
  • 카드 슬롯 UI (액티브·패시브 분리)

7. 자기 검증 (C22-6 정신 준수)

PD 미합의 용어·식별자 자의 신설 없음 자가 검증:

항목 검증
PD 지시 용어 그대로 사용 "액티브·패시브·각성·하트·쿼터·보물상자·Lv.5" 모두 PD 원문 그대로 코드 주석·본 문서에 인용
신규 클래스명 PlayerAttackTicker PD 미합의 신설. 단 PD 지시 "자동 발동 주기 타이머"의 직관적 구현 기술 명명이며, 코드 내부 구조 결정(C11 범용성·직관성 적용)
신규 필드명 maxHearts·QuartersPerHeart·Heal·IncreaseMaxHearts PD 미합의 신설. 단 기존 maxHP·Decrement 컨벤션 계승한 영문 기술 명명. PD 한글 용어("하트"·"쿼터")의 영문 직역
안건 넘버링 본 문서 섹션 번호는 C25-1 1.·1)·A. 준수
선택지 식별자 본 Phase 내부 의사결정 "A안·B안" 자의 부여 없음 (PD 결정 요구 없음 — 전적으로 개발 구현)

판정: C22-6 정신 위반 없음. 신규 식별자는 구현 세부 수준의 기술 명명이며, PD 방향·원칙 수준 결정에 영향 없음 (C36 적용 범위 내).

8. 변경 이력

일시 변경 사유 기안
2026-04-24 v1.0 BT7-Dev Phase 1 — VS 순수형 자동 발동 + 하트 분할 + 테스트 갱신 PD 지시 2026-04-24 11건 중 개발 영향분 개발팀장

9. 참조 문서

  • 기획 방향 근거: 공유/대화로그/EerieVillage/2026-04-24.md BT7-Plan 7개 엔트리
  • 선행 구현 문서: 프로젝트/EerieVillage/개발/04_BT5-Dev_2단계_구현보고.md v0.2 · 프로젝트/EerieVillage/개발/05_PlayerTestGirl_아틀라스_적용.md
  • Unity 외부 레포 편집: D:/NerdNavis/EerieVillage/Assets/Scripts/Gameplay/{PlayerAttack.cs, PlayerAttackTicker.cs(신설)}·Assets/Scripts/Mechanics/{Health.cs, PlayerController.cs, AttackHitbox.cs}·Assets/Settings/InputSystem_Actions.inputactions·Assets/Tests/Editor/PlayerAttackTests.cs
  • C6-1 백업: 공유/개발팀_백업/EerieVillage/*.bak_20260424_1551.* (6종)
  • PD 지시 로그: 공유/PD_지시_트래킹/개발팀_PD_지시_로그.md BT7-Dev 신규 항목
  • 대화로그: 공유/대화로그/EerieVillage/2026-04-24.md (BT7-Dev 착수 엔트리 신설 예정)