본문 바로가기

앱 개발

[딱팡 개발일지] 기획서 한 장에서 두 스토어 출시까지

딱팡(DDAK PANG)을 만들면서 고민했던 것, 막혔던 것, 바꾼 것들을 정리해 둡니다. 게임 소개는 앞 글에 있어요. → (소개 글 링크)

  • 개발: 1인 (ECSOFT)
  • 플랫폼: Android · iOS
  • 기술: Kotlin Multiplatform(공용 규칙 엔진) + Android Jetpack Compose + iOS SwiftUI
  • 리소스: 이미지 ComfyUI(Z-Image Turbo), 음악·효과음 Stable Audio 3, 글꼴 주아

1. 아이디어 — "색 대신 목표를 맞추면 어떨까?"

숫자 버블슈터를 찾아보면 거의 다 2048식(같은 숫자끼리 합쳐서 커짐)이고, 동물 테마 버블슈터는 그림만 동물이지 결국 색 맞추기였어요.

그래서 규칙을 하나만 바꿨습니다. "무엇이 터지는가"를 바깥의 목표 카드가 정한다.

  • 숫자 합: 쏜 방울 + 닿은 방울의 합 = 목표 숫자면 팡
  • 동물 조립: 닿은 방울들 안에 목표 동물의 부위가 다 모이면 합체

게임 이름은 목표에 "딱" 맞는 순간과 "팡" 터지는 결과를 그대로 붙여 딱팡으로 정했습니다.

2. 가장 중요했던 규칙 한 줄

판정 범위 = 붙은 방울 + 그 칸에 바로 닿은 방울(최대 7개)

처음엔 "닿은 방울들 중 어떤 조합의 합"으로 할까도 생각했는데, 그러면 어떤 부분집합을 셀지 모호해지고 플레이어도 헷갈립니다. 범위를 이웃 한 겹으로 딱 고정하니 판정이 결정적이 되었고, 덕분에 조준하는 동안 결과를 미리 계산해서 보여 줄 수 있게 됐어요. 이 "쏘기 전에 결과가 보이는" 조준 미리보기가 딱팡 조작감의 절반입니다.

3. 구조 — 규칙은 한 번만 짠다

물리 엔진은 쓰지 않았어요. 발사 궤적은 직선 + 좌우 벽 반사, 착지는 가장 가까운 빈 칸에 스냅이면 충분합니다.

 
shared (KMP)  ─ 육각 그리드 · 발사/반사/스냅 · 판정 · 목표 생성
              ─ 스테이지 100개 · 부스터 · 난이도 · 랭킹 API · 기기 ID
Android       ─ Jetpack Compose Canvas 로 그리기 · 입력 · 연출 · 소리
iOS           ─ SwiftUI Canvas 로 같은 그리기 · 입력 · 연출 · 소리

규칙 엔진을 KMP 공용 모듈로 빼 두니 iOS 이식 때 판정·목표 생성·스테이지 데이터를 한 줄도 다시 짜지 않았고, 두 플랫폼 수치가 어긋날 일도 없었어요. 공용 모듈에는 테스트를 58개 붙여 두었습니다.

4. "아무 데도 맞출 수 없는 판" 막기

규칙을 정하고 가장 먼저 부딪힌 문제는 막힘이었어요. 목표를 무작위로 뽑으면 후반에 "지금 필드에서는 절대 못 만드는 숫자"가 자주 나옵니다.

그래서 새 목표를 뽑을 때 붙일 수 있는 빈 칸을 전부 돌면서, 실제로 만들 수 있는 합(또는 동물)만 후보로 삼게 했어요. 쏠 방울도 가방(bag) 방식으로 뽑되, 현재 목표를 만들 수 있는 값에 가중치를 둡니다.

그리고 이게 맞는지 손으로만 확인하기엔 한계가 있어서 봇 시뮬레이터를 만들었어요. 봇이 수천 판을 돌려 달성률·클리어율을 뽑아 줍니다.

판조준 방식달성률
숫자 1,000판 무작위 24%
숫자 60판 맞출 수 있으면 맞춤 84%
동물 1,000판 무작위 6%
동물 60판 맞출 수 있으면 맞춤 36% ← 문제

숫자 모드는 바로 손으로 쳐 볼 만했는데, 동물 모드는 한참 손봐야 했어요. 나중에 스테이지 100개의 발사 수·천장 속도도 전부 이 봇(능숙한 봇 / 서툰 봇 두 종류)으로 승률을 재면서 맞췄습니다.

5. 동물 모드가 제일 어려웠다

① 방울에 무엇을 그릴까 처음엔 방울에 동물 얼굴을 넣었는데, 실제로 해 보니 "몸통 방울"에 얼굴이 있으니 이상하더라고요. 그래서 동물 한 마리를 옆모습으로 그리고 부위별로 잘라서, 방울에는 자기 부위 조각만 확대해 넣었습니다. 목표 카드에는 회색 실루엣 위에 필드에 모인 부위만 색이 칠해져요.

② 부위가 늘면 거의 안 모인다 난이도를 부위 수(3 → 4 → 5)로 올리려고 했는데, "이웃 한 겹" 판정으로는 4~5부위가 거의 모이지 않았어요.

난이도이웃 판정이어진 덩어리 판정
3부위 81% (이웃 판정 유지)
4부위 13~26% 46%
5부위 0~2% 41%

그래서 4·5부위는 같은 동물로 이어진 덩어리 전체에서 부위를 찾도록 바꿨습니다. 그랬더니 이번엔 "멀리 있는 조각이 왜 붙었지?"라는 반응이 나와서, 조준할 때 어떤 길로 이어졌는지 초록 선으로 보여 주도록 했어요. 규칙은 그대로 두고, 이유를 보여 주는 쪽을 택했습니다.

③ 버그 수준으로 막히던 판 동물 무한 모드에서 계속 돼지 몸통만 나오고, 카드에는 "아무거나 붙이면 완성"이라는데 넣을 곳이 없는 판이 나왔어요. 원인은 사방이 방울로 막힌 구멍도 "붙일 수 있는 칸"으로 세고 있었던 것. 그 구멍 옆에 부위가 모여 있으니 목표도 안 바뀌고, 도움 방울도 그 구멍용 부위만 계속 나왔던 거죠. 맨 아래에서 빈 칸끼리 이어져 실제로 닿을 수 있는 칸만 세도록 한 곳을 고치니 목표 생성·도움 방울·힌트가 한꺼번에 정상이 됐습니다. 덤으로 같은 방울이 세 번 연속 나오지 않게 막았어요.

6. 손맛 — "숫자가 방울을 벗고 탈출한다"

숫자가 맞았을 때 연출은 여러 번 갈아엎었어요. 최종 흐름은 이렇습니다.

  1. 방울이 살짝 부풀었다가 껍질이 조각으로 깨져 흩어지고
  2. 안에 있던 숫자만 톡 튀어나와 커졌다가 자리를 잡고
  3. 첫 숫자가 목표 카드로 날아가는 도중에 나머지 숫자들이 따라붙어 합쳐지며 값이 오르고 ("4 + 2 = 6")
  4. 카드에 쿵! — 카드가 눌리며 흔들리고, 충격파와 반짝이

전체 1.4초 정도인데, 느린 화면(8배 슬로모)으로 빌드해서 프레임 단위로 보면서 타이밍을 맞췄어요. 마지막 한 발로 클리어하면 연출이 다 끝난 뒤 폭죽과 함께 "스테이지 클리어!"가 뜨도록 순서도 정리했습니다.

7. 그림과 소리는 로컬 AI로

  • 이미지: 로컬 ComfyUI(Z-Image Turbo). 플랫 스티커 / 3D 클레이 / 동화 수채 / 광택 캐주얼 네 가지로 뽑아 보고, 외곽선이 굵어서 작은 방울 속에서도 잘 읽히는 광택 캐주얼로 정했어요. 동물은 흰 배경으로 뽑아 배경을 지우고, 스크립트로 부위 5조각을 잘랐습니다. 배경은 처음엔 밝은 낮 풍경이었는데, 어두운 게임판과 따로 놀아서 파티 야경 네 장으로 바꾸고 스테이지 진행·점수에 따라 교체되게 했어요.
  • 소리: Stable Audio 3. 처음 small 모델로 만든 BGM이 전부 잡음이었는데, 설정을 아무리 바꿔도 안 되더라고요. 스펙트로그램을 보니 음 줄무늬가 아예 없었고, medium 모델로 바꾸니 바로 해결됐습니다. 효과음 15종, BGM 3곡(이음매 없는 루프)이에요.
  • 글꼴: 시스템 글꼴이 게임과 안 어울려서 주아(SIL OFL)로 통일.
  • 제목 로고: AI가 한글을 정확히 못 그려서 "딱팡" 로고는 코드로 그렸어요. 네온 번짐 → 외곽선 → 금·분홍 그라데이션, 옆의 방울이 주기적으로 부풀다 톡 터집니다.

8. 난이도 = "얼마나 보여 주는가"

조준 미리보기가 너무 친절하면 머리를 쓸 일이 없어요. 그래서 난이도는 규칙이 아니라 보여 주는 양으로 나눴습니다.

  • 쉬움: 늘 전부 보임
  • 보통: 한 판에서 4번 터뜨릴 때까지 전부 → 12번까지 합계만 → 그 뒤엔 없음
  • 어려움: 2번까지 합계만 → 그 뒤엔 없음

기준을 발사 수가 아니라 터뜨린 횟수로 잡은 이유는, 빗나가는 동안엔 계속 도움을 받으며 연습하고 잘 맞히는 사람만 빨리 어려워지게 하려는 거예요. 해 보니 숨긴 줄 알았는데 새는 힌트가 계속 나왔어요. 합계 글자의 색(초록/분홍), 목표 카드의 금색 테두리, 합계 라벨이 뜨는 위치까지 — 하나씩 찾아서 가렸습니다.

9. iOS 이식에서 배운 것

  • Kotlin/Native 모듈을 Swift에서 부를 때 List<Int>, Map<Int, …> 를 그대로 넘기면 원소가 NSNumber 로 와서 캐스트 순간 앱이 죽어요. 스테이지 목록과 판 끝에서 튕기던 원인이었고, 브리지에서 IntArray 나 스냅숏 객체로 바꿔 넘겨서 해결했습니다. (new… 로 시작하는 함수 이름이 바뀌는 것, copy() → doCopy() 도 소소한 함정)
  • 광고 SDK가 처음 뜰 때 웹 프로세스를 띄우느라 기기가 몇 초 버벅이면서 BGM이 끊겼어요. 그래서 인트로를 로딩 화면으로 바꿔 그동안 서버 확인·그림 해독·BGM 준비·광고 SDK 깨우기를 다 끝내고, 홈에 들어가는 순간 음악이 바로 흐르게 했습니다.
  • SwiftUI 버튼은 게임패드로 눌리지 않아서, 메뉴용 패드 포커스 이동을 직접 만들었어요.

10. 출시 직전에 만난 것들

  • 릴리즈 빌드만 실행 즉시 종료. 디버그는 멀쩡했어요. 광고 SDK가 끌고 오는 WorkManager → Room 이 앱 시작 때 DB 클래스를 이름으로 찾아 기본 생성자로 만드는데, R8이 그 생성자를 지워 버렸던 것. usage.txt 에 지운 목록이 그대로 찍혀 있어서 금방 찾았고, keep 규칙 한 줄로 해결했습니다. 교훈: 릴리즈는 꼭 실기에서 한 번 띄워 본다.
  • Play 권장 조치 4건(오래된 SDK, 지원 중단 API, edge-to-edge, 화면 방향 고정). 방향은 매니페스트에서 빼고, 폰만 코드로 세로 고정, 태블릿·펼친 폴드는 자유 회전하게 바꿨어요.
  • 데이터 보안 양식: 회원가입이 없어서 계정 삭제 대상은 아니지만, 데이터 삭제 요청 URL은 따로 필요해서 소개 페이지에 삭제 요청 안내를 넣었습니다. 오류 보고 서버도 HTTPS로 옮겨 "전송 중 암호화"에 "예"로 답할 수 있게 했어요.

11. 마치며

기획서 초안부터 두 스토어에 올리기까지 일주일 남짓 걸렸어요. 돌아보면 제일 잘한 결정은 "판정 범위를 이웃 한 겹으로 고정한 것" 과 "규칙을 공용 모듈 하나에 두고 봇으로 숫자를 잰 것" 이었습니다. 나머지는 대부분 실기에서 직접 해 보고 고친 것들이에요.

다음에는 실제 플레이 데이터를 보면서 스테이지 난이도를 다듬고, 동물 세트와 방울 테마를 늘려 볼 생각입니다.

 

https://play.google.com/store/apps/details?id=com.ecsoft.ddakpang

 

play.google.com

 

 

DDAK PANG App - App Store

Download DDAK PANG by UI CHAN HONG on the App Store. See screenshots, ratings and reviews, user tips, and more apps like DDAK PANG.

apps.apple.com

 

 

읽어 주셔서 고맙습니다. 해 보시고 의견 남겨 주세요! 🫧