Painting Star 개발기 1 — 프로토타입에서 Phaser 앱으로
아이디어를 검증할 때 가장 빠른 방법은 단일 HTML 파일이다. 프레임워크도 없고, 빌드 툴도 없고, <script> 하나에 전부 때려 넣는다. Painting Star도 그렇게 시작했다.
게임 컨셉
모든 칸을 한 붓으로 방문하되, 색 구슬을 지나면 붓 색이 누적되고 별 칸에 정확한 혼합색으로 도달해야 클리어하는 퍼즐 게임이다. 색은 물감처럼 더하기만 되고 빼지지 않는다. 경로의 순서가 곧 퍼즐의 해답이다.
단순한 규칙인데 생각보다 퍼즐 깊이가 나왔다. colorpath.html 하나짜리 파일에서 게임의 핵심 루프를 확인했다.
마이그레이션 결정
프로토타입이 동작하자마자 구조적인 문제가 보였다. 1,100줄짜리 IIFE 안에 색 시스템, 레벨 생성, 렌더링, 입력 처리가 전부 섞여 있었다. 모바일 앱으로 출시하려면 어차피 손봐야 하는 부분이었다.
선택지는 두 가지였다.
- 바닐라 JS 구조 유지, Capacitor만 씌우기
- Phaser 3 + TypeScript로 재작성
첫 번째가 공수는 적다. 하지만 Canvas 2D를 직접 다루는 코드가 이미 복잡해진 상태에서 더 키우는 건 나중에 더 힘들어진다는 걸 경험으로 알고 있었다. Phaser를 선택했다.
구조 설계
마이그레이션 전에 기능 단위로 파일을 나눴다.
| 파일 | 역할 |
|---|---|
ColorSystem.ts |
색 벡터 모델, 혼합 로직 |
PuzzleGenerator.ts |
해밀턴 경로 생성, 이벤트 배치 |
DifficultyManager.ts |
스테이지별 파라미터 |
GameScene.ts |
게임 루프, 렌더링, 입력 처리 |
AdManager.ts |
광고 초기화, 레이아웃 조정 |
Phaser를 쓰면서도 실제 그리기는 Canvas 2D로 직접 한다. Phaser의 display list에 CanvasDrawer 오브젝트를 올려두고, render 단계에서 Canvas context를 받아 직접 드로잉하는 방식이다. Phaser 내장 게임 오브젝트 대신 Canvas API를 그대로 쓰는 이유는 프로토타입에서 작성한 드로잉 코드를 최대한 재활용하기 위해서다.
절차적 레벨 생성
레벨을 하드코딩하지 않고 전부 생성한다. 핵심은 두 가지다.
해밀턴 경로 생성: Warnsdorff 휴리스틱으로 모든 칸을 한 번씩 방문하는 경로를 찾는다. 실패하면 DFS 백트래킹으로 폴백한다.
부분집합 체인: 다중 별 스테이지에서 각 별의 목표 색이 체인을 이뤄야 한다. {r} ⊂ {r,y} ⊂ {r,y,b} 같은 구조. 색은 누적만 되기 때문에 순서가 잘못된 별 조합은 수학적으로 클리어 불가다. 생성 단계에서 이를 강제한다.
스테이지가 올라갈수록 그리드가 커지고, 별 개수가 늘고, 장애물이 추가된다. 장애물은 체커보드 패리티와 연결성 제약을 통과해야만 배치된다. 그렇지 않으면 해밀턴 경로 자체가 불가능한 그리드가 나온다.
결과
colorpath.html은 레거시로 남겨두고 건드리지 않는다. Phaser 앱은 app/ 폴더에 분리되어 있다. 마이그레이션 후 기능은 동일하지만 코드가 다룰 수 있는 단위로 쪼개졌다. 다음 단계는 이걸 실제 iOS 앱으로 패키징하는 것이었다.