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