점심을 먹고 나면 누가 커피를 살지 정해야 한다. 보통은 사다리 앱을 켠다. 그런데 사다리는 한 명만 걸린다. 여섯 명이 마신 커피값을 한 사람이 다 내면 그날 분위기가 조금 묘해진다.

그래서 직접 만들기로 했다. 이름은 「오늘 누가 쏘냐?」(WhoPays)다. 처음 커밋한 날이 9월 16일이고, 열다섯 번째 게임을 넣은 날이 9월 30일이다. 보름 동안 사다리 하나에서 시작해 게임 15개, 서버 없이 여러 폰으로 같이 하는 기능, Play 스토어 비공개 테스트까지 왔다.

이 연재에서는 그 보름 동안 한 일과 부딪힌 문제를 하나씩 정리한다. 첫 글에서는 전체 흐름을 먼저 본다.

오늘 누가 쏘냐? 타이틀, 참가자 설정, 사다리, 결과 화면

어떤 게임인가

폰 하나를 가운데 두고 2~8명이 모여서 한 판 한다. 게임이 끝나면 등수가 나오고, 커피값 총액을 넣어 두었다면 등수에 따라 각자 낼 금액까지 계산해 준다. 결과는 카카오톡에 붙여 넣을 수 있게 글로 복사하거나 이미지로 공유한다.

게임은 지금 15개다.

분류 게임
운 사다리 타기, 룰렛, 제비뽑기
순발력 눈치 게임, 폭탄 돌리기, 연타 대결, 딱 멈춰!, 끝까지 버텨!
말놀이·숫자 369, 바니바니, 31 게임, 업다운, 제일 작은 숫자
머리 초성 퀴즈, 순서 외우기

모든 게임은 폰 하나로도, 각자 자기 폰으로도 할 수 있다. 각자 폰으로 하는 「같이 하기」는 외부 서버 없이 같은 Wi-Fi 안에서 방장 폰이 서버 역할을 한다.

만든 방식

Unity 6(6000.5)와 2D URP로 만들었다. 몇 가지 원칙을 처음부터 정해 두고 끝까지 지켰다.

  • UI는 전부 코드로 만든다. 씬에는 카메라 하나만 있고, 프리팹도 없다. 실행하면 Bootstrap이 캔버스와 화면을 코드로 만든다.
  • 그림과 소리도 코드로 만든다. 32x32 픽셀 캐릭터 8종, 효과음, 배경음악, 앱 아이콘, 한글 폰트 서브셋까지 전부 Python 스크립트가 생성한다.
  • 외부 패키지와 서버는 쓰지 않는다. 저장은 PlayerPrefs, 통신은 .NET 소켓으로 직접 한다.
  • 작업 전에 레드마인 이슈를 등록하고, 커밋에 fixed #번호를 넣는다. 보름 동안 이슈가 #700부터 #808까지 쌓였고, 이 글도 그 기록을 보며 쓰고 있다.

개발에는 AI 코딩 도구(Claude Code)를 적극적으로 활용했다. 구조와 규칙, 작업 단위는 직접 설계하고, 반복되는 구현은 도구로 빠르게 처리했다. 빌드는 단계마다 실제 폰 여러 대에 설치해 검증했다. 이 속도를 내기 위해 프로젝트 구조를 어떻게 잡았는지는 연재 중간중간 다룬다.

보름의 타임라인

날짜 한 일
9/16 첫 커밋. 사다리 타기·눈치 게임, 픽셀 캐릭터, 효과음
9/17 WebGL 빌드를 IIS에 올려 브라우저로 테스트. 한글이 안 보이고, 폰 LTE에서는 아예 안 열림
9/21 가장 긴 하루. 등수별 커피값 분담, 같이 하기(LAN) 1~4단계, Android 빌드와 Play 스토어 등록, 폭탄 돌리기·룰렛·초성 퀴즈, 결과 이미지 공유, 배경음악·진동, 폴더블 화면 대응. 버전 1.0 → 1.2
9/22~23 369, 바니바니. 게임 선택 화면을 2열 그리드로. 1.3
9/24 순서 외우기, 딱 멈춰!, 제일 작은 숫자, 제비뽑기. 1.4
9/26 31 게임, 연타 대결, 업다운. 게임 선택 화면을 3열로. 1.5
9/30 끝까지 버텨!로 열다섯 번째 게임. 3열 그리드가 꽉 찼다. 1.6

C# 코드는 약 1만 6천 줄, 커밋은 57개다. 게임 하나가 대략 500~1,100줄인데, 폰 하나 모드·방장 모드·게스트 모드를 한 클래스에 담다 보니 길어졌다.

기능은 대부분 "불편하다"는 한마디에서 나왔다

돌아보면 계획표대로 만든 기능보다, 직접 해 보다가 불편해서 만든 기능이 더 많다.

한 명만 걸리는 게 아쉬웠다. 여섯 명이 사다리를 탔더니 한 명만 「당첨」이고 나머지는 전부 「세이프」였다. 그래서 이름 옆에 등수를 보여 주고, 한 명이 다 내는 대신 순위별로 금액을 나누도록 바꿨다. 아래쪽 최대 4명이 40:30:20:10처럼 등수가 낮을수록 많이 내고 나머지는 면제되는 분담 공식이 여기서 나왔다.

왼쪽은 처음 버전(한 명만 당첨), 오른쪽은 지금 버전(총 30,000원을 등수대로 12,000·9,000·6,000·3,000원 분담)

폰 하나에 모이기 어려웠다. 눈치 게임은 한 화면을 사람 수만큼 칸으로 나눠 동시에 누른다. 여섯 명이 폰 하나에 손가락을 올리면 좁다. 사람이 많으면 기기 하나로는 한계가 있다고 보고 여러 폰 대전을 설계했다. 블루투스도 후보였지만 배포를 Android로만 하기로 정하면서 같은 Wi-Fi 안의 LAN 방식으로 갔다.

눈치 게임 설명 화면. 한 화면을 사람 수만큼 칸으로 나눈다

IP 주소를 입력하라는 건 무리였다. 첫 로비 화면에는 방장 폰의 주소가 그대로 떴다. 게다가 Docker·WSL 같은 가상 어댑터 주소까지 같이 나왔다. 회식 자리에서 192.168.x.x를 받아 적게 할 수는 없어서, 주소 마지막 숫자만 「방 번호」로 보여 주고 자동으로 방을 찾는 방식으로 바꿨다.

폴더블 폰에서 화면 아래가 잘렸다. 갤럭시 Z 폴드를 펼친 비율에서는 게임 선택 화면 아래쪽 카드가 버튼에 가려 보이지 않았다. 화면 비율에 따라 캔버스를 너비 기준 또는 높이 기준으로 맞추는 CanvasFitter를 넣어 해결했다.

폴드 화면에서 게임 선택 카드가 아래 버튼에 가려진 모습

부딪힌 문제들

연재에서 자세히 다룰 문제를 미리 정리하면 다음과 같다.

  • 브라우저에서 돌렸더니 한글이 전부 빈칸이었다. Unity 내장 폰트에는 한글 글리프가 없고, WebGL에서는 OS 폰트로 대체되지도 않는다.
  • 폰 LTE에서는 both async and sync fetching of the wasm failed 오류와 함께 아예 열리지 않았다. 40MB짜리 무압축 wasm을 받다가 8MB쯤에서 연결이 끊겼다.
  • PC에서 만든 방에는 들어가지는데, 폰에서 PC 방으로 들어가면 연결 시간 초과가 났다. Windows 방화벽이 「개인」 네트워크에서도 Unity 에디터의 인바운드 연결을 막고 있었다.
  • 빌드 스크립트에 -version 인자를 넘겼더니 빌드가 아예 실행되지 않았다. Unity 자체 명령줄 옵션과 이름이 겹쳤다.
  • Play Console에 처음 올린 번들을 다른 트랙에 다시 올리려 하자 **「2 버전 코드는 이미 사용되었습니다」**가 떴다.
  • 업다운을 「맞히면 탈락」으로 만들었더니 다들 범위 끝 숫자만 불러서 판이 끝나지 않았다. 「맞히면 탈출」로 뒤집었다.

연재 순서

편 주제
1 보름 동안 파티게임 15개를 만들었다 (이 글)
2 프리팹 없이 UI를 전부 코드로 — UIFactory, 화면 관리, 폴더블 대응
3 캐릭터·효과음·배경음악·폰트를 Python으로 생성하기
4 Unity WebGL을 IIS에 올리기 — 한글 빈칸과 40MB wasm (10월 5일 공개)
5 등수별 커피값 분담 공식 (10월 5일 공개)
6 서버 없이 여러 폰으로 — UDP 비콘, 서브넷 스캔, 방 번호 (10월 5일 공개)
7 방장이 판정한다 — 호스트 권위, 재접속, 늦게 도착한 입력 (10월 6일 공개)
8 비밀은 방장 폰만 안다 — 15개 게임의 규칙을 다듬은 이야기 (10월 6일 공개)
9 Play 스토어 비공개 테스트까지 — AAB, 서명 키, 버전 코드 (10월 6일 공개)

다음 글 2편에서는 씬 편집기를 한 번도 열지 않고 UI를 코드로만 만든 방법을 다룬다.