이 게임을 만들면 뭐가 필요한가
장르를 고르면 실제로 필요한 시스템과, 그걸 빼먹으면 무슨 일이 나는지가 함께 나옵니다. 일반론이 아니라 이 사이트의 게임들을 만들며 부딪힌 것만 적었습니다.
체크리스트는 검색하면 얼마든지 나옵니다. 그런데 대부분 무엇을 해야 하는지만 적혀 있고, 안 하면 무슨 일이 나는지는 안 적혀 있습니다. 정작 판단이 필요한 순간에 필요한 건 그쪽입니다 — 지금 이걸 건너뛰어도 되는지, 나중에 어떤 모양으로 터지는지.
그래서 항목마다 '이걸 안 하면' 을 붙였습니다. 전부 실제로 겪은 것이고, 몇 개는 배포한 뒤에야 알았습니다.
매치-3 / 퍼즐
- 시작 보드에 매치가 없고, 둘 수 있는 수가 최소 하나 있을 것
이걸 보장하지 않으면 시작하자마자 저절로 터지거나, 아무 수도 없는 판이 나온다. 판 생성 뒤 검사해서 다시 만드는 게 가장 싸다.
- 수가 없을 때의 자동 셔플 — 그리고 셔플로도 안 되면 지는 것
셔플을 무한히 돌리면 화면이 멈춘다. 색 구성을 유지한 채 섞되, 섞어도 수가 없으면 패배로 닫는다(fail-closed).
- 연쇄 중에 만들어진 특수 블록을 어떻게 셀지
무작위로 떨어진 블록이 2×2 한 색을 이룰 확률은 낮아도, 자리가 수십 곳이라 캐스케이드마다 얻어걸린다. 직접 민 경우만 인정하지 않으면 특수가 남발된다.
- 같은 대상에 두 번 적용되지 않게 막기
한 번의 매치가 같은 장애물을 두 번 때리면 규칙을 예측할 수 없다. 처리한 대상을 표시해 두는 한 줄이 필요하다.
- 매치 검출과 런(run) 검출이 어긋나지 않는지 검사
성능 때문에 두 벌로 짜 두면 한쪽만 고치기 쉽다. 무작위 수백 판을 두 경로로 돌려 대조하는 검사가 싸게 먹힌다.
- 시드 재현 — 전용 난수 발생기로
언어 표준 shuffle 은 대개 전역 난수를 쓴다. 화면 장식이 전역에서 뽑아 쓰면 같은 시드가 다른 판을 낸다. 자기 난수 발생기를 들고 다녀야 한다.
러너 / 아케이드
- 난이도 상승에 상한 두기
매번 몇 %씩 빨라지게 두면 스무 번쯤에서 사람이 반응할 수 없는 속도가 된다. 그때부터는 실력이 아니라 운이다.
- 판정 창의 하한
성공할 때마다 목표를 좁히는 구조라면 하한이 필요하다. 화면에서 안 보이는 폭까지 줄이면 어려운 게 아니라 안 보이는 것이다.
- 다음 목표가 코앞에 생기지 않게 최소 간격
새 목표가 판단할 시간 없이 지나가면 실력과 무관해진다.
- 한 판이 30초 안에 끝나고 즉시 재시작될 것
짧은 판은 플레이어에게도 좋지만 만드는 쪽의 검증 속도를 결정한다.
- 점수의 뜻을 하나로
거리·연쇄·처치를 다 더하면 무엇을 잘해야 점수가 오르는지 안 보인다. 축을 하나 정하고 나머지는 그 축에 곱한다.
로그라이트 / RPG
- 절차 생성 맵에서 도달 가능성 검사
기둥 하나가 좁은 통로에 떨어지면 그 층은 못 깬다. 시드가 고정이면 그 사람에게는 영영 못 깬다. 시작점에서 모든 목표에 걸어서 닿는지 빌드에서 검사한다.
- 흔드는 연산은 길을 유지하거나 넓히는 것만
무작위로 막는 연산을 허용하면 위 검사가 매번 실패한다.
- 저장 파일을 외부 입력으로 취급
못 읽으면 기본값으로 덮어쓰지 말고 거절하고 이유를 보여 준다. 조용히 새 기록으로 갈아 끼우면 되살릴 수 있었던 진행도까지 사라진다.
- 체크포인트를 저장 파일에서 읽지 않기
저장된 값을 그대로 쓰면 파일을 고쳐 가 본 적 없는 층으로 복귀할 수 있다. 최고 도달 층에서 규칙으로 계산한다.
- 수치 적용 순서를 정하기 — 성장 다음에 환경 보정
뒤집으면 '쿨다운 25% 감소' 같은 비율 보정이 강화 단계에 따라 다른 값이 된다. 사람마다 다르게 작동하는데 알아채기 어렵다.
- 직업/빌드가 모든 항목에서 우위가 되지 않게
하나라도 그런 선택지가 생기면 나머지는 고를 이유가 없어진다.
FPS / 온라인 대전
- 지연 보상 — 쏜 시점의 화면으로 판정
없으면 '분명히 맞혔는데 안 맞는' 상태가 된다. 서버가 과거 상태를 되감아 판정해야 한다.
- 판단 축이 화면에서 보이는지 먼저 확인
부위를 조준하는 게임을 쿼터뷰로 만들면 15m 밖 로봇의 머리와 다리가 몇 픽셀 차이다. 조작이 어려운 게 아니라 읽히지 않는 것이다.
- 원격 플레이어의 순간이동 처리
패킷이 늦게 오면 상대가 뚝뚝 끊겨 보인다. 보간과 외삽의 경계를 정해 둬야 한다.
- 동시에 울리는 소리의 상한
연쇄가 터지면 수십 개가 한꺼번에 울려 오히려 무슨 일인지 안 들린다. 보이스 풀과 '가장 큰 사건 하나' 규칙이 필요하다.
- 익스포트 빌드에서만 나는 버그를 위한 검증
에디터에서 되는 것과 배포본에서 되는 것은 다르다. 배포본으로 한 번은 돌려야 한다.
웹으로 내보내기
- 파일당 용량 제한 — 자르고 이어 붙이기
정적 호스팅은 흔히 파일당 25MiB 제한이 있는데 엔진 wasm 은 40MB 다. 20MB 로 잘라 올리고 서버가 이어 붙인다.
- Content-Type 과 Content-Encoding
wasm 의 타입이 틀리면 스트리밍 컴파일을 포기하고 다 받은 뒤 컴파일한다. 조용히 느려질 뿐 에러는 안 난다.
- 캐시 수명은 파일 이름에 해시가 있을 때만 길게
이름이 고정인데 1년 캐시를 걸면 다음 배포가 사용자에게 도달하지 않는다. 자주 고칠 문서는 서버가 직접 응답하게 둔다.
- 스레드 지원 여부와 서버 헤더 짝 맞추기
스레드를 쓰려면 특정 헤더 두 개가 필요하고, 그러면 외부 자원 로딩이 까다로워진다. 내보내기 설정과 서버가 안 맞으면 로딩 중 멈춘다.
- 배포 확인은 눈이 아니라 해시로
'게임이 뜬다'는 새 빌드가 올라갔다는 증거가 아니다. 로컬 산출물과 라이브 응답의 해시를 비교한다.
스토어에 올리기
- 폰트를 프로젝트에 심기
엔진 기본 폰트에 한글 글리프가 없어도 데스크톱에서는 OS 가 대신 그려 준다. 실기기에서만 전부 깨진다 — 데스크톱 스크린샷으로는 절대 못 잡는다.
- 오디오를 실기기에서 확인
검증을 음소거로 돌리면 오디오 스레드가 아예 생기지 않는다. 소리 코드가 한 번도 실행되지 않은 채 배포될 수 있다.
- 버전 번호의 단일 출처
화면에 그리는 값과 빌드 설정이 갈라지면, 화면에는 1.0.1 인데 스토어에는 1.0.0 이 올라간 상태가 되고 아무도 모른다.
- 빌드 번호는 영구 예약된다
한 번 올린 번호는 내렸어도 재사용할 수 없다. 죽은 번호로 두고 다음 번호로 간다.
- 뒤로가기로 즉시 종료하지 않기
되돌릴 수 없는 동작인데 스치듯 눌리는 버튼이다. 물어보고 닫되, 확인 창이 떠 있는 동안 뒤의 버튼이 살아 있으면 안 된다.
- 광고를 넣는다면 표시는 사용자 탭에서만
앱이 스스로 띄우지 않고, 실패해도 자동 재시도하지 않고, 보상은 닫힐 때 한 번만 준다.
순위표 · 서버가 붙는 순간
- 점수는 클라이언트가 보고한다는 것을 전제로
완전한 위조 방지는 서버가 게임을 다시 돌리지 않는 한 불가능하다. 목표를 '말이 안 되는 값 거르기'로 낮춘다.
- 앞뒤가 안 맞는 값의 기준을 숫자로
최소 플레이 시간, 초당 점수 상한, 같은 사람의 연타 간격. 상한은 넉넉히 — 정상 플레이를 막는 것보다 낫다.
- 연타 판별에 IP 원문을 저장하지 않기
필요한 것은 '같은 사람인가'뿐이다. 소금 친 해시면 되고, 소금이 없으면 아예 받지 않는 쪽으로 닫는다.
- 집계로 저장하기
판 하나하나를 남기면 표가 플레이 수만큼 자라고 개인을 되짚을 수 있게 된다. 조합의 횟수만 세면 공개해도 된다.
- 스키마는 배포 때 한 번, 요청마다가 아니라
매 요청에서 테이블을 확인하면 비용도 들지만, 실패가 배포가 아니라 플레이 중에 드러난다.