스테이지 개념을 만들어야 해. * 스테이지마다 버텨야 하는 시간이 달라. 버텨야 하는 시간 개념도 만들어야 해. 예를 들어 5분이 주어진다면, 5분동안 죽지 않고 살아있으면 성공, 아니면 실패. 이 버텨야 하는 시간을 테이블에 입력할 수 있어야 해. * 스테이지마다 맵이 달라. 타일맵으로 서로 다른 생김새의 맵을 그릴거야. 이 타일맵 리소스명을 테이블에 입력할 수 있어야 해. * 스테이지마다 등장하는 몬스터의 종류가 달라. 몬스터는 한 스테이지에 여러 종류 있을 수 있으며, 동시에 다 등장하지 않고, 스폰 시작 시점이 달라 (예: 게임 시작 후 1분 뒤부터 스폰되기 시작). 이것도 테이블에 입력할 수 있어야 해. * 스테이지마다 등장하는 몬스터의 수량이 달라. 그리고 한 스테이지에서도 점차 더 많은 몬스터가 스폰되기 시작해야 해. 몬스터를 다 죽이면 버텨야 하는 시간을 다 버티지 않아도 즉시 성공이야. 이것도 테이블에 입력할 수 있어야 해. * 테이블 이름 = Stage.csv (버텨야 하는 시간, 맵 설정) / StageMonster.csv (등장하는 몬스터 종류, 수량, 시점 설정) 이걸 테이블로 구현하고 작동시키는 프롬프트를 짜줘
프롬프트
목표
- 스테이지 공통 정보와 몬스터 스폰 규칙을 분리해 중복을 없애고 확장성을 높인다.
- 파일은 두 개:
1) Stage.csv : 스테이지 공통 정보(시간·맵)
2) StageMonster.csv : 스테이지별 몬스터 스폰 규칙(여러 행 가능)
승패 규칙
- 성공: (1) 버텨야 하는 시간(DurationSec) 동안 생존, 또는 (2) 모든 몬스터 타입의 TotalBudget이 소진되고 필드에 살아있는 몬스터가 0.
- 실패: 플레이어 사망.
데이터 스키마
[Stage.csv]
- 컬럼(헤더 포함):
StageId (int) : 스테이지 식별자
DurationSec (int) : 이 스테이지에서 버텨야 하는 시간(초) (예: 300=5분)
TilemapResource (string) : 타일맵 프리팹/씬 리소스명 (예: "TM_Forest_A")
- 각 StageId는 단 한 행만 존재해야 함(중복 금지).
예시)
StageId,DurationSec,TilemapResource
1,300,TM_Forest_A
2,420,TM_Cave_B
[StageMonster.csv]
- 컬럼(헤더 포함):
StageId (int) : 어떤 스테이지에 속하는지 (외래키)
MonsterId (string) : 몬스터 프리팹/리소스 식별자
SpawnStartSec (float) : 게임 시작 후 이 타입 스폰 시작 시점(초)
WaveIntervalSec (float) : 웨이브 간격(초)
WaveSizeStart (int) : 첫 웨이브 스폰 수
WaveSizeGrowth (int) : 웨이브마다 증가량(+n)
WaveSizeMax (int) : 웨이브당 최대 스폰 수 상한
TotalBudget (int) : 스테이지 전체에서 이 타입이 스폰될 총 수(≥1)
MaxAliveCap (int) : 이 타입이 동시에 필드에 존재 가능한 최대 수(≥1)
- 같은 StageId에 대해 여러 행을 둠 → 다양한 몬스터 타입/규칙 동시 지원.
예시)
StageId,MonsterId,SpawnStartSec,WaveIntervalSec,WaveSizeStart,WaveSizeGrowth,WaveSizeMax,TotalBudget,MaxAliveCap
1,Slime_Green,0,3,3,1,10,80,25
1,Bat,60,2,2,1,8,60,20
1,Wolf,120,4,1,1,6,40,15
2,Slime_Blue,0,2.5,4,1,12,120,30
2,Goblin,90,3,2,2,10,90,25
구현 지시
1) 로더 & 정의
- CSV 두 개를 로드한 후, 다음 구조로 메모리 캐시 구성:
StageDefinition {
StageId,
DurationSec,
TilemapResource,
SpawnRules: List<SpawnRule> // StageMonster.csv에서 StageId 매칭되는 모든 행
}
- 검증:
* Stage.csv 에 없는 StageId가 StageMonster.csv에 있으면 경고 후 해당 몬스터 행 스킵.
* WaveIntervalSec>0, WaveSizeStart≥1, WaveSizeMax≥1, WaveSizeMax≥WaveSizeStart, TotalBudget≥1, MaxAliveCap≥1, SpawnStartSec≥0.
* Stage.csv의 StageId 중복 금지, 누락 금지 로그.
- 리소스 접근은 초기엔 Resources.Load(프리팹/씬)로, 나중에 Addressables 전환 쉽게 인터페이스화(IStageDataProvider/IMapLoader).
2) StageManager
- Start(StageId):
a) StageDefinition 로드
b) TilemapResource 로 맵 로드/배치(기존 맵 정리 후 교체)
c) StageTimer(DurationSec) 시작
d) StageDefinition.SpawnRules 각 항목마다 MonsterSpawner 인
스테이지 테이블에 입력한 버텨야할 시간을 실시간으로 게임 HUD에 표기해주는 코덱스 프롬프트를 짜줘. 내가 유니티에서 해야 할 것도 알려달라고 하고.
목표
- Stage.csv에 입력된 해당 스테이지의 DurationSec(버텨야 하는 시간)을 읽어,
게임 HUD에 "남은 시간"을 실시간으로 mm:ss 포맷으로 표시한다.
- 일시정지(Time.timeScale=0, 예: 레벨업 패널) 동안 타이머는 멈추고, 재개 시 이어서 감소한다.
- 스테이지 성공/실패 시 HUD 동작(숨김/완료 표시)까지 포함한다.
전제/연동
- 스테이지 시스템은 Stage.csv(공통 정보) + StageMonster.csv(스폰 규칙)로 분리되어 있음.
- StageManager가 StageDefinition을 로드해 StageId, DurationSec, TilemapResource 등을 보유하고 있음.
- StageManager는 스테이지 성공/실패를 판정한다.
- TextMeshProUGUI를 HUD에 사용한다.
구현 요구사항
1) StageTimer(남은 시간) 관리
- StageManager(혹은 StageTimerService)에 float remainingSeconds 필드를 두고,
스테이지 시작 시 remainingSeconds = DurationSec(Stage.csv 값)으로 세팅한다.
- 타이머 감소는 Time.deltaTime(스케일 적용)을 사용해 일시정지 시 멈추도록 한다.
- remainingSeconds가 0 이하가 되면 스테이지 ‘성공’ 신호를 발생시킨다(기존 성공 로직과 합치기).
2) 이벤트/인터페이스
- 다음 이벤트를 정의하고 발행한다:
- OnStageStarted(StageDefinition def, float durationSec)
- OnStageTimerChanged(float remainingSec) // 최소 10Hz(0.1초 간격) 혹은 매 프레임, 문자열 재할당 최소화
- OnStageCleared() // 성공
- OnStageFailed() // 실패
- HUD는 이 이벤트를 구독해 텍스트를 갱신한다. (풀링/캐싱으로 GC 최소화)
3) HUD 표시 (StageTimerHUD 컴포넌트)
- 공개 필드:
- TextMeshProUGUI timeText (필수)
- bool blinkOnLowTime = true
- float lowTimeThreshold = 10f // 남은 10초 이하일 때 깜빡이기
- Optional: Slider/Fill 이미지로 진행바(남은 시간 비율) 표시
- 포맷:
- mm:ss (예: 05:00 → 04:59 …)
- 포맷 함수: FormatSeconds(int total) → $"{mm:D2}:{ss:D2}"
- 갱신:
- OnStageTimerChanged에서만 문자열을 갱신(매 프레임 ToString 호출 최소화)
- 남은 시간이 lowTimeThreshold 이하일 때 timeText에 경고 색상/경미한 스케일 펄스 애니메이션 적용(옵션)
- 성공/실패:
- OnStageCleared → “CLEAR!”(또는 숨김) 처리
- OnStageFailed → “FAILED”(또는 숨김) 처리
- 어떤 모드로 표시할지는 public enum UIMode { HideOnEnd, ShowClearText, ShowFailText } 등으로 옵션화
4) 성능/안정성
- 문자열 할당 최소화를 위해 StringBuilder 또는 사전 포맷 캐시(0~3600초) 선택 적용 가능
- 타이머 틱은 0.1초 간격(InvokeRepeating/코루틴)으로도 충분. 정확도가 필요한 경우 매 프레임 처리하되 UI 갱신만 0.1초 간격.
- TimeScale=0 상태에선 remainingSeconds가 감소하지 않아야 한다(스테이지 생존형 룰 유지).
- 씬 전환/스테이지 재시작 시 모든 이벤트 구독 해제/재구독을 안전하게 처리.
5) 수락 기준(테스트)
- Stage.csv의 DurationSec=300이면 게임 시작 즉시 HUD에 05:00이 보이고 1초마다 정확히 감소한다.
- 일시정지 중에는 값이 줄지 않는다. 재개 시 바로 이어서 감소한다.
- 남은 시간이 10초 이하가 되면 텍스트가 시각적으로 경고 상태가 된다(색상/펄스).
- 스테이지가 DurationSec 도달로 성공하면 HUD가 “CLEAR!”로 전환(또는 숨김)된다.
- 스테이지가 플레이어 사망으로 실패하면 HUD가 “FAILED”로 전환(또는 숨김)된다.
- 문자열/GC 스파이크가 프레임 드랍을 유발하지 않는다.