들어가며
godot엔진을 공부하기 위해 (cluade code, antigravity를 사용하였기 때문에 codex로만 작업을 진행.)
이번 작업은 기능 하나를 추가하는 식의 단발성 수정이 아니었다.
스테이지를 계속 늘려갈 수 있는 운영 구조를 만들고, 월드맵 UI를 정리하고, 실제 플레이 체감까지 손보는 쪽에 가까웠다.
정리하면 2026-03-07부터 03-08까지 한 일은 크게 네 가지다.
StageId 단일 int구조를WorldId + StageNumber + StageKey로 확장Draft / Test / Playable상태 기반 운영 구조 도입Stage 2를 다시 설계하고 자동 오케스트레이션에 태움- 월드맵 popup과 선택 시스템을 실제 사용 기준으로 정리
겉으로 보면 서로 다른 작업처럼 보이지만, 결국 하나의 방향으로 연결돼 있다.
“새 맵을 계속 추가해도, 운영 데이터와 작업 중 데이터를 섞지 않고, UI와 테스트 동선까지 버티는 구조를 만들자.”
1. Stage 1 only 구조를 버린 이유
초기에는 사실상 Stage 1만 운영 기준이었다.
문제는 Stage 2부터는 테스트 잔재 데이터와 작업 중 자산이 섞여 있어서, 맵을 늘릴수록 기준이 흐려진다는 점이었다.
예전 방식의 문제는 명확했다.
StageId단일 int에 모든 의미가 몰려 있었다- 운영 스테이지와 작업 중 스테이지를 코드가 구분하지 못했다
- 월드맵, 저장, 결과 복귀, 스킬 조건, 보스 이벤트가 모두 같은 키 체계에 기대고 있었다
Stage 2를 테스트하려고 상태를 억지로 운영 상태처럼 다뤄야 했다
그래서 이번에는 아예 구조를 바꿨다.
public readonly struct StageKey
{
public readonly int WorldId;
public readonly int StageNumber;
}
여기에 StageContentStatus를 붙였다.
public enum StageContentStatus
{
Draft,
Test,
Playable
}
핵심은 단순하다.
Draft: 제작 중Test: 내부 테스트용Playable: 실제 운영 가능
이렇게 나누고 나니 월드맵, 게임씬, 저장, 에디터 도구가 같은 기준으로 움직이기 시작했다.
2. Stage 2를 다시 “쓸 수 있는 초안”으로 만든 과정
Stage 2는 맵 경로와 타워 슬롯은 이미 들어가 있었지만, 웨이브 데이터는 예전 테스트 흔적이 많이 남아 있었다.
처음 확인한 문제는 이런 식이었다.
- 현재 맵은 단일 경로인데 웨이브는 존재하지 않는
PathId를 참조 - 테스트용 낮은 시작 자원과 임시 조합이 섞여 있음
- 실제 스테이지 초안이라기보다 실험 데이터에 가까움
그래서 목표를 먼저 잠갔다.
- 역할:
학습 확장형 - 상태:
Draft - 목표:
초심자 1회 클리어 가능 - 구조:
7 waves,PathId 0 only - 규칙:
Wave 1~6 최소 8마리
초기 초안은 자동 회귀 기준으로는 파손이 없었다.
문제는 실제 플레이였다.
직접 해보니 초반 아처 3개를 전방에 깔면 이후 추가 건설 없이도 클리어가 가능했다.
이건 두 가지를 의미했다.
- 시작 골드가 많다
- 중후반 압박이 약하다
그래서 2차 조정에서는 방향을 분명하게 잡았다.
InitialGold: 360 -> 280Wave 4~7물량 상향- 초반
Wave 1~3는 유지
최종 초안은 이렇게 정리했다.
InitialGold = 280
InitialLives = 75
W1: Goblin x8
W2: Goblin x8 + Scout x2
W3: Goblin x8 + Scout x2
W4: Goblin x10 + Orc x4
W5: Scout x8 + Goblin x6
W6: Goblin x10 + Orc x5
W7: Goblin x10 + Orc x4 + Shaman x2
아직 Playable은 아니다.
하지만 최소한 “작업 중 Draft 자산”이 아니라, 실제로 검증 가능한 초안 상태까지는 끌어올렸다.
3. 테스트 동선을 바꾼 이유
구조를 엄격하게 만들면 보통 테스트가 불편해진다.
실제로 Draft/Test를 운영 경로에서 막아버리면, 개발자는 새 스테이지를 확인할 때마다 상태를 바꾸거나 별도 메뉴를 타야 한다.
이건 스테이지가 하나둘일 때나 참을 수 있다.
그래서 운영 원칙과 개발자 동선을 분리했다.
- 운영 기준:
Playable만 시작 가능 - 개발자 기준: 에디터/개발 빌드에서는
Draft/Test도 월드맵에서 직접 시작 가능
이렇게 해두니 두 마리 토끼를 같이 잡을 수 있었다.
- 운영 구조는 안 무너진다
- 개발자는 월드맵에서 바로 새 스테이지를 눌러 테스트할 수 있다
이 작업은 단순 편의 기능이 아니라, 콘텐츠 생산 속도를 떨어뜨리지 않기 위한 안전장치에 가까웠다.
4. 월드맵 popup은 결국 프리팹 기준으로 정리했다
이번 구간에서 의외로 시간을 많이 쓴 부분이 월드맵 popup이었다.
문제는 두 가지였다.
4.1 한글 깨짐
처음엔 폰트 문제처럼 보였는데, 실제 원인은 코드에서 런타임으로 만드는 문자열 일부가 깨져 있던 쪽이었다.총 별, 사용 별, 남은 별, 강화, 별이 부족합니다 같은 문구를 정상 한글로 정리하면서 바로 잡았다.
4.2 유지보수 방식
더 큰 문제는 UI 레이아웃을 코드가 런타임에 자꾸 다시 만들고 밀어넣는 구조였다.
이 구조는 처음엔 빠르지만 결국 이런 문제가 생긴다.
- 프리팹을 수정해도 코드가 덮어쓴다
- 어떤 값이 실제 기준인지 파악하기 어렵다
- 참조가 깨져도 fallback UI가 조용히 뜬다
- 버그가 “즉시 드러나지 않고” 나중에 이상한 형태로 나타난다
그래서 방향을 바꿨다.
SkillTreeUI,HeroSelectionUI를 프리팹 기준 구조로 전환- 빌더 메뉴로 프리팹 재생성 가능하게 정리
- 최종적으로 런타임 fallback 제거
- 참조 누락 시
Debug.LogError + 중단
이제는 프리팹이 기준이고, 문제가 있으면 바로 드러난다.
유지보수 관점에서는 이쪽이 훨씬 낫다.
5. 선택 체감: “보이는 걸 눌렀는데 왜 안 눌리지?”
플레이하다 보면 가장 거슬리는 버그 중 하나가 이런 종류다.
“오브젝트 이미지를 눌렀는데 선택이 안 된다.”
원인을 보면 대개 단순하다.
보이는 스프라이트를 누르는 게 아니라, 더 작은 충돌 범위나 중심점 거리를 누르고 있기 때문이다.
이번에는 선택 판정을 SpriteRenderer.bounds 기준으로 보강했다.
즉,
- 영웅
- 적
- 병사
- 타워
모두 “실제로 보이는 이미지”에 더 가깝게 선택되도록 만든 것이다.
그리고 여기서 한 걸음 더 가서, 타워를 선택하면 공격 범위가 따로 보이도록 했다.
- 선택 원과 범위 원은 분리
- 일반 타워는 공격 범위
- 배럭은
RallyRange - 선택 해제 시 즉시 숨김
처음엔 범위용 스프라이트 로드가 안 되어 Hierarchy에는 있는데 게임 화면에는 안 보이는 상태가 있었다.
원인은 새 이미지의 임포트 설정이 Multiple Sprite였던 쪽이었고, Single로 정리하면서 해결했다.
이건 작은 디테일 같지만 체감에는 꽤 중요했다.
“선택됐다”와 “어디까지 닿는지 안다”는 플레이 감각이 완전히 다르기 때문이다.
6. 오케스트레이션을 같이 굴린 이유
이번 작업에서 개인적으로 가장 중요하게 본 건, 기능을 넣는 것보다 회귀를 같이 잠그는 것이었다.
구조 변경, UI 변경, 밸런스 변경은 서로 영향을 많이 준다.
이럴 때 수동 테스트만 믿으면, 당장은 보여도 나중에 어디서 깨졌는지 찾기 어려워진다.
그래서 가능한 건 오케스트레이션에 같이 태웠다.
Selection UI smokeStage1 PipelineStage2 Draft PipelineWorldMap Meta Popup RegressionBattleSim Economy Checklist
그리고 포커스를 잃으면 회귀가 멈추는 문제도 같이 잡았다.
프로젝트가 기본적으로 runInBackground: 0이라서, 회귀 실행 동안에는 Application.runInBackground = true를 강제로 켜고 끝나면 복원하도록 보강했다.
결국 이번 이틀 작업은 “기능 추가”라기보다, 작업 흐름 전체를 덜 깨지게 만드는 쪽에 더 가까웠다.
7. 이번 작업에서 얻은 정리
이번 구간에서 특히 확실해진 건 세 가지다.
1. 운영 구조와 개발자 편의는 분리해야 한다
운영 기준을 느슨하게 하면 결국 데이터가 섞인다.
반대로 너무 엄격하게 막으면 개발이 느려진다.Draft/Test/Playable + 개발자 전용 월드맵 시작 게이트 조합은 이 균형을 잘 잡아줬다.
2. UI는 결국 프리팹이 기준이어야 한다
코드 fallback은 프로토타입 단계에서는 빠르지만, 운영 단계에서는 문제를 숨기는 쪽으로 작동한다.
지금처럼 popup 구조가 정리된 뒤에는 fail-fast가 맞다.
3. 자동 회귀는 기능이 아니라 작업 속도다
선택 판정, popup, 웨이브 초안, 월드맵 진입처럼 서로 얽힌 변경이 많아질수록
“한 번에 돌려보고 상태를 잠글 수 있는 흐름” 자체가 생산성이다.
8. 다음 작업
다음 우선순위는 명확하다.
Stage 2실제 플레이를 3~5회 더 돌린다3아처 방치 클리어가 완전히 깨졌는지 확인한다- 필요하면
Wave 5~7만 추가 미세 조정한다 - 이후
Stage 2를Test로 올릴지 판단한다
그리고 선택/범위 표시 쪽은 이제 체감이 안정적이면 큰 구조 변경보다는 시각 튜닝 단계로 넘어가면 된다.
마무리
이번 작업은 눈에 확 들어오는 새 기능보다, 프로젝트가 앞으로 더 많은 스테이지와 UI 변경을 감당할 수 있게 바닥을 다시 까는 작업에 가까웠다.
특히 좋았던 건, 코드를 바꾼 뒤 끝내지 않고
- 자산
- 프리팹
- 문서
- 자동 회귀
- 실제 플레이 피드백
을 같이 묶어서 잠그는 흐름이 조금씩 자리를 잡기 시작했다는 점이다.

'AI > Unity' 카테고리의 다른 글
| 농장 경영 시뮬레이션 게임 만들기 - 1 (0) | 2026.04.19 |
|---|---|
| 🛠️ Unity SRPG 개발 로그: 데이터 구조 대확장 — 직업, 보스, 챕터, 조합 시스템 (1) | 2026.04.05 |
| [Unity] 타워디펜스 맵 & 웨이브 에디터 (0) | 2026.03.02 |
| 타워 디펜스 만들기 - 맵 이미지 프롬프트 (1) | 2026.03.01 |
| Antigravity를 사용하여 타워 디펜스 게임 만들기 - 2 (전장 확장 및 스크롤 카메라 구현기) (0) | 2026.02.26 |