AI만 사용하여 멀티플레이어 추출 RTS 게임 개발하기: Codex(GPT Astra) 활용기 및 초기 프롬프트
Developing a multiplayer extraction RTS using AI only, with Codex (GPT Astra). What I learned and the prompt it started with
핵심 요약
AI 에이전트들을 활용해 5주 만에 Rust 기반의 브라우저 멀티플레이어 추출 RTS 게임을 개발한 과정과 핵심 프롬프트를 공유합니다.
AI 개발 방식 — Codex와 Claude Code를 활용해 5,700번의 커밋과 85만 줄의 Rust 코드를 작성함
기술 스택 — Rust와 WASM을 사용하여 시뮬레이션부터 렌더링까지 브라우저 환경에서 고성능 구현
자동화된 워크플로우 — 게임 내 데이터 분석, 영상 제작, 커뮤니티 피드백 루프를 AI로 자동화함
엄격한 품질 관리 — CI/CD 파이프라인을 통해 테스트 커버리지, 뮤테이션 테스트, 퍼징 등 높은 기술적 기준을 강제함
지난 5주 동안 AI만 써서 브라우저 기반 멀티플레이어 익스트랙션 RTS 게임인 'Out of Pixkrieg'를 만들었다. GPT Astra를 얹은 Codex랑 Opus 5.5를 쓰는 Claude Code가 실무를 다 하고, 나는 프롬프트 날리고 결정만 내리는 식이다.
게임 루프는 간단하다. 절차적으로 생성된 유닛들을 데리고 장비 챙겨서 레이드 나가는 거다. 거기서 파밍하고, 채굴하고, 포로 구출하고, 산적이나 AI 파티, 다른 플레이어들이랑 싸우면 된다. 살아서 탈출한 유닛만 전리품을 챙겨서 돌아올 수 있고, 죽은 놈들은 그냥 끝이다. 무사히 가져온 전리품은 기지랑 유닛 강화에 쓴다. PvPvE 방식이고, 전부 브라우저 탭 하나에서 돌아간다.
셋업
Codex랑 Claude Code 둘 다 VS Code 확장 프로그램으로 깔아서 같은 레포지토리에서 돌렸다. 내가 오케스트레이터한테 프롬프트를 던지면, 얘들이 하위 에이전트들한테 일을 시키고 방향을 잡아주는 구조다. 핵심은 두 툴이 다 읽어들이는 AGENTS.md 파일이다. 여기엔 황금률(코드 중복 금지, 기성 에셋 금지, 엔진 렌더러는 최소한의 8비트 UI만 사용)이랑 빡센 규칙들(clippy pedantic에 -D warnings 걸기, forbid(unsafe_code), 문서화 필수, 커버리지랑 뮤테이션 스코어 하한선, 퍼징)이 적혀 있고, 내가 직접 결정할 것들도 명시해 뒀다. cargo xtask ci 한 번이면 모든 관문을 통과해야 하고, 테스트 통과 못 하거나 독립적인 검토를 거치지 않으면 머지 자체가 안 된다.
에이전트들이 만든 것들
픽셀 유닛, 동물, 지형, 깃발, 아바타, 스킬(건물, 워커, 아이템, 이름, 픽셀 폰트, 사운드 포함)을 위한 절차적 생성기
기지와 레이드 시스템, 권한 있는 레이드 서버, 계정, 매치메이킹, 데이터 영속성을 갖춘 브라우저 멀티플레이어
디스코드 봇, 플레이테스트 대시보드, 그리고 내 개발 로그 영상을 만들어주는 파이프라인
지금까지 커밋 5,700번 정도 찍었고, 테스트 포함해서 Rust 코드만 85만 줄 정도 된다.
스택, 그리고 왜 이걸 또 쓸 건지
시뮬레이션, 넷코드, 생성기, 렌더러, UI, 오디오까지 전부 Rust로 짰다. 클라이언트는 wgpu를 써서 WASM으로 컴파일했고(WebGPU랑 WebGL2 폴백 빌드), 껍데기만 가볍게 TypeScript를 썼다. 서버는 tokio랑 axum, WebTransport에 WebSocket 폴백, Postgres, Redis, Docker Compose, Caddy 조합이다.
Rust + WASM 조합은 내가 지금까지 해본 '바이브 코딩' 중에 단연 최고다. 깐깐한 컴파일러가 제일 싼 리뷰어거든. 에이전트들이 싼 똥 대부분이 선에서 걸러진다. 여러 크레이트에 걸친 리팩토링도 안전하고, 같은 코드가 네이티브랑 브라우저에서 다 돌아가니까 테스트는 네이티브에서 빠르게 돌리고, 클라이언트는 서버랑 똑같은 로직(길 찾기, 규칙 등)을 그대로 실행할 수 있다.
처음부터 측정해라. 게임은 퍼널을 기록하고 모든 디자인 목표에는 타겟 범위가 있다. 예를 들면, 첫 명령까지 10초(측정값 20초), 5~8분짜리 튜토리얼(2.9분), 레이드당 유닛 손실률 15~20%(7.3%), 맵 중심부 손실률 40~60%(6%, 제일 위험한 곳이 제일 안전했음). 퍼널을 보니까 플레이어 절반이 튜토리얼에서 나가떨어지더라. 피드백엔 아무도 안 적어준 내용이었다. 에이전트들이 수치 뽑아내고 튜토리얼 싹 갈아엎었더니, 지금 플레이테스트에선 70%가 통과한다. 딱 한 번 고쳤는데 확실히 좋아졌다.
콘텐츠를 자동화해라. 난 영상 편집도 싫고 마케팅 자료 만드는 것도 소질 없다. 클립 따본 적도 없고 SNS 채널도 없었다. 근데 지금은 에이전트들이 만든 개발 로그 파이프라인이 게임에서 결정론적인 장면을 찍고, 로컬 TTS 내레이션으로 편집하고, Whisper로 음성 검수하고, 측정 가능한 QC까지 돌린다. 쇼츠, 클립, 비포/애프터 비교까지 힘 안 들이고 다 된다. 아직 완벽하진 않아도 API 호출 없이 풀로 돌아가고, AI 리뷰까지 얹으니까 퀄리티도 괜찮다.
커뮤니티를 게임 안에 녹여라. 예전 프로젝트인 ExoSiege 때 디스코드 링크가 효과가 좋았거든. 이번엔 인게임 채팅이랑 디스코드 채팅을 아예 하나로 합쳤다. 게임 안에서 피드백 남기면 디스코드 포럼 스레드로 올라가고, 거기 달린 댓글이 다시 게임으로 들어온다. 여기저기 흩어질 필요 없이 한곳에서 다 해결된다.
시작할 때 쓴 프롬프트
수많은 프롬프트를 거쳤지만, 결국 이 모든 건 2~3일 만에 세션 하나로 전체 베이스를 뽑아낸 아주 상세한 프롬프트 하나에서 시작됐다. 독일어로 작성된 걸 번역했고, 경로랑 기계적인 세부 사항은 뺀 채로 이 글 맨 아래에 적어뒀다.
개요: 실전 투입 가능한 브라우저 기반 RTS 익스트랙션 게임 ("RTS 버전 타르코프")
0. 개요 및 작업 방식
이 디렉토리에서 너는 브라우저에서 돌아가는 완벽하고 실전 투입 가능한 멀티플레이어 게임을 만든다. 전체 스택은 로컬과 운영 환경 모두 Docker Compose 위에서 돌아간다. 네이티브 데스크톱 빌드나 스팀 출시는 나중 일이고 이번 과업에는 포함되지 않지만, 아키텍처는 처음부터 그 길을 열어두도록 설계한다(6.1).
코드, 아키텍처, 성능, 넷코드, 보안, 게임 디자인, 그래픽, UX, 오디오, 운영 등 모든 분야에서 타협 없이 최고 수준을 유지한다. 4절은 모든 코드 한 줄과 모든 결정 사항에 적용된다.
완료 조건(13)을 충족할 때까지 완전 자율적으로 작업한다.
일상적인 결정은 스스로 내리고, 기록하고, 계속 작업한다. 질문하려고 멈추지 마라. 14절에 명시된 경우만 예외다.
사전 지식 없이 시작한다. 관련 내용은 모두 여기에 있고, 참조 저장소는 마지막에 첨부되어 있다.
환경: Windows 11, PowerShell 5.1 및 Git Bash, 6코어 데스크톱 CPU, 하이엔드 GPU, 64GB RAM, MSVC 빌드 도구. Docker Desktop(Hyper-V) VM(~12GB)은 호스트 포트를 점유하는 다른 상시 실행 스택과 공유되므로, 리소스 제한과 포트 충돌 방지는 필수다. 툴체인은 시스템 전체가 아닌 프로젝트 로컬에 설치한다.
1. 게임 컨셉 (사용자 확정)
장르: 오픈 월드 RTS 익스트랙션, "RTS 버전 타르코프". PvP를 포함한 완전한 멀티플레이어.
플랫폼: 일단 브라우저에서 바로 누구나 플레이할 수 있게 한다. 나중에 같은 코드베이스로 네이티브 클라이언트와 스팀 버전을 출시하며, 플레이어 풀은 공유한다(이번 과업 범위 아님).
레이드: 세션 기반. 레이드 인스턴스는 1~2시간 동안 열려 있고, 플레이어들은 자유롭게 드나든다(난입 가능).
맵 구성: 곳곳에 널린 소규모 산적 캠프(PvE), 채굴 가능한 자원 노드(돌, 나무 등), 신규 인원을 모집하는 상점이 있는 안전 지대, 인원들을 데리고 탈출하는 탈출 지점, 다른 플레이어 및 그들의 군대와 벌이는 PvP.
유닛: 모든 유닛은 절차적으로 생성되며 외형, 역할, 장비, 이름이 전부 제각각이다.
레이드에서 고용한 신병은 전리품이다. 고용 즉시 전투에 투입되지만, 탈출해야만 플레이어의 소유가 된다.
사망하면 유닛은 영구적으로 소멸한다. 플레이어는 항상 기본 유닛 3기로 시작할 수 있으므로, 완전히 망해서 게임을 못 하는 상황은 없다.
"유닛 봉봉"을 까면 랜덤 유닛이 나온다. 희귀도는 생성기 티어(신병, 베테랑, 엘리트, 영웅)를 따른다.
로비 / 기지: 탈출시킨 유닛들은 기지에서 수동적으로 자원을 생산하며, 이는 모집과 제작에 쓰인다. 핵심 긴장 요소: 사람들을 다시 위험한 곳으로 보낼 것인가, 아니면 안전하게 생산을 맡길 것인가.
규모: 처음부터 대규모 인원을 상정하고 계획한다. 대규모 군대는 나중에 추가되는 기능이 아니라 기본 사양이다.
호스팅: 독일 소재 임대 서버, 사용자 PC에서 RTT 약 15ms.
여기서 정해지지 않은 모든 사항은 2절의 원칙에 따라 docs/GDD.md에 직접 설계한다: 제작, 아이템, 전리품 테이블, 경제, 성장, 레이드 입장 및 종료, 진영, 분대, 맵 크기, 튜토리얼, 소셜 기능. 모든 결정과 밸런스는 시뮬레이션과 봇 테스트를 통해 정당성을 입증한다.
2. 디자인 원칙
리스크와 보상: 인원을 데려갈지 결정할 때마다 그 무게감이 느껴져야 한다.
모든 유닛은 소중하다: 절차적 정체성과 영구적 죽음이 애착을 만든다.
대규모 상황에서의 가독성: 배경보다 캐릭터를 우선하고, 분대 단위로 묶으며, 줌아웃 시 상세 수준(LOD)을 조절한다.
설계 단계부터 지연 시간 고려: 반사 신경을 요구하는 마이크로 컨트롤 대신, 시전 시간, 예고된 능력, 서버 측 태세를 활용한다.
최소한의 직관적인 UI.
공정성: 서버가 모든 것을 결정하며, 페이 투 윈(Pay-to-Win)은 없다.
3. 황금률 (협상 불가)
코드는 절대 중복 안 됨. 장기적인 유지보수를 위해 항상 제대로 추상화함.
기성품 에셋은 안 씀. 기존 생성기를 쓰거나 새로 만듦. 지형, 폰트, 사운드, 음악, 아이콘, UI 그래픽 전부 포함.
UI 관련 모든 건 직관적이고 미니멀하고 단순하게. 8비트 캐릭터 스타일이랑 어울리는 독특한 스타일 유지.
CI가 규칙을 기계적으로 강제함: Rust와 TypeScript 전반에 걸친 중복 탐지(예: jscpd)는 고정 임계값 적용. 모듈 계층 구조(7절) 확인을 통해 플랫폼 크레이트 외부의 브라우저 코드가 빌드를 깨뜨리지 않도록 함. 테스트용 참조 파일이 담긴 폴더 하나를 제외하고는 저장소에 이미지, 오디오, 폰트 파일 절대 금지. UI는 손으로 설계하되 코드로 구현함. 공유 팔레트 토큰을 쓰고, 프레임은 코드로 짜며, 아이콘은 생성기로, 픽셀 폰트는 글리프 데이터로 처리함.
4. 품질 기준: 예외 없이 최고 수준
기준은 '괜찮은 프로토타입'이 아니라 해당 장르의 '최고 상업용 타이틀'임. "돌아간다"고 해서 "완성"된 게 아님. 뛰어나고, 측정 가능하며, 독립적인 검토를 거쳐야 완성임. "이 정도면 됐지" 싶으면 그건 완성된 게 아님.
원칙:
main 브랜치에 꼼수 금지. CI로 체크함: 플레이스홀더, 모의 객체(mock), 스텁, 주석 처리된 코드, TODO, FIXME, todo!(), unimplemented!(), "나중에 할게" 같은 거 일절 없음.
모든 컴포넌트는 해당 분야 최고 전문가가 서명한다는 생각으로 만듦.
중요한 결정은 최소 두 가지 이상의 진지한 대안을 검토함. 가능한 한 측정하고 그 결과를 ADR에 기록함.
기준 미달인 결과물은 입만 털지 말고 개선함.
단순함이 곧 품질임. 모든 요구사항을 훌륭하게 충족하는 가장 단순한 솔루션. 이득 없는 추상화는 없고, 필요 없는 의존성도 없음.
CI의 기술적 관문:
Rust:rustfmt; 선택적 pedantic 린트를 포함한 clippy -D warnings; 기본적으로 #!\[forbid(unsafe\_code)\] 적용(unsafe는 서면 사유, SAFETY 주석, Miri 테스트가 있어야만 허용); 모든 라이브러리 크레이트에 #!\[deny(missing\_docs)\] 적용.
TypeScript 셸: strict 모드, 경고 없는 린트. Docker: Dockerfile 린트(hadolint), Compose 설정 검증.
빌드: 경고 0개. 불안정한 테스트(Flaky tests): 0개. 불안정한 테스트는 곧 버그임.
커버리지: foundation, generators, rules, net, meta에서 라인 기준 90% 이상(cargo-llvm-cov). 이건 목표가 아니라 최소 하한선임.
변이 테스트(Mutation testing): rules, economy, transactions, netcode에 대해 cargo-mutants 사용, 90% 이상 검출.
속성 테스트(Property tests): 모든 불변 조건(자원 중복 금지, HP 범위 유지, 트랜잭션 잔액 일치 등)에 대해 수행.
퍼징(Fuzzing): 모든 디코더와 파서(cargo-fuzz)에 적용: 네트워크 프로토콜, 저장 형식, 생성기 사양. 정기적인 CI 실행, 미해결 발견 사항 0건.
공급망:cargo-deny(라이선스, 권고 사항, 중복 버전 확인), 최소한의 고정된 의존성, 고정된 베이스 이미지, 재현 가능한 빌드.
성능: CI에서 p50 및 p99 예산 설정, 벤치마크 씬에서 예산의 2배를 넘는 프레임 금지, 메모리 예산 및 GPU 시간 측정.
리뷰를 통한 제품 관문:
스코어카드: 그래픽, UX, 게임 감각, 오디오, 게임 디자인 각각 최소 9/10 이상. 최소 두 명의 독립적인 리뷰어 서브에이전트가 점수 매김(의견 불일치 시 세 번째 에이전트가 결정).
게임 감각: 동일 프레임 내 로컬 피드백, 모든 행동에 대한 시각적/청각적 피드백, 깔끔한 픽셀 그리드(정수 스케일링, 서브픽셀 지터 없음, 블러 없음).
최종 패널: 분야별 전문가 서브에이전트 1명씩(게임 디자인, 아트 디렉션, 렌더링, 시뮬레이션, 넷코드, 보안, UX, 오디오, 운영). 각자 최소 9/10 이상으로 승인해야 함; 모든 발견 사항은 수정되거나 ADR에 사유가 기록되어야 함.
5. 시작 자료: 참조 저장소 (읽기 전용)
맨 끝에 첨부된 저장소는 유닛 및 구조물 생성기가 포함된 유일한 시작 자료야. 지형을 포함한 나머지는 전부 새로 만들었어. 이건 절대 수정 안 함. 참조 실행을 하려면 git-ignored 캐시 디렉토리에 복사해서 쓰면 돼.
Godot 4.5 .NET / C#, 엔진 독립적 코어, 골든 테스트, 콘솔 하네스. README랑 docs/부터 읽어. 스타일 결정 사항들이 거기 다 적혀 있으니까.
페인팅 로직: 색상 대신 재질과 톤 단계로 구성된 캔버스 사용. 5단계 OKLCH 램프를 쓰는데, 그림자는 청자색(285°)으로, 하이라이트는 따뜻한 노란색(85°)으로 변함. 전역 외곽선 색상은 Oklch(0.215, 0.038, 282) 하나로 통일. 빛은 왼쪽 상단에서 들어오고, 모양별로 볼륨 셰이딩 적용. 재질은 흙 느낌으로 가고, 팀 컬러랑 보석 색상만 채도를 높였어.
측정치: 몸체 중앙값 14 px (무기 포함 18 px, 너비 17 px); 아틀라스당 148 프레임 (5개 클립 × 3개 저장 방향, 서쪽은 미러링); 유닛당 약 34 ms (네이티브, 싱글 코어); 팔레트 인덱스 포함 약 7 KB 다운로드 (Brotli). 이름 생성기도 따로 있음.
포팅: 유닛, 아이템, 이름, 구조물 생성기는 Rust로 재구현했고 C# 원본과 비트 단위로 정확히 일치하는지 검증함. C# 하네스가 참조 해시(예: 1만 개 이상의 시드에 대한 모든 프레임의 ContentHash)를 생성하면, Rust도 네이티브 및 WASM 빌드에서 똑같이 맞춰야 해. 그 이후부터는 Rust가 표준이야. 원본의 버그는 고치지 말고 docs/UPSTREAM_ISSUES.md에 기록해 둬.
6. 기술 아키텍처
이 방향성은 2026년 10월 3일에 성능을 최우선 기준으로 분석했고, 일부 측정까지 마쳤어. 먼저 스파이크(M1)로 검증해 봐. 만약 측정 결과가 결정 사항을 반박한다면, 더 나은 대안을 선택하고 ADR에 그 이유를 적어놔.
기각됨:
Option
Reason
Godot
no stable C# web export, WebGL2 only, large downloads
three.js
3D engine, wrong tool for 2D pixel sprites
PixiJS
the scene graph becomes the bottleneck with massive armies
TypeScript renderer with DOM UI
a later native/Steam client would rewrite renderer and UI, violates rule 1
Node server
same kernel 1.4–1.7× slower than C#, hard to parallelise
Lockstep
trivial map hacks, the slowest player slows everyone, determinism across browsers unreliable
6.1 개요
모든 것을 위한 단일 Rust 코드베이스: 시뮬레이션, 넷코드, 생성기, 렌더러, UI, 오디오 로직.
클라이언트:wgpu 기반의 Rust, WASM으로 컴파일. WebGPU를 기본으로 하고 WebGL2를 폴백으로 사용, 셰이더는 WGSL. 여기에 로딩, 캔버스, 브라우저 API, 텍스트/IME/클립보드용 숨겨진 입력 필드를 처리하는 가벼운 TypeScript 셸 추가.
플랫폼 계층: 윈도우/캔버스, 입력, 네트워크 전송, 오디오 출력, 저장 및 로그인은 트레이트(trait) 뒤에 숨김. 현재는 브라우저 구현체만 존재함. 엔진과 게임 크레이트는 플랫폼 중립적으로 유지(테스트는 네이티브에서 실행)하므로, 네이티브 클라이언트나 Steam은 포팅이 아니라 추가 타겟이 됨.
영속성: Postgres 사용. 휘발성 상태는 Redis에 저장.
6.2 시뮬레이션
모든 레이드 인스턴스는 레이드 호스트 하위의 개별 프로세스로 돌아가며, 데이터 지향 ECS, 10~20 Hz 고정 틱, 모든 코어를 활용하는 작업 시스템을 사용함.
이동 및 전투: 공간 그리드, 엇갈린 타겟 탐색, 분리(separation) 적용; 근접 유닛은 고정됨.
측정치 (경로 탐색과 시야가 없는 C# 프로토타입, 12 스레드): 유닛 5만 개당 틱당 5.4 ms, 10만 개당 9.2 ms (p99 17 ms). 모든 기능 포함 시 2~3배 정도 더 걸릴 것으로 예상.
레이드 상태는 30~60초마다 Postgres에 저장되고 크래시 발생 시 자동으로 복구됨: 플레이어의 1~2시간 투자와 영구적 죽음(permadeath)이 걸려 있으니까.
6.3 넷코드
Curve replication: Planetary Annihilation처럼 스냅샷을 쓰는 대신 곡선 복제 방식을 씀. 세그먼트는 시작 지점, 속도, 시작 시간으로 구성되고, 공격이나 피격, 사망 같은 건 전부 이벤트로 처리함.
Interest management: 서버는 딱 보이는 것만 보내줌. 전장의 안개(Fog of war)도 서버에서 계산하니까 맵핵 같은 건 아예 불가능함. 뷰포트 안은 풀 정밀도로 보여주고, 멀어질수록 정밀도를 낮추며, 줌아웃하면 분대 단위 데이터만 보냄.
Tolerance: 오차 범위는 2px, 보정은 부드럽게 블렌딩함.
Measured: 필드 전투에서 유닛 1000기 기준으로 측정했을 때, 곡선 방식은 약 15 KB/s에 이벤트 2 KB/s 정도인데, 스냅샷 방식은 53~78 KB/s나 처먹음.
Transport: WebTransport로 스트림이랑 데이터그램을 따로 쓰고, 안 되면 WebSocket으로 완전히 폴백함. 둘 다 똑같이 테스트 완료. 바이너리 방식에 양자화된 프로토콜을 쓰고, 중간 난입이나 재접속을 위해 키프레임도 넣음.
6.4 Latency design
목표: RTT 150ms까지는 즉각 반응, 500ms까지는 문제없이 플레이 가능하게.
Local and instant: 카메라, 유닛 선택, UI, 명령 마커, 확인 애니메이션은 즉시 반응.
Predicted: 내 분대는 명령 내리자마자 바로 움직임. 클라이언트 WASM에서 서버랑 똑같은 경로 탐색 알고리즘을 돌리거든. 서버는 클라이언트 타임스탬프 기준으로 명령을 평가하는데, 250~300ms로 제한을 걸어서 예측이랑 서버 결과가 딱 맞게 함.
Authoritative: 데미지, 사망, 루팅, 상점, 탈출 같은 건 서버가 절대 권한을 가짐.
Attack wind-ups hide latency: 근접 공격은 타격 프레임까지 275~305ms, 원거리 공격은 490~580ms에 투사체 비행 시간까지 더함. 메시지가 늦게 도착하면 클라이언트에서 애니메이션을 늦게 시작하는 식으로 처리함.
Design rules: 채널링 시간은 최소 RTT 예산만큼 길게 잡음(루팅, 채굴, 탈출은 10~30초). 스킬은 최소 0.5초 전에 미리 예고함. 서버 사이드 스탠스(체력 X% 이하일 때 퇴각, 거리 유지, 집중 공격 등)를 넣어서 반응 속도 차이를 메꿈.
개발 첫날부터 Latency emulator 도입: 프로필 4개로 테스트함. EU 20ms, 해외 150ms(패킷 손실 1%), 먼 곳 300ms(패킷 손실 2%, 지터 ±50ms), 최악의 경우 500ms(패킷 손실 2%, 지터 ±100ms). 500ms에서도 렉 걸리는 느낌 없고, 4px 넘는 눈에 띄는 보정 없고, 명령 내린 프레임에 유닛이 바로 움직이고, 전투 상황도 잘 보임.
6.5 Client and renderer (Rust on wgpu, WASM)
GPU-driven: 유닛 하나하나가 GPU 버퍼의 엔트리 하나임(곡선, 클립, 클립 시작, 방향, 디자인, 팔레트 행, 플래그). 버텍스 셰이더가 위치랑 프레임을 계산하고, CPU는 이벤트 발생할 때만 값을 씀.
Palette-index atlases (R8): 텍스처 배열에 팔레트 텍스처를 더해서 씀. 팀 컬러, 피격 효과, 디더링 페이드 같은 건 전부 셰이더에서 돌림. 유닛당 RGBA로 약 294 KB 먹던 걸 75 KB 정도로 줄임.
Depth test instead of Y sorting (binary transparency): 모든 유닛을 드로우 콜 한 번에 처리하고, 그림자는 별도 패스로, 구조물이나 절벽에 가려지는 건 깊이 값으로 처리함.
WebGL2 fallback:wgpu의 WebGL2 기능 세트 안에서 돌아감. 스토리지 버퍼가 없으면 데이터 텍스처를 쓰고, 셰이더 변형은 소스 하나에서 생성해서 중복 작성 안 함.
Massive armies: 보병은 분대나 타입별로 디자인을 공유함(레이드당 64~256개). 유닛마다 팔레트 행을 다르게 해서 다양성을 줌. 영웅은 개별적으로 처리하고, 전략 줌 레벨에서는 배너나 점으로 표시함.
Threads: 기본 스레드 하나에 필요할 때만 웹 워커를 씀. SharedArrayBuffer는 측정 결과 꼭 필요할 때만 쓰는데, COOP/COEP 헤더 때문에 임베딩이 빡세지니까 조심해야 함.
WASM size: 처음부터 로드 예산 맞춰서 측정함. Browsers: 데스크톱용 크롬, 엣지, 파이어폭스, 사파리 최신 버전 지원.
6.6 Generators and content
모든 생성기는 Rust로 짰고, 모듈마다 워크숍 웹 앱을 하나씩 붙임.
기반(Foundation): 결정론적 수학(비트 단위 일치), 시드 스트림, StableHash, 노이즈, OKLCH, 램프 및 데이터로서의 스타일, 머티리얼 캔버스, 페인터, 팔레트 인덱스 스프라이트 및 아틀라스 포맷, 인코더.
유닛(Units), 아이템(무기, 장비, 인벤토리 아이콘) 및 이름, 그리고 구조물(structures)(부품 및 사이트), 포팅 완료(섹션 5): 난이도별 산적 캠프, 상점과 모집병이 있는 안전 지대, 탈출 지점. 모든 구조물은 충돌 판정, 시야 차단, 깊이, 그림자 및 앵커 슬롯을 제공함.
지형(Terrain): 새로 만든 우리만의 미니멀리스트 방식(6.7).
식생 및 소품(자원 노드 포함, 신규): 장식용 및 채집 가능한 객체(나무, 덤불, 바위, 광석)를 위한 통합 생성기. 채집 가능이란 플래그, 고갈 단계, 가독성 신호를 의미함. 유닛과 동일한 페인팅 로직 적용.
맵 레이아웃(신규, 게임플레이 우선): 난이도별 캠프, 자원 필드, 안전 지대, 탈출 지점, 공정한 스폰, 경로 및 초크 포인트; 지형은 그 주변을 자연스럽게 채움. 검증기(Validator)가 크기 등급별 이동 가능 여부, 스폰 공정성, 위험도 변화, 탈출 지점 접근성을 체크함.
맵(Maps): 생성 및 검증된 풀(pool)을 시즌별로 로테이션하여 타르코프처럼 맵 숙련도가 곧 실력이 되게 함; 레이드마다 새로운 시드를 쓰는 것도 기술적으로 가능. 클라이언트와 서버가 시드와 사양에 맞춰 맵을 생성하므로 맵 다운로드는 없음. 채집된 노드나 파괴된 문 같은 변경 사항은 네트워크 이벤트로 처리.
픽셀 폰트, 오디오 및 아이콘: 전부 생성형. 효과음은 합성, 음악은 생성형이거나 코드로 작곡하며, 플랫폼 레이어 뒤의 WebAudio를 통해 출력.
6.7 지형: 우리만의 미니멀리스트 생성기
목표: 수천 개의 유닛이 돋보일 수 있는, 차분하고 즉각적으로 읽히는 바닥. 미니멀리즘은 예산 절감이 아니라 품질을 위한 목표임: 요소는 적게, 하나하나 완벽하게 구현.
유닛 스타일 계승: 동일한 페인팅 로직 사용. 항상 유닛보다 대비와 채도를 낮게 유지하고, 팀 색상은 절대 쓰지 않음.
노이즈 대신 넓고 차분한 영역: 바이옴당 지형 재질을 최소화(풀, 흙/길, 모래, 바위, 늪). 세부 묘사는 월드 고정 그리드 위에 드문드문 배치된 모티프(풀 덤불, 자갈, 꽃)로 처리하며, 설계된 변형 중에서 셀별 해시값으로 선택. 노이즈로 픽셀 하나하나에 색을 입히지 않음.
게임플레이 요소로서의 높이: 유닛 스타일의 절벽 면과 명확한 경사로가 있는 3~5단계의 높이 구분. 이를 통해 읽기 쉬운 초크 포인트 형성. 절벽은 이동을 차단하며, GDD에 정의된 높이별 시야 규칙을 따름.
물: 호수, 강, 여울. 얕은 물은 이동 속도 저하, 깊은 물은 통과 불가. 애니메이션은 팔레트 사이클링으로만 구현.
전환: 최소 런 길이를 가진 깔끔한 픽셀 경계, 두 재질 사이에는 최대 한 줄의 디더링 밴드만 허용.
바이옴: 독자적인 알고리즘이 아니라 팔레트와 식생 세트로 구성.
청크 단위 렌더링: 압축된 머티리얼 맵과 월드 좌표에서 결정론적으로 픽셀 세부 정보를 도출하는 셰이더 사용. 큰 베이크 텍스처는 없고, 유닛과 동일한 팔레트 인덱스 및 LUT 원리를 따르며 청크당 드로우 콜을 최소화.
시맨틱 출력: 크기 등급별 보행 가능 여부, 이동 비용, 높이 레벨, 시야 차단, 수심, 사이트 적합성.
청크 경계에서 결정론적이고 매끄러운 연결: 골든 테스트를 거치며, "지형 + 식생"으로 단독 배포 가능.
6.8 백엔드, 메타, 지속성
메타 서비스: 계정(이메일 복구 기능이 있는 패스키 등 방식 선택 및 정당화; 로그인 인터페이스는 나중에 Steam 같은 제공업체가 연동될 수 있게 함), 로스터, 저장소, 기지, 제작, 상점, 봉봉(bonbons), 스타터 유닛, 매치메이킹, 레이드 오케스트레이션(레이드 호스트 할당), 시즌.
수동적 자원(Passive resources): 요청 시 타임스탬프를 기반으로 계산하며, 상시 돌아가는 프로세스는 없음.
가치 거래(탈출, 사망, 구매): 서버 사이드에서 처리하며 원자적(atomic)이고 멱등성(idempotent)을 보장하여 아이템 복사 방지.
마이그레이션: 버전 관리되며, 시작 시 멱등하게 실행되고 테스트를 거침.
GDPR: 데이터 최소화, 삭제 개념 도입, 자체 호스팅되는 개인정보 친화적 텔레메트리.
6.9 Docker, 호스팅 및 운영
이 스택은 이미 검증된 자매 프로젝트를 그대로 따랐고, 자세한 내용은 여기 다 적어놨음.
포트 (일부러 표준 안 썼고, .env에서 수정 가능): 5886/tcp client (정적 웹 클라이언트, /workshop/<name>/ 하위 워크숍, nginx), 5887/tcp meta (/api/*, /health), 5888/tcp raid (WebSocket 폴백 /ws, /health), 5888/udp raid (WebTransport, QUIC 기반 HTTP/3). Postgres랑 Redis는 내부 네트워크에서만 돌리고, 디버그용 포트는 주석 처리해 놨으니 필요하면 127.0.0.1로만 바인딩해서 쓰셈.
서비스는 docker-compose.yml에 있고, 최상단에 name: 설정, 컨테이너 이름은 <codename>_<service>, 브리지 네트워크랑 Postgres 볼륨 이름까지 다 지정해 둠:
모든 서비스에 공유 x-logging 앵커 적용 (json-file, 10MB, 파일 3개).
모든 서비스에 리소스 제한(cpus, memory, pids) 걸어둠. Docker VM을 공유해서 쓰니까, 서비스 하나 맛탱이 가도 다른 거까지 다 같이 죽으면 안 되잖아. 일단 측정하면서 조정할 초기값: db 1 CPU / 1GB, redis 0.5 / 512MB, meta 2 / 1GB, raid 6 / 4GB, client 0.5 / 128MB.
Rust 서비스는 init: true, restart: unless-stopped, raid는 stop_grace_period 30초 이상으로 설정.
depends_on은 service_started만 쓰고, service_healthy는 절대 금지: 이 호스트에서는 헬스 체크 기반으로 시작 순서 맞추면 가끔씩 꼬여서 멈춤. 서비스들이 알아서 DB랑 Redis 기다리게 하고, 지수 백오프(exponential backoff) 제한 걸어둠.
Dockerfile에 헬스 체크 넣고, 루트 권한 없는 최소한의 런타임 이미지 사용. 셸 없는 Rust 바이너리에는 healthcheck 서브커맨드 구현함.
.env는 git에서 제외하고, 주석 다 달린 .env.example 제공 (시크릿, CORS origin은 절대 * 금지, 로그 레벨, 제한, 포트, 퍼블릭 URL 등). 프로덕션 환경에서는 기본 시크릿 그대로면 서버 안 켜지게 막아둠.
빌드: Dockerfile은 docker/ 밑에 둠 (BuildKit 사용, repo 루트를 컨텍스트로). Rust는 핀 고정된 툴체인으로 멀티 스테이지 빌드하고, 의존성 레이어 캐싱, LTO랑 codegen-units = 1 적용해서 런타임 이미지에는 바이너리만 딱 들어감. 클라이언트는 Rust → wasm32-unknown-unknown → wasm-bindgen (버전 고정) → wasm-opt 순으로 가고, 셸은 pnpm --frozen-lockfile 사용. nginx는 올바른 application/wasm 타입으로 압축된 파일 서빙하고, 핑거프린트 찍힌 에셋은 immutable, index.html은 no-store로 설정해서 배포할 때마다 항상 최신 엔트리 문서가 나가게 함.
**scripts/stack.ps1**이 로컬에서 스택 띄우는 유일한 공식 방법임. 이 호스트에서는 docker compose up이 가끔 멈추는데, create랑 docker start 하나씩 하는 건 잘 돌아가거든. up [-Build]는 먼저 포트 확인하고 (다른 프로세스가 쓰고 있으면 바로 컷), 이미지 하나씩 빌드하고, docker compose create 돌린 다음, 의존성 순서대로 컨테이너 띄우고 타임아웃 걸어서 healthy 상태 될 때까지 기다림. down, restart, status도 다 됨.
WebTransport 및 TLS: 로컬에서는 raid 호스트가 단기용 자체 서명 ECDSA 인증서 만들고, meta 서비스가 그 해시값을 클라이언트에 넘겨줌 (serverCertificateHashes). 지원 안 되는 환경은 WebSocket으로 폴백함. 프로덕션에서는 raid 호스트가 직접 QUIC/TLS 종료함 (ACME). 리버스 프록시가 WebTransport를 그냥 넘겨줄 수 있는지 확인하고 쓸 것.
Docker 없는 로컬 개발:meta랑 raid는 cargo run으로 돌리고 (DATABASE_URL 없이 인메모리 저장소 사용), 클라이언트는 핫 리로드 되는 개발 서버 띄우면 됨.
프로덕션: Compose 파일 그대로 쓰고 docker-compose.prod.yml 추가. 호스트에는 TLS 프록시로 Caddy 띄우고, /ws용으로 CDN 안 타는 DNS 전용 실시간 호스트 사용. WebTransport는 raid 호스트로 바로 꽂음. raid 호스트는 전용 코어 쓰는 루트 서버고 meta 서비스에 등록됨. IaC(Infrastructure as Code) 적용했고, 배포/롤백 스크립트, 복구 테스트 마친 백업, observability 프로필, 알람이랑 런북까지 다 갖춰둠.
7. Repo 및 모듈 구조
엄격한 레이어로 나뉜 모노레포임. foundation ← generators ← engine ← game 순서고, 플랫폼 관련 코드는 맨 끝단에만 둠.
제너레이터는 게임 코드를 전혀 모름. 오직 데이터와 인터페이스만 주고받음 (예: 사이트가 태그된 슬롯을 제공하면, 게임이 그 안에 누가 들어갈지 결정함).
엔진과 게임 코드는 플랫폼을 모름. 브라우저 관련 코드는 전부 crates/platform/web에 박아둠. 나중에 native나 steam 플랫폼을 추가해도 구조를 뜯어고칠 필요가 없음.
스타일은 코드값이 아니라 입력값임 (foundation에서 가져온 프리셋).
모든 제너레이터는 하나의 출력 형식을 공유함. 그래서 렌더러, 아틀라스 패커, 익스포터가 각각 하나씩만 있으면 됨.
모든 제너레이터 모듈은 독립적으로 배포 가능함. 빌드, 테스트, README, CHANGELOG, semver, 라이선스, 워크숍까지 다 따로 돎. 제너레이터는 따로 라이선스를 걸 수 있고, 게임 코드는 독점 소유임. 라이선스 제안은 docs/NEEDS_USER.md에 적어두셈.
8. 확장성 및 성능 목표
이건 기준점이지, 한계치가 아님. 측정 결과가 좋으면 더 높게 잡으면 됨.
Area
Target
Raid
64 players, 50,000 simulated units in total
Server tick
p99 < 50 ms at 20 Hz with all features on 8 dedicated cores (proven locally in the `raid` container with its CPU limit)
Client
60 fps with 5,000 visible animated units on an Intel Iris Xe class iGPU
Renderer stress test
100k instances
Terrain
whole map semantics on the server < 2 s; visible area in the client < 300 ms; p99 per chunk ≤ 25 ms in WASM
Bandwidth per client
typically < 30 KB/s, large-battle peak < 100 KB/s
Loading
< 5 MB to the lobby including WASM, lobby in < 3 s on broadband
Stack
cold start via `stack.ps1 up` without build ≤ 30 s to all `healthy`
Stability
2-h bot raids without memory growth, < 60 s lost after a crash
윈도우 브라우저 설정에서 "절전 모드"로 돌리는 CPU 내장 그래픽을 하한선 기준으로 잡음.
9. 아트, UI, 오디오
게임 전체를 관통하는 아트 바이블 하나로 통일. 유닛이 스타일을 정하면 구조물, 지형, 식물, 아이콘, UI도 똑같은 페인팅 로직을 따라감. 원칙: 배경보다 피사체 우선 (유닛은 항상 눈에 띄어야 함), 노이즈 없이 깔끔하게 그릴 것 (모든 픽셀은 의도된 형태의 일부여야 함), 숨 쉬는 팔레트 (빛, 물, 불은 블러 처리가 아니라 램프를 통해 애니메이션 효과를 줌). 투영법, 픽셀 밀도, 램프, 외곽선 규칙, 대비 예산, 팀별 고유 색상, 상태 신호(채굴 가능, 위험, 안전 구역, 추출 등), 모든 줌 레벨에서의 가독성을 전부 다룸.
팀 색상은 색맹도 구분 가능하게 만들고, 모양으로도 차이를 둠.
Rust 렌더러 기반 UI: 팔레트 토큰, 커스텀 픽셀 폰트, 코드로 짠 프레임, 정수 스케일링, 필요할 땐 최소한으로, 그리고 게임 세계관에 녹아들게(diegetic) 배치, 키 바인딩 변경 가능, 독일어와 영어 지원.
입력: 마우스와 키보드 지원. 입력 레이어는 나중에 구조 변경 없이 게임패드도 받을 수 있게 설계함.
온보딩: 핵심 루프를 몇 분 만에 익힐 수 있는 튜토리얼 레이드 제공.
오디오: 절차적 생성 방식 사용. 대규모 전투 시 믹싱 우선순위를 둬서 소리가 뭉개지지 않게 함.
10. 품질 보증 (QA)
4절에서 언급한 관문들 외에 추가 사항:
테스트: 유닛 테스트, 속성 테스트, 골든 테스트(결정론적 결과 확인 및 C# 원본과의 패리티 확인, 네이티브 및 WASM 환경 모두), Docker 스택 대상 통합 테스트, 부하 및 게임플레이 테스트용 헤드리스 봇 클라이언트, 밸런싱을 위한 경제 시뮬레이션, 커브 기반 리플레이, 지연 시간 에뮬레이터 및 소크 테스트, 크래시 테스트(예: 레이드 도중 docker kill로 컨테이너 강제 종료), 보안 테스트(권한 부여, 입력 검증, 속도 제한, 아이템 복사 시도 등).
브라우저 테스트: Playwright를 사용해 Chromium, Firefox, WebKit 환경에서 Docker 스택을 대상으로 WebTransport 및 WebSocket 테스트 수행.
벤치마크: CI에 예산(기준치)을 설정하고, 성능 저하 발생 시 빌드 실패 처리.
시각적 검토: 메인 세션이 아닌 서브 에이전트에서만 수행. 서브 에이전트당 최대 10개 정도의 잘라내거나 축소된 이미지만 사용하고, 한 번에 최대 2~3개 에이전트만 가동함. 사용자의 업로드 대역폭이 제한적이고 공유되므로 항상 구체적인 질문과 함께 진행할 것.
코드 리뷰:main 브랜치로 병합하기 전 별도의 적대적 리뷰(정확성, 보안, 성능, 유지보수성 확인) 필수. 발견된 문제는 병합 전에 반드시 수정할 것.
11. 작업 방식 및 자율성
시작: 참조 저장소를 읽은 다음 docs/VISION.md, GDD.md, ARCHITECTURE.md, ART_BIBLE.md, QUALITY.md(게이트, 스코어카드, 예산) 및 ROADMAP.md를 작성해.
진행 상황:docs/PROGRESS.md(상태, 다음 단계, 잠재적 위험)와 docs/adr/(결정 사항)은 항상 최신 상태로 유지해서, 컨텍스트 요약 후에도 작업이 매끄럽게 이어지도록 해. 작업 단계마다 시작할 때 PROGRESS.md를 읽어.
결정 사항: 다음 순서를 따라. 황금률 > 정확성 및 보안 > 품질 > 성능 > 노력. 노력은 더 나쁜 해결책을 선택할 이유가 안 돼.
서브 에이전트 및 멀티 에이전트 워크플로우: 병렬 구축과 독립적이고 적대적인 검토를 위해 적극적으로 활용해.
Docker 원칙: 스택은 오직 scripts/stack.ps1로만 다루고, 절대 docker compose up을 쓰지 마. 외부 컨테이너나 스택은 절대 건드리지 말고, 스택은 항상 정의된 상태로 남겨둬.
Git: 기능 브랜치에서 작고 깔끔하게 커밋하고, 체크 통과하고 검토 끝나면 main에 병합해. 명시적인 승인 없이는 푸시, 원격 저장소 전송, 배포 금지.
정직: 측정된 결과만 보고하고, 테스트 안 된 건 절대 끝났다고 하지 마. 실패는 출력 결과와 함께 문서화해. 포장된 결과는 대놓고 드러난 문제보다 훨씬 나빠.
중간에 멈추지 마: 계속할지 물어보지 마. Definition of Done(완료 정의)을 충족하거나 14번 항목만 남았을 때 멈춰.
12. 로드맵: 게이트가 포함된 마일스톤
모든 게이트에는 4절의 품질 게이트가 포함됨.
M
Content
Gate
M0
Repo, CI with all checks, toolchains, docs, full Docker stack
CI green, violations detected, all `healthy`
M1
Spikes: 100k-instance renderer (WebGPU, WebGL2, iGPU, WebKit), WASM size, 100k-unit sim with flowfields and vision, netcode with 50 bots in a 5,000-unit battle, generator parity
Section 8 met, or an ADR with a better alternative
M2
Foundation, units, items, names, structures in Rust, workshops
생성기 모듈: (유닛, 구조물, 지형 + 식생, 맵 레이아웃) 각각 독립적으로 배포 가능해야 함.
정직한 최종 보고서: 무엇을 완료했고, 무엇을 측정했으며, 어떤 약점이 남아있고, 무엇을 추천하는지 작성.
14. 자율성의 한계
다음 사항은 직접 하지 마. 완벽하게 준비해서 docs/NEEDS_USER.md에 나열하고, 나머지는 계속 작업해:
돈 쓰기, 계약 체결, 타사 계정 생성;
자격 증명 및 보안 키 다루기, 공개 서버에 배포, 원격 저장소에 푸시, 무언가 배포하기;
실제 현금화(명시적 지시 없이는 범위 밖);
네이티브 빌드 및 Steam(이번 과업 범위 아님);
참조 저장소 및 외부 Docker 스택 변경;
게임 이름 및 브랜드: 중립적인 코드네임인 Exfil을 사용해 작업할 것.
15. 참조 저장소 (사용자가 첨부함)
유닛 및 구조물 생성기: <path to the reference repo>/procedural-pixel-units
주요 댓글
r/codex
개발자들은 AI를 활용한 게임 개발의 기술적 구현(테스트, 밸런싱)에 큰 관심을 보였으며, AI 시대의 새로운 게임 경제와 수익화 가능성에 대해 긍정적으로 논의했습니다.
1
정말 멋진 공유네요, 아주 좋아 보여요!
혹시 어떻게 하시는지 여쭤봐도 될까요:
게임 테스트는 어떻게 하시나요? 유닛/E2E 테스트 외에 몬테카를로 루프나 24시간 내내 문제를 찾아내는 무언가를 돌리시나요?
밸런스는 어떻게 잡으시나요? 제 말은, 어떤 게임이든 최소한의 밸런스는 있어야 하잖아요. 인간인 본인이 계속 '이거 OP니까 너프해'라고 말하지 않고, AI로 '대략적인' 밸런스라도 자동화할 방법을 찾으셨나요?
2
감사합니다!
유닛 테스트와 E2E 테스트 외에도 꽤 강도 높은 테스트 스위트를 사용하고 있습니다:
결정론적 골든 테스트. 시뮬레이션과 절차적 월드 생성기는 완전히 결정론적이어서, 모든 맵 수정 사항은 네이티브 및 WASM 콘텐츠 해시로 고정됩니다. 실수로 이전 맵이나 리플레이를 변경하는 모든 변경 사항은 즉시 실패 처리됩니다.
속성 테스트 및 퍼징. 속성 테스트는 규칙과 네트워크 프로토콜을 다룹니다. 퍼저는 디코더와 서버의 명령에 적대적인 입력을 던집니다.
변이 테스트. 규칙, 넷코드, 메타 서버와 같은 핵심 크레이트에서 90% 이상의 킬 타겟으로 실행됩니다.
봇 시뮬레이션. 봇 파티가 베테랑, 신규 유저, 다양한 파티 규모 등 AI 진영을 상대로 실제 레이드를 진행합니다. 경제 시뮬레이션은 전체 진행 시즌을 실행합니다.
말 그대로 24시간 내내 돌아가는 건 아닙니다. 무거운 작업들(변이, 완전 패리티, 퍼징)은 야간 게이트로 실행되고, 나머지는 머지할 때마다 실행됩니다. 봇과 경제 시뮬레이션은 밸런스 질문이 생길 때마다 필요에 따라 실행하고요. 현재 시뮬레이션이 실제 플레이어에게서 측정하는 데이터와는 차이가 좀 있어서 계속 조정 중입니다.
밸런스에 관해서는, 네, 대략적인 기준선을 잡을 수 있었습니다. 완벽과는 거리가 멀지만, 실제 플레이어들과 함께 반복하면서 무엇이 재미있고 무엇이 작동하는지 확인하기에는 충분합니다. 예를 들어 신규 유저 대 베테랑의 외곽/중앙/내부 구역 사망률이나, 크라운 획득 속도 같은 목표를 정의하고 테스트와 봇 시뮬레이션이 이를 측정하게 합니다. 그런 다음 실제 플레이테스트 데이터(게임이 파티가 어디서 언제 유닛을 잃는지 같은 레이드별 사실을 기록함)와 플레이어 피드백을 결합해 시뮬레이션과 목표치를 모두 개선합니다.
1
거짓말 안 하고 진짜 멋지네요. 저도 제 게임을 개발 중인 개발자인데, 이거라면 1~2달러 정도는 낼 의향이 있어요. AI 생성 게임 시대가 오면 동료 AI 게임 개발자들도 서로의 게임을 사서 플레이하게 될지 궁금해지네요. AAA급 규모는 아니겠지만, 재미있다면 왜 안 되겠어요? 새로운 마이크로 경제가 될 수도 있겠죠. 혹시 이 게임이나 다른 아이디어로 수익화할 계획이 있으신가요?
1
좋게 봐주셔서 감사합니다. 저에게 게임 개발은 취미라서 수익화는 거의 나중 문제예요. 하지만 스팀이나 앱 출시가 가능하도록 게임을 만들긴 했습니다. 그래서 일회성 구매 가격을 책정하는 건 옵션이 될 수 있겠네요. 'Out of Pixkrieg'에서 꾸미기 아이템이 얼마나 잘 먹힐지는 잘 모르겠지만요. 본인 게임은 어떻게 수익화하고 계신가요?