1075 lines
53 KiB
Markdown
1075 lines
53 KiB
Markdown
|
|
---
|
||
|
|
type: 설계_문서
|
||
|
|
scope: 스킬_시스템
|
||
|
|
author: 개발팀장
|
||
|
|
date: 2026-04-24
|
||
|
|
version: v1.0 (BT12-Dev Phase 1 개발팀장 Opus 설계)
|
||
|
|
project: EerieVillage (기묘한 고을 : 조선퇴마뎐 / EerieVillage: Joseon Exorcist)
|
||
|
|
phase: BT12-Dev Phase 1 — 개발팀장 설계
|
||
|
|
data_source: 프로젝트/EerieVillage/기획/content/02_스킬_효과_컨셉.md v0.2 · 프로젝트/EerieVillage/기획/content/02_스킬_효과_컨셉_v0.3.csv · 프로젝트/EerieVillage/기획/system/01_카드_시스템.md v0.2 · 프로젝트/EerieVillage/기획/balance/01_전투_수치.md v0.2 · 프로젝트/EerieVillage/기획/04_전투_기본_스펙.md v0.2 · 프로젝트/EerieVillage/기획/content/01_카드_풀.md v0.2 · 프로젝트/EerieVillage/개발/06_BT7-Plan_VS순수형_재구조.md v1.0
|
||
|
|
status: Phase 1 설계 완료 — Phase 2 클라이언트팀 구현 위임 대기 · Phase 3 개발팀장 검증 대기
|
||
|
|
---
|
||
|
|
|
||
|
|
# 스킬 시스템 설계 v1
|
||
|
|
|
||
|
|
## §1. 아키텍처 개요
|
||
|
|
|
||
|
|
### 1-1. 설계 목표 (C11 개발 관점 3축)
|
||
|
|
|
||
|
|
1. **자원 효율성**: 60종 스킬을 런타임에 동시 처리해도 GC 알로케이션·파티클 상한·물리 연산 부담이 모바일 60fps 허용 범위 내 유지
|
||
|
|
2. **코드 구조 직관성**: 스킬 데이터·런타임 인스턴스·효과 발동 3계층이 명확히 분리되어 다음 개발자가 "이 스킬은 어디서 정의되고 어떻게 발동되는가"를 5분 내 파악 가능
|
||
|
|
3. **범용성**: 카테고리·패턴 설계로 60종 중 49종이 공통 런타임 클래스 3~4종으로 커버되며, 신규 카드 추가 시 ScriptableObject 인스턴스 생성만으로 확장 가능
|
||
|
|
|
||
|
|
### 1-2. 재미 축 정합 (기획서 §1 인용)
|
||
|
|
|
||
|
|
- **축 1 — 육성 롤러코스터의 카타르시스**: 각성 발동 3조건 동시 충족 구조가 "쌓고 폭발" 감정 곡선 형성
|
||
|
|
- **축 2 — 자동 발동 감상 (VS 순수형)**: `PlayerAttackTicker` 패턴 확장으로 모든 액티브가 주기·이벤트 트리거 자동 발동
|
||
|
|
- **축 3 — 영속 성장의 위안 (보조)**: 패시브 스탯 누적·최대 하트 증가가 런 내 성장 감각 제공
|
||
|
|
|
||
|
|
### 1-3. 4계층 아키텍처
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────┐
|
||
|
|
│ 계층 4: 효과 발동 (기존 BT7-Dev 자산 재활용) │
|
||
|
|
│ - AttackHitbox (근접·범위) │
|
||
|
|
│ - 신설: ProjectileSpawner (투사체) │
|
||
|
|
│ - 신설: AuraZone (설치·지속) │
|
||
|
|
│ - 신설: MinionSpawner (소환) │
|
||
|
|
│ - 신설: DebuffApplier (상태이상) │
|
||
|
|
│ - Health.Decrement(int damage) 호출 │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
▲ 호출
|
||
|
|
┌─────────────────────────────────────────────────┐
|
||
|
|
│ 계층 3: 스킬 런타임 (SkillRuntime 컴포넌트) │
|
||
|
|
│ - ActiveSkillRuntime (Cooldown·OnHit·OnKill) │
|
||
|
|
│ - PassiveSkillRuntime (상시·조건부) │
|
||
|
|
│ - AwakeningSkillRuntime (진화 후 재활용) │
|
||
|
|
│ - 각 카드당 1 인스턴스. PlayerSkillInventory 관리 │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
▲ 참조
|
||
|
|
┌─────────────────────────────────────────────────┐
|
||
|
|
│ 계층 2: 스킬 데이터 (SkillDataAsset · ScriptableObj)│
|
||
|
|
│ - ActiveSkillData : 카테고리·주기·판정·대미지 │
|
||
|
|
│ - PassiveSkillData : 카테고리·스탯 보정·조건 │
|
||
|
|
│ - AwakeningSkillData : 원_액티브·필요_패시브[]·변형│
|
||
|
|
│ - 60종 .asset 파일 (CSV → 변환 파이프라인) │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
▲ 변환 입력
|
||
|
|
┌─────────────────────────────────────────────────┐
|
||
|
|
│ 계층 1: 정적 SOT (CSV v0.3 60종) │
|
||
|
|
│ - 02_스킬_효과_컨셉_v0.3.csv │
|
||
|
|
│ - 기획팀 편집 → 에디터 툴로 SkillDataAsset 재생성 │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### 1-4. 기존 BT7-Dev 자산 활용 (C39 시스템 반영 실측)
|
||
|
|
|
||
|
|
본 설계는 BT7-Dev Phase 1 산출물(`06_BT7-Plan_VS순수형_재구조.md` v1.0)을 **완전 계승**한다. 중복 구현 금지 (C10 중복 작업 방지).
|
||
|
|
|
||
|
|
| BT7-Dev 자산 | 본 설계 활용 방식 |
|
||
|
|
|-------------|----------------|
|
||
|
|
| `Platformer.Gameplay.PlayerAttack : Simulation.Event` | 그대로 사용. 기본 무기 발동은 본 이벤트 유지. 스킬별 효과는 별도 이벤트 확장 |
|
||
|
|
| `Platformer.Gameplay.PlayerAttackTicker` | 기본 무기 주기 타이머로 유지. **본 설계의 `ActiveSkillRuntime`은 독립 Cooldown 관리** (슬롯별 개별 주기 필요) |
|
||
|
|
| `Platformer.Mechanics.AttackHitbox` (근접 판정) | 카테고리 B(근접·범위) 스킬의 효과 발동기로 재활용. `AttackHitbox.Fire(direction)` 호출만으로 판정 생성 가능 |
|
||
|
|
| `Platformer.Mechanics.Health`: `maxHearts`·`QuartersPerHeart`·`Decrement(int damage)`·`Heal(int quarters)`·`IncreaseMaxHearts(int delta)`·`CurrentHP`·`IsAlive` | 적 피해 전달 API. P-C 생존 강화형(P12·P13)은 `IncreaseMaxHearts(1)` 호출. P-D 회복형(P16·P17)은 `Heal(1)` 호출. 계약 변경 없음 |
|
||
|
|
| `Platformer.Mechanics.PlayerController.Facing` (public Vector2) | 투사체 발사 방향·근접 판정 방향 기준값 |
|
||
|
|
| `Platformer.Core.Simulation.Schedule<T>()` | 스킬 효과 발동 시 이벤트 Dispatch 패턴 유지 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §2. 인터페이스 정의 (C# 코드 블록)
|
||
|
|
|
||
|
|
### 2-1. 공통 런타임 인터페이스
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
namespace EerieVillage.Skills
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 모든 스킬 런타임의 공통 계약.
|
||
|
|
/// PlayerSkillInventory가 보유 스킬을 이 인터페이스 배열로 관리.
|
||
|
|
/// </summary>
|
||
|
|
public interface ISkillRuntime
|
||
|
|
{
|
||
|
|
/// <summary>참조하는 정적 데이터 (ScriptableObject)</summary>
|
||
|
|
SkillDataAsset Data { get; }
|
||
|
|
|
||
|
|
/// <summary>현재 스택 레벨 (1~5). 5 도달 시 각성 조건 1 충족</summary>
|
||
|
|
int StackLevel { get; }
|
||
|
|
|
||
|
|
/// <summary>스킬 장착·Lv 업 시 1회 호출 (런타임 초기화)</summary>
|
||
|
|
void OnEquip(PlayerSkillInventory inventory);
|
||
|
|
|
||
|
|
/// <summary>스킬 해제 시 1회 호출 (각성 대체 등)</summary>
|
||
|
|
void OnUnequip();
|
||
|
|
|
||
|
|
/// <summary>동일 카드 재픽으로 Lv N→N+1 업그레이드</summary>
|
||
|
|
void Upgrade();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 액티브 스킬 런타임 계약. 주기·이벤트 트리거로 효과 발동.
|
||
|
|
/// </summary>
|
||
|
|
public interface IActiveSkill : ISkillRuntime
|
||
|
|
{
|
||
|
|
/// <summary>기본 쿨다운(초). balance/01 v0.2 `BaseCooldown` 1.5s 기반</summary>
|
||
|
|
float BaseCooldown { get; }
|
||
|
|
|
||
|
|
/// <summary>패시브 보정 후 실제 쿨다운 (연사술 P06 적용 결과)</summary>
|
||
|
|
float EffectiveCooldown { get; }
|
||
|
|
|
||
|
|
/// <summary>현재 Cooldown 타이머(초). MonoBehaviour Update가 감산</summary>
|
||
|
|
float CooldownRemaining { get; }
|
||
|
|
|
||
|
|
/// <summary>쿨다운 경과 또는 트리거 이벤트 시 호출. 효과 발동 Dispatch</summary>
|
||
|
|
void Fire();
|
||
|
|
|
||
|
|
/// <summary>트리거 이벤트 구독 — OnHit·OnKill·OnTime 중 해당</summary>
|
||
|
|
ActiveTrigger Trigger { get; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 패시브 스킬 런타임 계약. 장착 즉시 상시 적용 + 일부는 조건부 트리거.
|
||
|
|
/// </summary>
|
||
|
|
public interface IPassiveSkill : ISkillRuntime
|
||
|
|
{
|
||
|
|
/// <summary>상시 적용 여부 (true = 장착 즉시 효과 · false = 조건부)</summary>
|
||
|
|
bool IsAlwaysOn { get; }
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 장착·해제·Lv 업 시 인벤토리의 PlayerStats에 보정값 반영.
|
||
|
|
/// 스탯 상승형(P-A)·주기 단축(P-B)·생존 강화(P-C)·자원 확장(P-E)은 모두 이 경로.
|
||
|
|
/// </summary>
|
||
|
|
void ApplyModifier(PlayerStats stats);
|
||
|
|
|
||
|
|
/// <summary>해제 시 보정값 원복</summary>
|
||
|
|
void RemoveModifier(PlayerStats stats);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 조건부 패시브(P11 호신부·P16 단전수련 등) 전용.
|
||
|
|
/// OnPlayerDamaged·OnEnemyKilled·OnTimer 이벤트 구독 후 조건 충족 시 효과 발동.
|
||
|
|
/// </summary>
|
||
|
|
void OnTrigger(PassiveTriggerContext ctx);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 각성 스킬 런타임 계약. 원 액티브를 대체(또는 강화)하여 진화.
|
||
|
|
/// </summary>
|
||
|
|
public interface IAwakeningSkill : ISkillRuntime
|
||
|
|
{
|
||
|
|
/// <summary>진화 대상 원 액티브 데이터</summary>
|
||
|
|
ActiveSkillData OriginalActive { get; }
|
||
|
|
|
||
|
|
/// <summary>필요 패시브 후보 (1개 이상 보유로 조건 충족)</summary>
|
||
|
|
PassiveSkillData[] RequiredPassives { get; }
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 각성 발동 시 1회 호출. 원 액티브 슬롯 점유 유지하며 진화 형태로 대체.
|
||
|
|
/// 진화 패턴 4종 중 하나의 효과를 적용.
|
||
|
|
/// </summary>
|
||
|
|
void Awaken(PlayerSkillInventory inventory);
|
||
|
|
|
||
|
|
/// <summary>진화 패턴 (1 스케일업 · 2 새효과 · 3 다중 발동 · 4 광역 확산)</summary>
|
||
|
|
AwakeningPattern Pattern { get; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>액티브 발동 트리거 분류 — 카테고리·카드에 따라 결정</summary>
|
||
|
|
public enum ActiveTrigger
|
||
|
|
{
|
||
|
|
OnTime, // 주기 타이머 (카테고리 A·B·C·D·E 기본)
|
||
|
|
OnHit, // 플레이어 피격 시 (카테고리 F·P11 호신부 등)
|
||
|
|
OnKill // 적 처치 시 (P16 단전수련 등 패시브도 동일 enum 재사용)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>각성 진화 패턴 (기획서 §4-2 4종)</summary>
|
||
|
|
public enum AwakeningPattern
|
||
|
|
{
|
||
|
|
ScaleUp, // 1. 대미지·범위·속도 대폭 증가
|
||
|
|
AddEffect, // 2. 새 효과 추가 (기존 유지 + 부가)
|
||
|
|
MultiFire, // 3. 발동 수 2배+
|
||
|
|
GlobalSpread // 4. 화면 전체 확산
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>패시브 조건부 트리거 컨텍스트</summary>
|
||
|
|
public struct PassiveTriggerContext
|
||
|
|
{
|
||
|
|
public PassiveTriggerKind Kind;
|
||
|
|
public float DamageTaken; // OnPlayerDamaged 전용
|
||
|
|
public int KillCount; // OnEnemyKilled 전용 (누적)
|
||
|
|
public float TimeElapsed; // OnTimer 전용
|
||
|
|
}
|
||
|
|
|
||
|
|
public enum PassiveTriggerKind
|
||
|
|
{
|
||
|
|
OnPlayerDamaged,
|
||
|
|
OnEnemyKilled,
|
||
|
|
OnTimer
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2-2. ScriptableObject 데이터 계약
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
namespace EerieVillage.Skills
|
||
|
|
{
|
||
|
|
/// <summary>모든 스킬 데이터 asset의 공통 base</summary>
|
||
|
|
public abstract class SkillDataAsset : ScriptableObject
|
||
|
|
{
|
||
|
|
[Header("공통")]
|
||
|
|
public string CardId; // "A01"·"P12"·"AW19" (CSV v0.3 ID 컬럼)
|
||
|
|
public string DisplayName; // "진언부(眞言符)"·"Magic Arrow"
|
||
|
|
public string EnglishName; // 영문명 (CSV 2번째 컬럼)
|
||
|
|
public Sprite Icon; // UI용 아이콘
|
||
|
|
[TextArea(2, 4)] public string Description; // 플레이어 표시 툴팁
|
||
|
|
public AttributeTag[] AttributeTags; // [물리]·[화염] 등 (Flags enum)
|
||
|
|
public TypeTag[] TypeTags; // [근접]·[원거리] 등 (Flags enum)
|
||
|
|
}
|
||
|
|
|
||
|
|
[CreateAssetMenu(fileName = "Active_", menuName = "EerieVillage/Skills/Active")]
|
||
|
|
public class ActiveSkillData : SkillDataAsset
|
||
|
|
{
|
||
|
|
[Header("액티브 전용")]
|
||
|
|
public ActiveCategory Category; // A·B·C·D·E·F
|
||
|
|
public ActiveTrigger Trigger; // OnTime·OnHit·OnKill
|
||
|
|
[Tooltip("Lv.1 기본 쿨다운 (초). balance/01 v0.2 `BaseCooldown` 1.5s 참조")]
|
||
|
|
public float BaseCooldown = 1.5f;
|
||
|
|
[Tooltip("Lv.1 기본 대미지 (쿼터 단위). balance/01 v0.2 `BaseDamage` 10 참조")]
|
||
|
|
public int BaseDamage = 10;
|
||
|
|
[Tooltip("판정 박스 기본 크기 (A·B 카테고리). balance/01 v0.2 `AttackBoxSize` 1.5x1.0 참조")]
|
||
|
|
public Vector2 HitboxSize = new Vector2(1.5f, 1.0f);
|
||
|
|
[Tooltip("투사체 전용 (A 카테고리). 궤적 타입 — Line·Homing·Arc 중 1")]
|
||
|
|
public ProjectileTrajectory Trajectory = ProjectileTrajectory.Line;
|
||
|
|
[Tooltip("소환 전용 (D 카테고리). 소환물 프리팹 참조")]
|
||
|
|
public GameObject MinionPrefab;
|
||
|
|
[Tooltip("상태이상 전용 (E 카테고리). 스택 한도")]
|
||
|
|
public int DebuffStackLimit = 3;
|
||
|
|
[Tooltip("확률 판정 전용 (F 카테고리). 발동 확률 0~1")]
|
||
|
|
[Range(0f, 1f)] public float FireProbability = 0.2f;
|
||
|
|
}
|
||
|
|
|
||
|
|
[CreateAssetMenu(fileName = "Passive_", menuName = "EerieVillage/Skills/Passive")]
|
||
|
|
public class PassiveSkillData : SkillDataAsset
|
||
|
|
{
|
||
|
|
[Header("패시브 전용")]
|
||
|
|
public PassiveCategory Category; // P-A·P-B·P-C·P-D·P-E
|
||
|
|
public bool IsAlwaysOn = true;
|
||
|
|
[Tooltip("대상 스탯 (P-A·P-C 전용)")]
|
||
|
|
public StatType TargetStat;
|
||
|
|
[Tooltip("Lv.1 기본 보정값 (대미지 배율·하트 수·확률 등)")]
|
||
|
|
public float BaseModifierValue;
|
||
|
|
[Tooltip("조건부 패시브 (P11·P16·P17 등) 트리거 종류")]
|
||
|
|
public PassiveTriggerKind TriggerKind;
|
||
|
|
}
|
||
|
|
|
||
|
|
[CreateAssetMenu(fileName = "Awakening_", menuName = "EerieVillage/Skills/Awakening")]
|
||
|
|
public class AwakeningSkillData : SkillDataAsset
|
||
|
|
{
|
||
|
|
[Header("각성 전용")]
|
||
|
|
public AwakeningPattern Pattern;
|
||
|
|
public ActiveSkillData OriginalActive; // 진화 대상 액티브
|
||
|
|
public PassiveSkillData[] RequiredPassives; // 필요 패시브 후보 (1+ 보유)
|
||
|
|
[Tooltip("각성 후 기본 대미지 (원 액티브 Lv.5 대비 2~3배 권장, balance/01 v0.2 §3 참조)")]
|
||
|
|
public int AwakeningBaseDamage = 25;
|
||
|
|
[Tooltip("각성 연출 프리팹 (풀스크린)")]
|
||
|
|
public GameObject AwakeningEffectPrefab;
|
||
|
|
}
|
||
|
|
|
||
|
|
public enum ActiveCategory { Projectile, MeleeArea, PlacementPersistent, Minion, Debuff, SpecialJudge }
|
||
|
|
public enum PassiveCategory { StatUp, CycleAmplify, Survival, Recovery, ResourceExpand }
|
||
|
|
public enum ProjectileTrajectory { Line, Homing, Arc }
|
||
|
|
public enum StatType { Damage, AttackSpeed, MoveSpeed, MaxHearts, CritChance, CritDamage, DamageReduction, Evasion, IFrameExtend, JumpHeight, XPGain, TreasureFind }
|
||
|
|
|
||
|
|
[System.Flags]
|
||
|
|
public enum AttributeTag { None = 0, Physical = 1 << 0, Fire = 1 << 1, Frost = 1 << 2, Lightning = 1 << 3, Dark = 1 << 4 }
|
||
|
|
|
||
|
|
[System.Flags]
|
||
|
|
public enum TypeTag { None = 0, Melee = 1 << 0, Ranged = 1 << 1, Area = 1 << 2, Persistent = 1 << 3, Recovery = 1 << 4, Defense = 1 << 5 }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2-3. PlayerSkillInventory — 장착 스킬 관리 중앙 컴포넌트
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
namespace EerieVillage.Skills
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// Player GameObject에 부착. 장착 스킬 슬롯·Lv·각성 상태 관리.
|
||
|
|
/// [RequireComponent(typeof(PlayerController))]
|
||
|
|
/// </summary>
|
||
|
|
public class PlayerSkillInventory : MonoBehaviour
|
||
|
|
{
|
||
|
|
[Header("슬롯 상한 (balance-designer 재확인 — VS 원작 6/6 참고)")]
|
||
|
|
public int ActiveSlotMax = 6;
|
||
|
|
public int PassiveSlotMax = 6;
|
||
|
|
|
||
|
|
// 장착 슬롯 (인덱스 = 슬롯 번호)
|
||
|
|
private readonly List<IActiveSkill> _activeSkills = new();
|
||
|
|
private readonly List<IPassiveSkill> _passiveSkills = new();
|
||
|
|
private readonly List<IAwakeningSkill> _awakenedSkills = new();
|
||
|
|
|
||
|
|
// 각 카드 ID → 런타임 인스턴스 매핑 (재픽 시 Lv 업용)
|
||
|
|
private readonly Dictionary<string, ISkillRuntime> _cardIdToRuntime = new();
|
||
|
|
|
||
|
|
// 통합 PlayerStats — 패시브 보정이 여기에 누적 적용
|
||
|
|
public PlayerStats Stats { get; private set; }
|
||
|
|
|
||
|
|
// 적 처치·피격 이벤트 구독자 리스트 (OnKill·OnHit 트리거용)
|
||
|
|
public event System.Action<EnemyController> OnEnemyKilled;
|
||
|
|
public event System.Action<float> OnPlayerDamaged;
|
||
|
|
|
||
|
|
// 보물상자 획득 시 각성 조건 판정 진입
|
||
|
|
public void OnTreasureChestOpened() { /* §5 각성 조건 매니저 호출 */ }
|
||
|
|
|
||
|
|
// 픽 UI 선택 결과 반영
|
||
|
|
public void AddSkillByCardId(string cardId) { /* §3 데이터 흐름 참조 */ }
|
||
|
|
|
||
|
|
// Update에서 Active 런타임들의 Cooldown 감산 + OnTime Fire 호출
|
||
|
|
void Update() { /* §4 자동 발동 사이클 참조 */ }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 통합 플레이어 스탯. 패시브 보정이 모두 이 객체에 누적.
|
||
|
|
/// ActiveSkillRuntime의 EffectiveCooldown·EffectiveDamage 산출 입력.
|
||
|
|
/// </summary>
|
||
|
|
public class PlayerStats
|
||
|
|
{
|
||
|
|
public float DamageMultiplier = 1.0f; // P01·P02~P05
|
||
|
|
public float CooldownMultiplier = 1.0f; // P06 연사술 (0.8 = 20% 단축)
|
||
|
|
public float AreaMultiplier = 1.0f; // P07 광역확장
|
||
|
|
public int ExtraProjectiles = 0; // P08 투사체증폭
|
||
|
|
public float CritChance = 0f; // P09
|
||
|
|
public float CritDamage = 1.5f; // P10
|
||
|
|
public int ExtraMaxHearts = 0; // P12·P13
|
||
|
|
public float DamageReduction = 0f; // P14 부적방패
|
||
|
|
public float EvasionChance = 0f; // P15 회피술
|
||
|
|
public float IFrameExtend = 0f; // P15 i-frame 연장분
|
||
|
|
public float MoveSpeedMultiplier = 1.0f; // P18 질풍보
|
||
|
|
public float XPMultiplier = 1.0f; // P19 선견지명
|
||
|
|
public float TreasureFindBonus = 0f; // P20 재물복
|
||
|
|
// 속성별 대미지 배율
|
||
|
|
public Dictionary<AttributeTag, float> AttributeMultiplier = new();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §3. 데이터 흐름 (CSV → ScriptableObject → Runtime → Health.Decrement)
|
||
|
|
|
||
|
|
### 3-1. 전체 파이프라인
|
||
|
|
|
||
|
|
```
|
||
|
|
[정적 SOT] [에디터 툴] [자산]
|
||
|
|
02_스킬_효과_컨셉_v0.3.csv → SkillCsvImporter → Assets/Data/Skills/*.asset (60종)
|
||
|
|
(기획팀 편집) (EditorWindow) (SkillDataAsset 자식)
|
||
|
|
↓ Resources.Load
|
||
|
|
[런타임 생성]
|
||
|
|
SkillRuntimeFactory → new ActiveSkillRuntime(data)
|
||
|
|
new PassiveSkillRuntime(data)
|
||
|
|
new AwakeningSkillRuntime(data)
|
||
|
|
↓ 장착
|
||
|
|
PlayerSkillInventory → ISkillRuntime 슬롯 편입
|
||
|
|
↓ Update
|
||
|
|
ActiveSkillRuntime → .Fire() 호출
|
||
|
|
↓ Schedule<T>
|
||
|
|
Simulation.Event → 카테고리별 Dispatcher
|
||
|
|
↓
|
||
|
|
효과 발동기 (AttackHitbox·ProjectileSpawner 등)
|
||
|
|
↓ OverlapBox·OnTrigger
|
||
|
|
Health.Decrement(damage)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3-2. CSV → ScriptableObject 변환 파이프라인 (개발팀 에디터 툴)
|
||
|
|
|
||
|
|
**목적**: 기획팀이 CSV v0.3를 편집하면 에디터 툴 원클릭으로 60종 `.asset` 파일을 재생성.
|
||
|
|
|
||
|
|
**구현 포인트**:
|
||
|
|
```csharp
|
||
|
|
namespace EerieVillage.Skills.Editor
|
||
|
|
{
|
||
|
|
/// <summary>CSV v0.3 → SkillDataAsset 60종 일괄 변환 에디터 툴</summary>
|
||
|
|
public class SkillCsvImporter : EditorWindow
|
||
|
|
{
|
||
|
|
private const string CsvPath = "프로젝트/EerieVillage/기획/content/02_스킬_효과_컨셉_v0.3.csv";
|
||
|
|
private const string OutputDir = "Assets/Data/Skills/";
|
||
|
|
|
||
|
|
[MenuItem("EerieVillage/Skills/Import CSV → ScriptableObject")]
|
||
|
|
static void Import()
|
||
|
|
{
|
||
|
|
// 1. UTF-8 BOM 처리 (CSV v0.3 필수 인코딩)
|
||
|
|
var text = System.IO.File.ReadAllText(CsvPath, System.Text.Encoding.UTF8);
|
||
|
|
// 2. CSV 파싱 (분류 컬럼: 액티브·패시브·각성)
|
||
|
|
// 3. ID prefix로 Active/Passive/Awakening 분기
|
||
|
|
// - A## → ActiveSkillData
|
||
|
|
// - P## → PassiveSkillData
|
||
|
|
// - AW## → AwakeningSkillData
|
||
|
|
// 4. 기존 asset 발견 시 필드 업데이트 (GUID·참조 보존)
|
||
|
|
// 5. 신규는 AssetDatabase.CreateAsset()
|
||
|
|
// 6. 카테고리 컬럼("A. 투사체"·"P-A. 스탯" 등)을 enum ActiveCategory·PassiveCategory로 매핑
|
||
|
|
// 7. 태그·수치는 Phase 2 balance-designer 확정 후 별도 컬럼 추가 시 반영
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**카테고리 문자열 매핑 테이블**:
|
||
|
|
| CSV 값 | Enum |
|
||
|
|
|--------|------|
|
||
|
|
| "A. 투사체" | `ActiveCategory.Projectile` |
|
||
|
|
| "B. 근접·범위" | `ActiveCategory.MeleeArea` |
|
||
|
|
| "C. 설치·지속" | `ActiveCategory.PlacementPersistent` |
|
||
|
|
| "D. 소환" | `ActiveCategory.Minion` |
|
||
|
|
| "E. 상태이상" | `ActiveCategory.Debuff` |
|
||
|
|
| "F. 특수 판정" | `ActiveCategory.SpecialJudge` |
|
||
|
|
| "P-A. 스탯" | `PassiveCategory.StatUp` |
|
||
|
|
| "P-B. 주기" | `PassiveCategory.CycleAmplify` |
|
||
|
|
| "P-C. 생존" | `PassiveCategory.Survival` |
|
||
|
|
| "P-D. 회복" | `PassiveCategory.Recovery` |
|
||
|
|
| "P-E. 자원" | `PassiveCategory.ResourceExpand` |
|
||
|
|
| "1. 스케일업" | `AwakeningPattern.ScaleUp` |
|
||
|
|
| "2. 새 효과" | `AwakeningPattern.AddEffect` |
|
||
|
|
| "3. 다중 발동" | `AwakeningPattern.MultiFire` |
|
||
|
|
| "4. 광역 확산" | `AwakeningPattern.GlobalSpread` |
|
||
|
|
|
||
|
|
### 3-3. 런타임 인스턴스 생성 (Factory 패턴)
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
namespace EerieVillage.Skills
|
||
|
|
{
|
||
|
|
public static class SkillRuntimeFactory
|
||
|
|
{
|
||
|
|
public static ISkillRuntime Create(SkillDataAsset data)
|
||
|
|
{
|
||
|
|
return data switch
|
||
|
|
{
|
||
|
|
ActiveSkillData a => new ActiveSkillRuntime(a),
|
||
|
|
PassiveSkillData p => new PassiveSkillRuntime(p),
|
||
|
|
AwakeningSkillData aw => new AwakeningSkillRuntime(aw),
|
||
|
|
_ => throw new System.ArgumentException($"Unknown SkillDataAsset type: {data.GetType()}")
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3-4. 피해 전달 경로 (적 Health.Decrement까지)
|
||
|
|
|
||
|
|
```
|
||
|
|
ActiveSkillRuntime.Fire()
|
||
|
|
↓ Schedule<SkillFireEvent>
|
||
|
|
Simulation.Event<SkillFireEvent>.Execute()
|
||
|
|
↓ 카테고리 분기
|
||
|
|
┌─── Category.Projectile ─────────────────────────┐
|
||
|
|
│ ProjectileSpawner.Spawn(data, playerFacing) │
|
||
|
|
│ ↓ Projectile.OnTriggerEnter2D │
|
||
|
|
│ Health.Decrement(effectiveDamage) │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
┌─── Category.MeleeArea ──────────────────────────┐
|
||
|
|
│ AttackHitbox.Fire(direction) [BT7-Dev 재활용] │
|
||
|
|
│ ↓ OverlapBoxAll → 각 적에 Decrement(damage) │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
┌─── Category.PlacementPersistent ────────────────┐
|
||
|
|
│ AuraZone.Instantiate(position, data) │
|
||
|
|
│ ↓ 영역 내 적 주기적 Decrement │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
┌─── Category.Minion ─────────────────────────────┐
|
||
|
|
│ MinionSpawner.Spawn(data.MinionPrefab) │
|
||
|
|
│ ↓ MinionAI.Update → 적 공격 → Decrement │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
┌─── Category.Debuff ─────────────────────────────┐
|
||
|
|
│ DebuffApplier.Apply(target, stackLimit) │
|
||
|
|
│ ↓ N스택 시 폭발 이벤트 → Decrement │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
┌─── Category.SpecialJudge ───────────────────────┐
|
||
|
|
│ Random.value < FireProbability → Fire │
|
||
|
|
│ ↓ 단발 회심 또는 i-frame 연장 │
|
||
|
|
│ Health.Decrement(critDamage) │
|
||
|
|
└─────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
`effectiveDamage` 산출 공식 (balance/01 v0.2 §3 대미지 공식 준수):
|
||
|
|
```csharp
|
||
|
|
int effectiveDamage = Mathf.RoundToInt(
|
||
|
|
data.BaseDamage * // ActiveSkillData 기본값
|
||
|
|
stats.DamageMultiplier * // P01
|
||
|
|
StackLevelFactor(runtime.StackLevel) * // Lv.1=1.0 ~ Lv.5=2.0
|
||
|
|
stats.AttributeMultiplier[data.PrimaryAttr] // P02~P05
|
||
|
|
);
|
||
|
|
// 각성 발동 시 BaseDamage 대신 AwakeningBaseDamage 사용
|
||
|
|
```
|
||
|
|
|
||
|
|
`StackLevelFactor` 정의 (balance/01 v0.2 §3 명시):
|
||
|
|
```csharp
|
||
|
|
static float StackLevelFactor(int lv) => lv switch
|
||
|
|
{
|
||
|
|
1 => 1.0f, 2 => 1.2f, 3 => 1.4f, 4 => 1.6f, 5 => 2.0f,
|
||
|
|
_ => throw new System.ArgumentOutOfRangeException(nameof(lv))
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §4. 자동 발동 사이클 (Cooldown·OnHit·OnKill·OnTime)
|
||
|
|
|
||
|
|
### 4-1. 기본 원칙 — VS 순수형 정합 (기획 04 §5 준수)
|
||
|
|
|
||
|
|
- **플레이어 수동 공격 입력 없음** (BT7-Dev 확증)
|
||
|
|
- 모든 액티브는 `ActiveTrigger` enum에 따라 자동 발동
|
||
|
|
- 기본 무기(퇴마사 고유 1종)는 `PlayerAttackTicker` 유지 (BT7-Dev 재활용)
|
||
|
|
- 카드 시스템 장착 액티브는 **각자 독립 Cooldown 관리**
|
||
|
|
|
||
|
|
### 4-2. ActiveSkillRuntime Update 로직 (핵심)
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
namespace EerieVillage.Skills
|
||
|
|
{
|
||
|
|
public class ActiveSkillRuntime : IActiveSkill
|
||
|
|
{
|
||
|
|
public ActiveSkillData Data { get; }
|
||
|
|
public int StackLevel { get; private set; } = 1;
|
||
|
|
public ActiveTrigger Trigger => Data.Trigger;
|
||
|
|
public float BaseCooldown => Data.BaseCooldown;
|
||
|
|
|
||
|
|
public float EffectiveCooldown =>
|
||
|
|
BaseCooldown * _inventory.Stats.CooldownMultiplier / StackLevelFactor(StackLevel);
|
||
|
|
// 예: BaseCooldown 1.5s · P06 연사술 0.8 · Lv.5 팩터 2.0 = 0.94s 실제 쿨다운
|
||
|
|
// 하드캡 0.5s (balance/01 v0.2 §9 리스크 1 "이펙트 중첩 혼잡 방지")
|
||
|
|
|
||
|
|
public float CooldownRemaining { get; private set; } = 0f;
|
||
|
|
|
||
|
|
private PlayerSkillInventory _inventory;
|
||
|
|
|
||
|
|
public ActiveSkillRuntime(ActiveSkillData data) { Data = data; }
|
||
|
|
|
||
|
|
public void OnEquip(PlayerSkillInventory inventory)
|
||
|
|
{
|
||
|
|
_inventory = inventory;
|
||
|
|
CooldownRemaining = Data.BaseCooldown; // 첫 발동은 주기 후
|
||
|
|
}
|
||
|
|
|
||
|
|
public void OnUnequip() { /* 이벤트 구독 해제 */ }
|
||
|
|
|
||
|
|
public void Upgrade()
|
||
|
|
{
|
||
|
|
if (StackLevel < 5) StackLevel++;
|
||
|
|
// Lv.5 도달 시 각성 조건 1 충족 — AwakeningManager.MarkActiveMaxed(this)
|
||
|
|
if (StackLevel == 5) _inventory.AwakeningManager.MarkActiveMaxed(Data);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>PlayerSkillInventory.Update가 매 프레임 호출</summary>
|
||
|
|
public void Tick(float deltaTime)
|
||
|
|
{
|
||
|
|
if (Trigger != ActiveTrigger.OnTime) return; // OnHit·OnKill은 이벤트 구독
|
||
|
|
CooldownRemaining -= deltaTime;
|
||
|
|
if (CooldownRemaining <= 0f)
|
||
|
|
{
|
||
|
|
Fire();
|
||
|
|
CooldownRemaining += EffectiveCooldown; // 누적 오버플로 방지 (BT7-Dev Ticker와 동일 패턴)
|
||
|
|
CooldownRemaining = Mathf.Max(CooldownRemaining, 0.5f); // 하드캡
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Fire()
|
||
|
|
{
|
||
|
|
// 카테고리별 Schedule Dispatch (F 카테고리는 확률 판정 선행)
|
||
|
|
if (Data.Category == ActiveCategory.SpecialJudge)
|
||
|
|
{
|
||
|
|
if (UnityEngine.Random.value > Data.FireProbability) return;
|
||
|
|
}
|
||
|
|
var ev = Simulation.Schedule<SkillFireEvent>();
|
||
|
|
ev.Runtime = this;
|
||
|
|
ev.Inventory = _inventory;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4-3. PlayerSkillInventory.Update 통합 제어
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
void Update()
|
||
|
|
{
|
||
|
|
// 액티브 Cooldown 감산·발동
|
||
|
|
foreach (var active in _activeSkills)
|
||
|
|
{
|
||
|
|
active.Tick(Time.deltaTime);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 조건부 패시브 타이머 감산 (P17 감로수 등 OnTimer 계열)
|
||
|
|
foreach (var passive in _passiveSkills)
|
||
|
|
{
|
||
|
|
if (!passive.IsAlwaysOn)
|
||
|
|
{
|
||
|
|
passive.OnTrigger(new PassiveTriggerContext
|
||
|
|
{
|
||
|
|
Kind = PassiveTriggerKind.OnTimer,
|
||
|
|
TimeElapsed = Time.deltaTime
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4-4. 이벤트 트리거 구독 (OnHit·OnKill)
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
// PlayerSkillInventory.OnEnable
|
||
|
|
Health playerHealth = GetComponent<Health>();
|
||
|
|
playerHealth.OnPlayerDamagedEvent += OnPlayerDamagedHandler; // Health에 이벤트 추가 필요 — BT7-Dev 확장 포인트
|
||
|
|
// EnemyDeath 이벤트는 Simulation Schedule에서 발생 → Simulation 커스텀 훅으로 구독
|
||
|
|
|
||
|
|
void OnPlayerDamagedHandler(float damageAmount)
|
||
|
|
{
|
||
|
|
OnPlayerDamaged?.Invoke(damageAmount);
|
||
|
|
// P11 호신부 등 OnPlayerDamaged 트리거 패시브·액티브 발동
|
||
|
|
foreach (var active in _activeSkills.Where(a => a.Trigger == ActiveTrigger.OnHit))
|
||
|
|
active.Fire();
|
||
|
|
foreach (var passive in _passiveSkills.Where(p => !p.IsAlwaysOn))
|
||
|
|
passive.OnTrigger(new PassiveTriggerContext
|
||
|
|
{
|
||
|
|
Kind = PassiveTriggerKind.OnPlayerDamaged,
|
||
|
|
DamageTaken = damageAmount
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void OnEnemyKilledHandler(EnemyController enemy)
|
||
|
|
{
|
||
|
|
OnEnemyKilled?.Invoke(enemy);
|
||
|
|
// P16 단전수련 (처치 시 회복) 등 OnEnemyKilled 트리거
|
||
|
|
foreach (var active in _activeSkills.Where(a => a.Trigger == ActiveTrigger.OnKill))
|
||
|
|
active.Fire();
|
||
|
|
foreach (var passive in _passiveSkills.Where(p => !p.IsAlwaysOn))
|
||
|
|
passive.OnTrigger(new PassiveTriggerContext
|
||
|
|
{
|
||
|
|
Kind = PassiveTriggerKind.OnEnemyKilled,
|
||
|
|
KillCount = _totalKillCount // 인벤토리 누적
|
||
|
|
});
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**BT7-Dev `Health.cs` 확장 필요 사항** (C39 작업 전 실측 기반 근본 개선안):
|
||
|
|
- `Health`에 `public event Action<int> OnDamagedEvent` 신설 — `Decrement(int damage)` 호출 시 발화
|
||
|
|
- 기존 `HealthIsZero` 이벤트는 유지 (BT7-Dev 계승)
|
||
|
|
- 본 변경은 **Phase 2 클라이언트팀 구현** 위임 시 포함 (후속 commit에서 `Health.cs` 확장)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §5. 각성 발동 조건 처리 (3 조건 동시 충족 매니저)
|
||
|
|
|
||
|
|
### 5-1. AwakeningManager 책임
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
namespace EerieVillage.Skills
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 각성 발동 3 조건 동시 충족 감지 + 발동 UI 진입 + 액티브 교체.
|
||
|
|
/// PlayerSkillInventory 하위 서브시스템.
|
||
|
|
/// </summary>
|
||
|
|
public class AwakeningManager
|
||
|
|
{
|
||
|
|
// 조건 1 충족 상태: Lv.5 도달 액티브 ID 집합
|
||
|
|
private readonly HashSet<string> _maxedActiveIds = new();
|
||
|
|
|
||
|
|
// 조건 2 충족 상태: 현재 보유 패시브 ID 집합 (참조)
|
||
|
|
// - PlayerSkillInventory가 실시간 유지
|
||
|
|
|
||
|
|
// 이미 각성된 액티브 ID (중복 발동 차단)
|
||
|
|
private readonly HashSet<string> _alreadyAwakenedIds = new();
|
||
|
|
|
||
|
|
private readonly PlayerSkillInventory _inventory;
|
||
|
|
private readonly List<AwakeningSkillData> _allAwakeningData; // Resources 전체 로드
|
||
|
|
|
||
|
|
public AwakeningManager(PlayerSkillInventory inventory)
|
||
|
|
{
|
||
|
|
_inventory = inventory;
|
||
|
|
_allAwakeningData = Resources.LoadAll<AwakeningSkillData>("Data/Skills/Awakening").ToList();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>액티브 Lv.5 도달 시 ActiveSkillRuntime.Upgrade에서 호출</summary>
|
||
|
|
public void MarkActiveMaxed(ActiveSkillData data)
|
||
|
|
{
|
||
|
|
_maxedActiveIds.Add(data.CardId);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>보물상자 개봉 시 PlayerSkillInventory.OnTreasureChestOpened가 호출</summary>
|
||
|
|
public void CheckAwakeningEligibility()
|
||
|
|
{
|
||
|
|
var eligibleAwakenings = new List<AwakeningSkillData>();
|
||
|
|
|
||
|
|
foreach (var awData in _allAwakeningData)
|
||
|
|
{
|
||
|
|
// 이미 각성됨 → 스킵
|
||
|
|
if (_alreadyAwakenedIds.Contains(awData.CardId)) continue;
|
||
|
|
|
||
|
|
// 조건 1: 원 액티브 Lv.5?
|
||
|
|
if (!_maxedActiveIds.Contains(awData.OriginalActive.CardId)) continue;
|
||
|
|
|
||
|
|
// 조건 2: 필요 패시브 1+ 보유?
|
||
|
|
bool hasRequiredPassive = awData.RequiredPassives.Any(p =>
|
||
|
|
_inventory.HasPassive(p.CardId));
|
||
|
|
if (!hasRequiredPassive) continue;
|
||
|
|
|
||
|
|
eligibleAwakenings.Add(awData);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 조건 3: 보물상자는 이미 개봉된 상태에서 호출됨 (진입 시점 보장)
|
||
|
|
|
||
|
|
if (eligibleAwakenings.Count == 0)
|
||
|
|
{
|
||
|
|
// 대체 보상 처리 (balance-designer 결정 — 재화·회복·경험치)
|
||
|
|
_inventory.GrantTreasureFallbackReward();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (eligibleAwakenings.Count == 1)
|
||
|
|
{
|
||
|
|
// 단일 각성 즉시 발동
|
||
|
|
TriggerAwakening(eligibleAwakenings[0]);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// 다중 각성 조건 동시 충족 — 플레이어 1개 선택 UI
|
||
|
|
// UXDesigner 연계: ux/02_HUD_설계.md 각성 UI 섹션 참조
|
||
|
|
_inventory.ShowAwakeningChoiceUI(eligibleAwakenings, TriggerAwakening);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void TriggerAwakening(AwakeningSkillData awData)
|
||
|
|
{
|
||
|
|
// 풀스크린 연출 Schedule (BT7-Dev Simulation 패턴)
|
||
|
|
var ev = Simulation.Schedule<AwakeningEffectEvent>();
|
||
|
|
ev.Data = awData;
|
||
|
|
|
||
|
|
// 원 액티브 런타임 교체
|
||
|
|
var runtime = new AwakeningSkillRuntime(awData);
|
||
|
|
_inventory.ReplaceActiveWithAwakening(awData.OriginalActive.CardId, runtime);
|
||
|
|
|
||
|
|
_alreadyAwakenedIds.Add(awData.CardId);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5-2. 3 조건 동시 충족 판정 테이블 (실행 시점)
|
||
|
|
|
||
|
|
| 조건 | 판정 시점 | 상태 저장 위치 |
|
||
|
|
|------|----------|--------------|
|
||
|
|
| 1. 원 액티브 Lv.5 | `ActiveSkillRuntime.Upgrade`에서 StackLevel == 5 도달 시 | `AwakeningManager._maxedActiveIds` HashSet |
|
||
|
|
| 2. 필요 패시브 1+ 보유 | 보물상자 개봉 시 `_inventory.HasPassive(id)` 실시간 조회 | `PlayerSkillInventory._passiveSkills` |
|
||
|
|
| 3. 보물상자 획득 | 스테이지 내 TreasureChest 오브젝트 OnTrigger 시 → `OnTreasureChestOpened()` | 이벤트 기반 (별도 저장 불요) |
|
||
|
|
|
||
|
|
**조건 3 트리거가 마스터** — 보물상자 개봉 시점에만 1·2 조건을 판정. Lv.5 도달 시점이나 패시브 장착 시점에 즉시 판정하지 않음 (보물상자가 "게이팅 조건").
|
||
|
|
|
||
|
|
### 5-3. AwakeningSkillRuntime 동작 (원 액티브 대체)
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
public class AwakeningSkillRuntime : IAwakeningSkill, IActiveSkill
|
||
|
|
{
|
||
|
|
public AwakeningSkillData Data { get; }
|
||
|
|
public ActiveSkillData OriginalActive => Data.OriginalActive;
|
||
|
|
public PassiveSkillData[] RequiredPassives => Data.RequiredPassives;
|
||
|
|
public AwakeningPattern Pattern => Data.Pattern;
|
||
|
|
public int StackLevel => 5; // 각성은 Lv.5 고정
|
||
|
|
|
||
|
|
// IActiveSkill 위임 — 기본적으로 원 액티브 동작 + 패턴별 변형
|
||
|
|
public ActiveTrigger Trigger => Data.OriginalActive.Trigger;
|
||
|
|
public float BaseCooldown => CalculateAwakeningCooldown();
|
||
|
|
public float EffectiveCooldown => /* §4-2 동일 공식 */;
|
||
|
|
public float CooldownRemaining { get; private set; }
|
||
|
|
|
||
|
|
private float CalculateAwakeningCooldown() => Pattern switch
|
||
|
|
{
|
||
|
|
AwakeningPattern.ScaleUp => Data.OriginalActive.BaseCooldown * 0.5f, // 빈도 2배
|
||
|
|
AwakeningPattern.MultiFire => Data.OriginalActive.BaseCooldown, // 주기 동일, 수량 2배
|
||
|
|
AwakeningPattern.AddEffect => Data.OriginalActive.BaseCooldown, // 주기 동일
|
||
|
|
AwakeningPattern.GlobalSpread => Data.OriginalActive.BaseCooldown * 2f, // 화면 전체라 빈도 저감
|
||
|
|
_ => Data.OriginalActive.BaseCooldown
|
||
|
|
};
|
||
|
|
|
||
|
|
public void Fire()
|
||
|
|
{
|
||
|
|
var ev = Simulation.Schedule<AwakeningFireEvent>();
|
||
|
|
ev.Runtime = this;
|
||
|
|
ev.Pattern = Pattern;
|
||
|
|
// Pattern별 Dispatcher가 효과 발동 처리 — §5-4 참조
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Awaken(PlayerSkillInventory inventory) { /* 초기 발동 연출 */ }
|
||
|
|
public void OnEquip(PlayerSkillInventory inv) { /* 불요 — Awaken이 대체 */ }
|
||
|
|
public void OnUnequip() { }
|
||
|
|
public void Upgrade() { throw new System.InvalidOperationException("각성은 Lv 업 없음"); }
|
||
|
|
|
||
|
|
public void Tick(float deltaTime) { /* §4-2 동일 로직 */ }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5-4. 각성 패턴별 Dispatcher
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
public class AwakeningFireEvent : Simulation.Event<AwakeningFireEvent>
|
||
|
|
{
|
||
|
|
public AwakeningSkillRuntime Runtime;
|
||
|
|
public AwakeningPattern Pattern;
|
||
|
|
|
||
|
|
public override void Execute()
|
||
|
|
{
|
||
|
|
switch (Pattern)
|
||
|
|
{
|
||
|
|
case AwakeningPattern.ScaleUp:
|
||
|
|
// 원 액티브 발동 + 판정 범위·대미지 배율 증폭
|
||
|
|
FireOriginalWithMultiplier(3.0f);
|
||
|
|
break;
|
||
|
|
case AwakeningPattern.AddEffect:
|
||
|
|
// 원 액티브 발동 + 추가 효과 (예: DoT·속성 부여)
|
||
|
|
FireOriginal();
|
||
|
|
FireAdditionalEffect(Runtime.Data);
|
||
|
|
break;
|
||
|
|
case AwakeningPattern.MultiFire:
|
||
|
|
// 원 액티브 N회 동시 발동 (투사체 2배·소환물 3~4기)
|
||
|
|
for (int i = 0; i < Runtime.Data.MultiFireCount; i++)
|
||
|
|
FireOriginal();
|
||
|
|
break;
|
||
|
|
case AwakeningPattern.GlobalSpread:
|
||
|
|
// 화면 전체 AoE 1회 발동
|
||
|
|
FireGlobalAoE(Runtime.Data);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**`MultiFireCount` 필드 추가 필요** — `AwakeningSkillData`에 `[SerializeField] public int MultiFireCount = 3;` (각성별 Inspector 튠). balance-designer 이관.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §6. 카테고리·패턴 매핑 (6+5+4)
|
||
|
|
|
||
|
|
### 6-1. 액티브 6 카테고리 → 효과 발동기 매핑
|
||
|
|
|
||
|
|
| 카테고리 | 효과 발동기 (Unity 컴포넌트) | BT7-Dev 재활용 | 신설 필요 |
|
||
|
|
|---------|------------------------|---------------|---------|
|
||
|
|
| A. 투사체 | `ProjectileSpawner` + `Projectile` | ❌ | ✅ (BT.Framework 오브젝트 풀링 활용) |
|
||
|
|
| B. 근접·범위 | `AttackHitbox` | ✅ BT7-Dev 재활용 | - |
|
||
|
|
| C. 설치·지속 | `AuraZone` | ❌ | ✅ |
|
||
|
|
| D. 소환 | `MinionSpawner` + `MinionAI` | ❌ | ✅ |
|
||
|
|
| E. 상태이상 | `DebuffApplier` + `DebuffStack` | ❌ | ✅ |
|
||
|
|
| F. 특수 판정 | 분기 — `AttackHitbox`(A16) + `IFrameExtender`(A18) | 부분 재활용 (A16 = Hitbox 확률 판정) | ✅ (A18 i-frame 연장) |
|
||
|
|
|
||
|
|
### 6-2. 패시브 5 카테고리 → PlayerStats 필드 매핑
|
||
|
|
|
||
|
|
| 카테고리 | 대상 카드 | PlayerStats 필드 (§2-3) |
|
||
|
|
|---------|---------|------------------------|
|
||
|
|
| P-A. 스탯 상승 | P01·P02~P05·P09·P10 | `DamageMultiplier`·`AttributeMultiplier`·`CritChance`·`CritDamage` |
|
||
|
|
| P-B. 주기·증폭 | P06·P07·P08 | `CooldownMultiplier`·`AreaMultiplier`·`ExtraProjectiles` |
|
||
|
|
| P-C. 생존 강화 | P11·P12·P13·P14·P15 | 조건부(`OnTrigger`) + `ExtraMaxHearts`·`DamageReduction`·`EvasionChance`·`IFrameExtend` |
|
||
|
|
| P-D. 회복 | P16·P17 | 조건부(`OnTrigger` OnEnemyKilled/OnTimer) → `Health.Heal(1)` 호출 |
|
||
|
|
| P-E. 자원 확장 | P18·P19·P20 | `MoveSpeedMultiplier`·`XPMultiplier`·`TreasureFindBonus` |
|
||
|
|
|
||
|
|
**특수 처리 — 최대 하트 수 증가 (P12·P13)**:
|
||
|
|
```csharp
|
||
|
|
// PassiveSkillRuntime.ApplyModifier (P-C 대상 TargetStat == MaxHearts)
|
||
|
|
public void ApplyModifier(PlayerStats stats)
|
||
|
|
{
|
||
|
|
if (Data.TargetStat == StatType.MaxHearts)
|
||
|
|
{
|
||
|
|
stats.ExtraMaxHearts += Mathf.RoundToInt(Data.BaseModifierValue * StackLevelFactor(StackLevel));
|
||
|
|
// Health.IncreaseMaxHearts 호출 — BT7-Dev API 재활용
|
||
|
|
var playerHealth = _inventory.GetComponent<Health>();
|
||
|
|
playerHealth.IncreaseMaxHearts(Mathf.RoundToInt(Data.BaseModifierValue));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6-3. 각성 4 패턴 → AwakeningFireEvent 분기
|
||
|
|
|
||
|
|
§5-4 Dispatcher에 정의됨. 패턴별 Unity 구현 복잡도:
|
||
|
|
|
||
|
|
| 패턴 | 구현 복잡도 | 주요 리스크 |
|
||
|
|
|------|-----------|-----------|
|
||
|
|
| 1. 스케일업 | 낮음 (기존 발동기에 배율만 적용) | 밸런스 치우침 |
|
||
|
|
| 2. 새효과 추가 | 중간 (원 효과 + 추가 효과 병렬 실행) | 이벤트 순서 관리 |
|
||
|
|
| 3. 다중 발동 | 중간 (동시 N회 발동 + 풀링 용량) | 파티클 상한 초과 (기획 §8 개발팀 확인 1) |
|
||
|
|
| 4. 광역 확산 | 높음 (화면 전체 AoE + 연쇄 전파) | 성능 이슈 (기획 §8 개발팀 확인 1 — 파티클·충돌 연산 배치 최적화 필수) |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §7. 1단계 산출물 (개발팀장 설계) — 본 Phase 완결
|
||
|
|
|
||
|
|
### 7-1. Phase 1 완결 확증 체크리스트
|
||
|
|
|
||
|
|
- [x] §1 아키텍처 4계층 정의 완료
|
||
|
|
- [x] §2 인터페이스 4종(`ISkillRuntime`·`IActiveSkill`·`IPassiveSkill`·`IAwakeningSkill`) + ScriptableObject 3종(`ActiveSkillData`·`PassiveSkillData`·`AwakeningSkillData`) + `PlayerSkillInventory`·`PlayerStats` 정의 완료
|
||
|
|
- [x] §3 CSV → ScriptableObject → Runtime → Health.Decrement 데이터 흐름 완비 (카테고리 매핑 테이블 포함)
|
||
|
|
- [x] §4 자동 발동 사이클 3 트리거(OnTime·OnHit·OnKill) + `ActiveSkillRuntime.Tick` 구현 설계
|
||
|
|
- [x] §5 각성 3 조건 동시 충족 매니저 + 패턴별 Dispatcher 설계
|
||
|
|
- [x] §6 카테고리·패턴 매핑 완비 (6+5+4 전수)
|
||
|
|
- [x] §10 BT7-Dev 통합 영역 명시
|
||
|
|
|
||
|
|
### 7-2. Phase 2 위임 작업 단위 분해 (클라이언트팀 Sonnet Task)
|
||
|
|
|
||
|
|
**후속 PM 차원 위임 권고** (본 Task 범위 초과 — C48·C50 준수 판단):
|
||
|
|
|
||
|
|
Phase 2는 **PM이 별도 Task로 클라이언트팀 Sonnet에 위임**하는 것이 C48 3자문 통과 기준에 정합. 본 Phase 1 Task에서 추가 Sonnet 호출 시 토큰 과도 + 단일 Task 범위 과대. 구현 작업 단위 분해는 다음과 같이 명시:
|
||
|
|
|
||
|
|
**Phase 2-A. 인터페이스·ScriptableObject 코드 생성** (Unity 외부 레포 편집):
|
||
|
|
- `Assets/Scripts/Skills/{ISkillRuntime.cs, IActiveSkill.cs, IPassiveSkill.cs, IAwakeningSkill.cs}` 4개 신설
|
||
|
|
- `Assets/Scripts/Skills/Data/{SkillDataAsset.cs, ActiveSkillData.cs, PassiveSkillData.cs, AwakeningSkillData.cs}` 4개 신설
|
||
|
|
- `Assets/Scripts/Skills/Runtime/{ActiveSkillRuntime.cs, PassiveSkillRuntime.cs, AwakeningSkillRuntime.cs, PlayerSkillInventory.cs, PlayerStats.cs, AwakeningManager.cs, SkillRuntimeFactory.cs}` 7개 신설
|
||
|
|
- `Assets/Scripts/Skills/Events/{SkillFireEvent.cs, AwakeningFireEvent.cs, AwakeningEffectEvent.cs}` 3개 신설
|
||
|
|
|
||
|
|
**Phase 2-B. 효과 발동기 신설**:
|
||
|
|
- `Assets/Scripts/Skills/Effectors/{ProjectileSpawner.cs, Projectile.cs, AuraZone.cs, MinionSpawner.cs, MinionAI.cs, DebuffApplier.cs, DebuffStack.cs}` 7개 신설
|
||
|
|
- 기존 `AttackHitbox.cs`는 B 카테고리 효과 발동기로 재활용 (코드 변경 불요)
|
||
|
|
|
||
|
|
**Phase 2-C. BT7-Dev 자산 확장**:
|
||
|
|
- `Assets/Scripts/Mechanics/Health.cs`에 `event Action<int> OnDamagedEvent` 신설 (Decrement 호출 시 발화)
|
||
|
|
- `Assets/Scripts/Mechanics/PlayerController.cs`에 `PlayerSkillInventory` 참조 필드 추가 (`[RequireComponent]`)
|
||
|
|
|
||
|
|
**Phase 2-D. 에디터 툴 구현**:
|
||
|
|
- `Assets/Editor/Skills/SkillCsvImporter.cs` 신설 (CSV v0.3 → 60종 `.asset` 변환)
|
||
|
|
- `Assets/Data/Skills/{Active,Passive,Awakening}/*.asset` 60종 생성 (에디터 툴 1회 실행)
|
||
|
|
|
||
|
|
**Phase 2-E. EditMode 테스트 확장**:
|
||
|
|
- 기존 BT7-Dev `PlayerAttackTests.cs` 13건 유지
|
||
|
|
- 신설 `Assets/Tests/Editor/SkillSystemTests.cs` — 인터페이스 계약·Factory·각성 조건 판정·CSV 파싱 검증 (최소 10건)
|
||
|
|
|
||
|
|
**Phase 2 예상 작업량**: 스크립트 약 25개 신설 + 테스트 10건 + asset 60개. 단일 Task 범위. 클라이언트팀 Sonnet 적정.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §8. 2단계 산출물 (클라이언트팀 구현) — 본 Phase 위임 대기
|
||
|
|
|
||
|
|
본 섹션은 **Phase 2 클라이언트팀 구현 결과 수령 후 갱신** 예정. 현재 미이행.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §9. 3단계 산출물 (개발팀장 검증) — 본 Phase 위임 대기
|
||
|
|
|
||
|
|
본 섹션은 **Phase 2 완료 후 개발팀장 Opus가 검증 결과 기입** 예정. 현재 미이행.
|
||
|
|
|
||
|
|
**검증 항목 (사전 명시)**:
|
||
|
|
1. **C11 개발 관점 3축 정합** — 자원 효율(파티클·GC)·코드 직관성(4계층 구조 명확)·범용성(ISkillRuntime 인터페이스 일관)
|
||
|
|
2. **기획서 SOT 정합** — 60종 카테고리·패턴 전수 처리 가능 확증
|
||
|
|
3. **BT7-Dev 충돌 없음** — `Health`·`PlayerAttack`·`PlayerAttackTicker`·`AttackHitbox`·`PlayerController` 계약 변경 최소
|
||
|
|
4. **BT.Framework 재활용 적정성** — 오브젝트 풀링(Projectile·Minion) 활용 여부
|
||
|
|
5. **EditMode 테스트 10건 + 기존 13건 전부 green**
|
||
|
|
6. **balance-designer 이관 항목 명시** — 수치·확률·쿨다운 하드캡 등
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §10. BT7-Dev 통합 영역
|
||
|
|
|
||
|
|
### 10-1. 완전 보존 자산 (코드 변경 없음)
|
||
|
|
|
||
|
|
| BT7-Dev 자산 | 본 설계 활용 |
|
||
|
|
|-------------|-----------|
|
||
|
|
| `Health.cs`: `maxHearts`·`QuartersPerHeart`·`Decrement(int)`·`Heal(int)`·`IncreaseMaxHearts(int)`·`CurrentHP`·`IsAlive`·`Die()`·`Increment()` | P-C·P-D 패시브 효과의 적용 API |
|
||
|
|
| `Health.cs`: i-frame 0.6s 보호 로직 | 본 설계 전제 (연속 피격 방지). 확률 회피(P15)는 `Decrement` 호출 전 분기 |
|
||
|
|
| `PlayerAttack : Simulation.Event` | 기본 무기 발동 이벤트로 유지 (스킬 시스템 이벤트는 별도) |
|
||
|
|
| `PlayerAttackTicker` | 기본 무기 주기 타이머. 스킬 슬롯 액티브는 독립 `ActiveSkillRuntime.Tick` |
|
||
|
|
| `AttackHitbox`: `Fire(Vector2)`·`size`·`offsetDistance`·`activeDuration`·`damage`·`targetLayers` | B 카테고리 스킬 효과 발동기 재활용 |
|
||
|
|
| `PlayerController`: `Facing`·`controlEnabled`·`animator` | 투사체 방향·발동 제어·애니메이션 연동 |
|
||
|
|
| `InputSystem_Actions` Attack 액션 제거 상태 | VS 순수형 정합 — 공격 입력 채널 없음 |
|
||
|
|
|
||
|
|
### 10-2. 확장 필요 자산 (Phase 2 클라이언트팀 구현 범위)
|
||
|
|
|
||
|
|
| BT7-Dev 자산 | 확장 내용 | 위험도 |
|
||
|
|
|-------------|---------|-------|
|
||
|
|
| `Health.cs` | `public event Action<int> OnDamagedEvent` 신설 (Decrement 시 발화) | 낮음 (기존 호출자 영향 없음) |
|
||
|
|
| `PlayerController.cs` | `PlayerSkillInventory` 참조 필드 추가·`[RequireComponent]` 추가 | 낮음 (필드 추가만) |
|
||
|
|
| `Player.prefab` | `PlayerSkillInventory` 컴포넌트 부착 (PD 수동 작업 or 에디터 툴) | 중간 (Inspector 작업 필요) |
|
||
|
|
|
||
|
|
### 10-3. BT.Framework 재활용 영역 (P29 준수)
|
||
|
|
|
||
|
|
- **오브젝트 풀링** — `ProjectileSpawner`·`MinionSpawner`·`AuraZone` 3종의 인스턴스 관리에 BT.Framework Tier 1 오브젝트 풀 활용
|
||
|
|
- **로깅·검증** — `ValidationEx`·`Log` Tier 1 유틸리티로 런타임 오류 감지
|
||
|
|
- **SafeAreaBorder** — 풀스크린 각성 연출 시 Safe Area 참조
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §11. 기각안
|
||
|
|
|
||
|
|
### 11-1. "ISkillRuntime 없이 ActiveSkillRuntime·PassiveSkillRuntime·AwakeningSkillRuntime을 독립 추상 클래스로 분리" — 기각
|
||
|
|
|
||
|
|
**기각 근거** (C11 코드 구조 직관성):
|
||
|
|
- `PlayerSkillInventory`가 3개의 독립 리스트를 별도 관리 시 "모든 스킬에 공통 작업" (Upgrade·OnEquip·저장/로드) 구현 시 3 경로 중복
|
||
|
|
- 단일 `ISkillRuntime` 인터페이스로 공통 계약 + 각 파생 인터페이스로 특화 계약 분리 → SOLID ISP·LSP 동시 만족
|
||
|
|
- **채택안**: ISkillRuntime base + IActiveSkill·IPassiveSkill·IAwakeningSkill 확장
|
||
|
|
|
||
|
|
### 11-2. "PlayerStats를 MonoBehaviour로 구현 (Inspector 가시화)" — 기각
|
||
|
|
|
||
|
|
**기각 근거** (C11 자원 효율):
|
||
|
|
- MonoBehaviour는 GC 오버헤드·Unity Lifecycle 부담
|
||
|
|
- `PlayerStats`는 런타임 동안 패시브 적용·조회만 빈번하게 발생 → 순수 POCO 클래스가 적정
|
||
|
|
- 디버깅 시 Inspector 가시화 필요 시 `PlayerSkillInventory`가 `[SerializeField]`로 최신값 표시
|
||
|
|
- **채택안**: 순수 POCO + `PlayerSkillInventory.Stats` public 프로퍼티
|
||
|
|
|
||
|
|
### 11-3. "스킬 효과를 Simulation.Event 기반 Schedule 대신 직접 MonoBehaviour 발동으로 구현" — 기각
|
||
|
|
|
||
|
|
**기각 근거** (BT7-Dev 패턴 일관성 + C11 코드 구조 직관성):
|
||
|
|
- BT7-Dev가 `Schedule<PlayerAttack>`·`Schedule<EnemyDeath>` 패턴을 일관 적용 — 본 설계가 다른 발동 체계 도입 시 코드 읽기 부담
|
||
|
|
- 이벤트 체계의 장점: 실행 순서 관리·일시 정지·재연 디버깅
|
||
|
|
- **채택안**: 모든 효과 발동을 `Simulation.Schedule<SkillFireEvent>` 등 Event 패턴으로 통일
|
||
|
|
|
||
|
|
### 11-4. "CSV 파싱을 런타임에 실행 (StreamingAssets)" — 기각
|
||
|
|
|
||
|
|
**기각 근거** (C11 자원 효율 + 기획팀 피드백 루프):
|
||
|
|
- 런타임 CSV 파싱은 모바일 IO 부담 + 파싱 에러의 늦은 감지
|
||
|
|
- ScriptableObject는 Unity 시리얼라이즈 최적화 + AssetBundle 적용 가능
|
||
|
|
- 기획팀 편집 → 에디터 툴 1회 실행 → 버전 관리되는 asset → PD Play 검증 워크플로우가 이미 확립됨 (BT7-Dev PlayerTestGirl 패턴)
|
||
|
|
- **채택안**: 에디터 툴 SkillCsvImporter로 CSV → 60종 `.asset` 변환, 런타임은 ScriptableObject만 참조
|
||
|
|
|
||
|
|
### 11-5. "각성 조건 3종 중 보물상자 대신 스테이지 클리어로 트리거" — 기각
|
||
|
|
|
||
|
|
**기각 근거** (기획서 §4-1 VS 원작 동일 준수 + 기획 SOT 위배 금지):
|
||
|
|
- 기획 `system/01_카드_시스템.md` v0.2 규칙 5에서 "보물상자 획득"을 조건 3으로 명시 확정
|
||
|
|
- 스테이지 클리어 트리거 변경은 기획팀 방향 변경 필요 (PD 결정 영역 — C36-2 c)
|
||
|
|
- 개발팀 자의로 변경 시 기획 SOT 위배 (C39 시스템 반영 실측 위반)
|
||
|
|
- **채택안**: 기획 SOT 그대로 — 보물상자 개봉 시점에 `AwakeningManager.CheckAwakeningEligibility()` 호출
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §12. 변경 이력
|
||
|
|
|
||
|
|
| 일시 | 변경 | 사유 | 기안 |
|
||
|
|
|------|------|------|------|
|
||
|
|
| 2026-04-24 | v1.0 Phase 1 설계 — 아키텍처 4계층·인터페이스 4+4종·데이터 흐름·자동 발동 사이클·각성 조건 매니저·카테고리 매핑 6+5+4·BT7-Dev 통합 영역 | BT12-Dev PD 직접 지시 "스킬 시스템 설계 진행" + C49 1단계 시범 적용 | 개발팀장 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §13. 후속 안건 (PM 보고용)
|
||
|
|
|
||
|
|
### PM 결정·위임 필요
|
||
|
|
|
||
|
|
1. **Phase 2 클라이언트팀 Sonnet 위임 Task 생성** — 본 설계 §7-2 Phase 2-A~E 작업 단위 기반. C48 3자문 통과 (영역 전문성·구현 코드 작성 = Sonnet 적정). C50 토큰 추정 클라이언트팀 Task 단일 건 기준 50~80K (대용량) → PD 사전 안내 검토 필요
|
||
|
|
2. **Phase 3 개발팀장 검증 Task 생성** — Phase 2 완료 후 개발팀장 Opus 검증. §9 검증 항목 6종 기반
|
||
|
|
3. **balance-designer 이관** — 60종 수치(`BaseDamage`·`BaseCooldown`·`FireProbability`·`DebuffStackLimit`·`MultiFireCount`·`AwakeningBaseDamage`) 확정 Task 기획팀장 경유 위임
|
||
|
|
4. **narrative-designer 이관 대기** — 카드명 세계관 재매핑(조선 퇴마 톤)은 PD 후속 검토 대기 (기획 CSV v0.3에 영문명만 있음)
|
||
|
|
|
||
|
|
### 차단 블로커
|
||
|
|
|
||
|
|
- **Unity 프로젝트 경로 `paths.local.json` 미설정** (`UNITY_PROJECT_ROOT: __SET_PER_PC__`). Phase 2 클라이언트팀 구현 위임 시 PD에게 PC 경로 설정 요청 필요 또는 BT7-Dev 참조 경로(`D:/NerdNavis/EerieVillage` 대화로그 인용) 재확인
|
||
|
|
|
||
|
|
### 개발팀 C11 확인 완료 사항
|
||
|
|
|
||
|
|
- §1-4 BT7-Dev 자산 활용 확증 (Health·PlayerAttack·PlayerAttackTicker·AttackHitbox·PlayerController 전수)
|
||
|
|
- §6-3 광역 확산 패턴(AW01·AW03·AW06·AW13·AW19) 성능 최적화 필수 확인 (기획 §8 개발팀 확인 1)
|
||
|
|
- §6-3 다중 발동 패턴(AW09·AW10·AW15·AW20) 동시 오브젝트 상한·풀링 용량 확인 (기획 §8 개발팀 확인 2)
|
||
|
|
- §10-3 BT.Framework Tier 1 오브젝트 풀 재활용 방향 확정 (P29 준수)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## §14. 참조 문서
|
||
|
|
|
||
|
|
- **기획 SOT**:
|
||
|
|
- `프로젝트/EerieVillage/기획/content/02_스킬_효과_컨셉.md` v0.2
|
||
|
|
- `프로젝트/EerieVillage/기획/content/02_스킬_효과_컨셉_v0.3.csv` (60종 캐주얼 명명)
|
||
|
|
- `프로젝트/EerieVillage/기획/system/01_카드_시스템.md` v0.2
|
||
|
|
- `프로젝트/EerieVillage/기획/balance/01_전투_수치.md` v0.2
|
||
|
|
- `프로젝트/EerieVillage/기획/04_전투_기본_스펙.md` v0.2
|
||
|
|
- `프로젝트/EerieVillage/기획/content/01_카드_풀.md` v0.2
|
||
|
|
- **BT7-Dev 선행**: `프로젝트/EerieVillage/개발/06_BT7-Plan_VS순수형_재구조.md` v1.0
|
||
|
|
- **조직 자산**: `프로젝트/코어프레임워크/01·03·04_*.md` (BT.Framework Tier 1 재활용 근거)
|
||
|
|
- **PD 지시 로그**: `공유/PD_지시_트래킹/개발팀_PD_지시_로그.md` BT12-Dev 항목
|
||
|
|
- **대화로그**: `공유/대화로그/EerieVillage/2026-04-24.md` BT12-Dev 엔트리 (후속 작성)
|