What this kind of game actually needs
Pick a genre and see the systems it really needs — and what breaks when each one is missing. Not general advice: every item here came from building the games on this site.
Checklists are easy to find. Most of them say what to do and stop there, without saying what happens if you skip it. The second part is what you actually need when deciding whether something can wait — and what shape it comes back in if it does.
So every item carries its failure. All of them happened, and a few of them only surfaced after shipping.
Match-3 / puzzle
- 시작 보드에 매치가 없고, 둘 수 있는 수가 최소 하나 있을 것
이걸 보장하지 않으면 시작하자마자 저절로 터지거나, 아무 수도 없는 판이 나온다. 판 생성 뒤 검사해서 다시 만드는 게 가장 싸다.
- 수가 없을 때의 자동 셔플 — 그리고 셔플로도 안 되면 지는 것
셔플을 무한히 돌리면 화면이 멈춘다. 색 구성을 유지한 채 섞되, 섞어도 수가 없으면 패배로 닫는다(fail-closed).
- 연쇄 중에 만들어진 특수 블록을 어떻게 셀지
무작위로 떨어진 블록이 2×2 한 색을 이룰 확률은 낮아도, 자리가 수십 곳이라 캐스케이드마다 얻어걸린다. 직접 민 경우만 인정하지 않으면 특수가 남발된다.
- 같은 대상에 두 번 적용되지 않게 막기
한 번의 매치가 같은 장애물을 두 번 때리면 규칙을 예측할 수 없다. 처리한 대상을 표시해 두는 한 줄이 필요하다.
- 매치 검출과 런(run) 검출이 어긋나지 않는지 검사
성능 때문에 두 벌로 짜 두면 한쪽만 고치기 쉽다. 무작위 수백 판을 두 경로로 돌려 대조하는 검사가 싸게 먹힌다.
- 시드 재현 — 전용 난수 발생기로
언어 표준 shuffle 은 대개 전역 난수를 쓴다. 화면 장식이 전역에서 뽑아 쓰면 같은 시드가 다른 판을 낸다. 자기 난수 발생기를 들고 다녀야 한다.
Runner / arcade
- 난이도 상승에 상한 두기
매번 몇 %씩 빨라지게 두면 스무 번쯤에서 사람이 반응할 수 없는 속도가 된다. 그때부터는 실력이 아니라 운이다.
- 판정 창의 하한
성공할 때마다 목표를 좁히는 구조라면 하한이 필요하다. 화면에서 안 보이는 폭까지 줄이면 어려운 게 아니라 안 보이는 것이다.
- 다음 목표가 코앞에 생기지 않게 최소 간격
새 목표가 판단할 시간 없이 지나가면 실력과 무관해진다.
- 한 판이 30초 안에 끝나고 즉시 재시작될 것
짧은 판은 플레이어에게도 좋지만 만드는 쪽의 검증 속도를 결정한다.
- 점수의 뜻을 하나로
거리·연쇄·처치를 다 더하면 무엇을 잘해야 점수가 오르는지 안 보인다. 축을 하나 정하고 나머지는 그 축에 곱한다.
Roguelite / RPG
- 절차 생성 맵에서 도달 가능성 검사
기둥 하나가 좁은 통로에 떨어지면 그 층은 못 깬다. 시드가 고정이면 그 사람에게는 영영 못 깬다. 시작점에서 모든 목표에 걸어서 닿는지 빌드에서 검사한다.
- 흔드는 연산은 길을 유지하거나 넓히는 것만
무작위로 막는 연산을 허용하면 위 검사가 매번 실패한다.
- 저장 파일을 외부 입력으로 취급
못 읽으면 기본값으로 덮어쓰지 말고 거절하고 이유를 보여 준다. 조용히 새 기록으로 갈아 끼우면 되살릴 수 있었던 진행도까지 사라진다.
- 체크포인트를 저장 파일에서 읽지 않기
저장된 값을 그대로 쓰면 파일을 고쳐 가 본 적 없는 층으로 복귀할 수 있다. 최고 도달 층에서 규칙으로 계산한다.
- 수치 적용 순서를 정하기 — 성장 다음에 환경 보정
뒤집으면 '쿨다운 25% 감소' 같은 비율 보정이 강화 단계에 따라 다른 값이 된다. 사람마다 다르게 작동하는데 알아채기 어렵다.
- 직업/빌드가 모든 항목에서 우위가 되지 않게
하나라도 그런 선택지가 생기면 나머지는 고를 이유가 없어진다.
FPS / online
- 지연 보상 — 쏜 시점의 화면으로 판정
없으면 '분명히 맞혔는데 안 맞는' 상태가 된다. 서버가 과거 상태를 되감아 판정해야 한다.
- 판단 축이 화면에서 보이는지 먼저 확인
부위를 조준하는 게임을 쿼터뷰로 만들면 15m 밖 로봇의 머리와 다리가 몇 픽셀 차이다. 조작이 어려운 게 아니라 읽히지 않는 것이다.
- 원격 플레이어의 순간이동 처리
패킷이 늦게 오면 상대가 뚝뚝 끊겨 보인다. 보간과 외삽의 경계를 정해 둬야 한다.
- 동시에 울리는 소리의 상한
연쇄가 터지면 수십 개가 한꺼번에 울려 오히려 무슨 일인지 안 들린다. 보이스 풀과 '가장 큰 사건 하나' 규칙이 필요하다.
- 익스포트 빌드에서만 나는 버그를 위한 검증
에디터에서 되는 것과 배포본에서 되는 것은 다르다. 배포본으로 한 번은 돌려야 한다.
Shipping to the web
- 파일당 용량 제한 — 자르고 이어 붙이기
정적 호스팅은 흔히 파일당 25MiB 제한이 있는데 엔진 wasm 은 40MB 다. 20MB 로 잘라 올리고 서버가 이어 붙인다.
- Content-Type 과 Content-Encoding
wasm 의 타입이 틀리면 스트리밍 컴파일을 포기하고 다 받은 뒤 컴파일한다. 조용히 느려질 뿐 에러는 안 난다.
- 캐시 수명은 파일 이름에 해시가 있을 때만 길게
이름이 고정인데 1년 캐시를 걸면 다음 배포가 사용자에게 도달하지 않는다. 자주 고칠 문서는 서버가 직접 응답하게 둔다.
- 스레드 지원 여부와 서버 헤더 짝 맞추기
스레드를 쓰려면 특정 헤더 두 개가 필요하고, 그러면 외부 자원 로딩이 까다로워진다. 내보내기 설정과 서버가 안 맞으면 로딩 중 멈춘다.
- 배포 확인은 눈이 아니라 해시로
'게임이 뜬다'는 새 빌드가 올라갔다는 증거가 아니다. 로컬 산출물과 라이브 응답의 해시를 비교한다.
Shipping to a store
- 폰트를 프로젝트에 심기
엔진 기본 폰트에 한글 글리프가 없어도 데스크톱에서는 OS 가 대신 그려 준다. 실기기에서만 전부 깨진다 — 데스크톱 스크린샷으로는 절대 못 잡는다.
- 오디오를 실기기에서 확인
검증을 음소거로 돌리면 오디오 스레드가 아예 생기지 않는다. 소리 코드가 한 번도 실행되지 않은 채 배포될 수 있다.
- 버전 번호의 단일 출처
화면에 그리는 값과 빌드 설정이 갈라지면, 화면에는 1.0.1 인데 스토어에는 1.0.0 이 올라간 상태가 되고 아무도 모른다.
- 빌드 번호는 영구 예약된다
한 번 올린 번호는 내렸어도 재사용할 수 없다. 죽은 번호로 두고 다음 번호로 간다.
- 뒤로가기로 즉시 종료하지 않기
되돌릴 수 없는 동작인데 스치듯 눌리는 버튼이다. 물어보고 닫되, 확인 창이 떠 있는 동안 뒤의 버튼이 살아 있으면 안 된다.
- 광고를 넣는다면 표시는 사용자 탭에서만
앱이 스스로 띄우지 않고, 실패해도 자동 재시도하지 않고, 보상은 닫힐 때 한 번만 준다.
Adding a server
- 점수는 클라이언트가 보고한다는 것을 전제로
완전한 위조 방지는 서버가 게임을 다시 돌리지 않는 한 불가능하다. 목표를 '말이 안 되는 값 거르기'로 낮춘다.
- 앞뒤가 안 맞는 값의 기준을 숫자로
최소 플레이 시간, 초당 점수 상한, 같은 사람의 연타 간격. 상한은 넉넉히 — 정상 플레이를 막는 것보다 낫다.
- 연타 판별에 IP 원문을 저장하지 않기
필요한 것은 '같은 사람인가'뿐이다. 소금 친 해시면 되고, 소금이 없으면 아예 받지 않는 쪽으로 닫는다.
- 집계로 저장하기
판 하나하나를 남기면 표가 플레이 수만큼 자라고 개인을 되짚을 수 있게 된다. 조합의 횟수만 세면 공개해도 된다.
- 스키마는 배포 때 한 번, 요청마다가 아니라
매 요청에서 테이블을 확인하면 비용도 들지만, 실패가 배포가 아니라 플레이 중에 드러난다.
Longer versions live in Guides and the dev log. Most items here have a full post behind them, in Korean.