Update README.md

This commit is contained in:
2026-06-29 11:02:55 +00:00
parent 9efb08fdf3
commit 8de6aa25ac

View File

@@ -1,34 +1,31 @@
# MTM 인게임 유닛 설계 — 사전 과제
챔피언 / 미니언 / 타워 유닛과, **유닛과 분리된 HP 표시(HPBar)** 구조를 설계했습니다.
요구사항대로 **구조 설계 중심**이며, 세부 수치/로직은 의도가 드러나는 스텁(`=> 0`, 주석 등)으로 남겼습니다.
# 사전 과제
## 파일 구성
| 파일 | 역할 |
| --- | --- |
| `Core/ComponentBase.cs` | Unity `MonoBehaviour`**엔진 비종속 대역**. 생명주기(`Awake/Start/Update/OnDestroy`) + `Destroy()` |
| `Core/IHealthView.cs` | HP**'보여주는' 쪽이 보는 읽기 전용 계약**. `CurrentHP/MaxHP/IsDead` + `HPChanged`/`Died` 이벤트 |
| `Core/Health.cs` | HP를 **'작동'시키는 코어**. `IHealthView` 구현, `TakeDamage`/`Revive`, 사망·변경 통지 |
| `Battle/BattleManager.cs` | 게임 진행 시간(`CurrentGameSeconds`), 미니언 스폰 (제공 원본 확장) |
| `Battle/AttackType.cs` | 공격 타입 enum (물리/마법) |
| `Units/Unit.cs` | 모든 유닛의 추상 베이스. `Health` 소유, `IHealthView` 노출, 데미지/사망 공통 흐름 |
| `Core/ComponentBase.cs` | Unity `MonoBehaviour`대역 |
| `Core/IHealthView.cs` | HP등을 읽을 수 있는 인터페이스 |
| `Core/Health.cs` | HP를 구현한 클래스. `IHealthView` 구현, `TakeDamage(피격데미지)`/`Revive(부활)`|
| `Battle/BattleManager.cs` | 게임 진행 시간(`CurrentGameSeconds`) |
| `Battle/AttackType.cs` | 공격 타입 |
| `Units/Unit.cs` | 모든 유닛의 추상 클래스ㅓ. `Health` 소유, `IHealthView` 노출 |
| `Units/Champion.cs` | 죽으면 **일정 시간 후 부활** |
| `Units/Minion.cs` | 죽으면 **소멸**, 생성 시 게임 시간 비례 공격력 |
| `Units/Tower.cs` | 죽으면 **영구 파괴**(잔해는 남음) |
| `UI/HPBar.cs` | 임의 대상의 HP를 **'보여주는'** UI. `IHealthView`구독 (제공 원본 확장) |
| `UI/HPBar.cs` | 임의 대상의 HP를 **'보여주는'** UI. `IHealthView`참조 |
## 과제 (1) — 유닛 3종
공통점(HP 소유·데미지·사망 흐름)은 `Unit`에 모으고, **유닛마다 다른 "죽으면 무엇을 하는가"는 `OnDied()` 추상 메서드로 분리**했습니다(Template Method). 초기화는 `Awake → Init()(자식이 스탯 세팅) → Health 생성` 순서를 부모가 보장합니다.
공통점(HP 소유·데미지·사망 흐름)은 `Unit`에 모으고, 초기화는 `Awake → Init()
| 유닛 | 요구사항 | 구현 |
| --- | --- | --- |
| **Champion** | 죽으면 일정 시간 후 부활 | `OnDied`에서 `_reviveAt` 예약 → `Update`에서 `CurrentGameSeconds` 도달 시 `Revive()`(= `Health.Revive()` + 부활 지점 스폰 훅). 부활 후 `IsDead=false`라 다음 죽음에 재예약되어 사이클 반복 |
| **Champion** | 죽으면 일정 시간 후 부활 `Revive()`|
| **Minion** | 죽으면 소멸 / 생성 시 시간 비례 공격력 | `OnDied → Destroy()`(오브젝트 제거). `Init`에서 `GetAtkByGameTime(BattleManager.CurrentGameSeconds)`로 공격력 세팅 |
| **Tower** | 죽으면 영구 파괴 | `OnDied`에서 `IsDestroyed = true` + 기능 정지/잔해 전환. **`Destroy()`를 부르지 않아** 오브젝트(흔적)는 남고, 부활 로직이 없어 영구 파괴 |
| **Tower** | 영구 파괴 여부를 저장 | `OnDied` 기능 정지/잔해 전환. 오브젝트(흔적)는 남고, 부활 로직이 없어 영구 파괴 |
## 과제 (2) — '보여주는' 객체와 '작동하는' 코어의 분리
## 과제 (2) — 분리
### 의존 방향을 한쪽으로만 (유닛 → HPBar 의존: 0)
@@ -39,21 +36,18 @@
의존 방향: UI → Core, Units → Core, Units → Battle
```
- **작동(코어) = `Health`** : 데미지/회복/사망을 실제로 처리하는 단일 진실 공급원. UI를 전혀 모르며, 상태 변화는 `HPChanged`/`Died` **이벤트로만**립니다(Observer).
- **표시 = `HPBar`** : `IHealthView`(읽기 전용)만 구독합니다. `IHealthView``TakeDamage`가 없어 **UI가 모델을 변경할 수 없습니다**(읽기/표시 책임만).
- **연결** : 유닛이 `HealthView` 프로퍼티로 `IHealthView`를 노출하고, 제3자(스포너/매니저 등)가 `hpBar.Bind(unit.HealthView)`로 연결합니다. 유닛은 HPBar를 전혀 참조하지 않습니다.
- **작동(코어) = `Health`** : 데미지/회복/사망을 실제로 처리하는 클래스. UI를 전혀 모르며, 상태 변화는 `HPChanged`/`Died` **이벤트로만** 알.
- **표시 = `HPBar`** : `IHealthView`(읽기 전용)만 참조.
- **연결** : 유닛이 프로퍼티로 `IHealthView`를 노출하고, 유닛은 HPBar를 전혀 참조하지 않습니다.
### 이점
- **양방향 무의존** — 유닛은 UI를, UI는 구체 유닛을 모릅니다.
- **임의 대상 반영** — `IHealthView`만 만족하면 무엇이든 표시 가능(유닛 외 오브젝트도 동일).
- **항상 최신 상태** — 폴링이 아닌 이벤트 푸시. `Bind` 시 1회 즉시 반영, 이후 변화 시점에만 갱신. `OnDestroy`에서 구독 해제로 누수 방지.
- **양방향 무의존** — 유닛은 UI를, UI는 구체 유닛을 모
- **임의 대상 반영** — `IHealthView`만 만족하면 무엇이든 표시 가능.
- **항상 최신 상태** — `Bind` 시 1회 즉시 반영, 이후 변화 시점에만 갱신.
## 설계 결정 / 트레이드오프
- **`ComponentBase`** : `UnityEngine` 의존 없이 어디서나 컴파일/리뷰되도록 둔 `MonoBehaviour` 대역입니다. 생명주기 이름을 동일하게 미러링해, 실제 Unity로 옮길 때는 `ComponentBase``MonoBehaviour`로 교체하면 그대로 이어집니다.
- **`AttackType` 파라미터** : 현재 구현은 `Atk`를 그대로 반환하지만, **물리/마법 등 데미지 타입별 분기로 확장**할 것을 대비해 `GetAttackDamage(AttackType)` 시그니처에 포함했습니다.
- **조립(composition) 계층 생략** : 누가 `Awake/Update`를 구동하고 `HPBar.Bind`를 호출하는지(스포너/매니저)는 본 **설계 테스트의 범위를 벗어나 생략**했습니다. 위 구조상 외부에서 한 줄로 조립 가능합니다.
- **스탯은 외부 주입 전제** : `MaxHP`/`Atk` 등 실제 수치는 데이터 테이블 등 **외부에서 주입**하는 것을 전제로 하며, 본 코드에서는 스텁(0)으로 두었습니다.
## 특이사항
- **`AttackType`** : **물리/마법 등 데미지 타입별 분기로 확장**할 것을 고려.
- **스탯은 외부 주입 전제** : `MaxHP`/`Atk` 등 실제 수치는 **외부에서 주입**하는 것을 전제.
## 적용 패턴
- **Template Method** — `Init` / `OnDied` / `GetAttackDamage`로 유닛별 차이만 분리