dotgimon 제작기 20. P0 스파이크 통과와 폰·워치 동기화
워치에서도 TS 엔진이 돌았다, KMP로 가지 않았다
P0 스파이크는 watchOS(Apple Watch 운영체제)와 Wear OS(Google의 스마트워치 운영체제) 워치에서 TypeScript 게임 엔진을 QuickJS-ng(기기에 내장할 수 있는 경량 JS 엔진)로 돌릴 수 있는지 확인하는 실험이었다. 관문 네 개를 모두 통과했다.
| 관문 | 기준 | 결과 |
|---|---|---|
| golden 일치 | Node == QuickJS | desktop / watchOS / Wear 모두 PARITY OK |
| 프레임 비용 | avg < 5ms, p95 < 8ms | 라이브 avg 0.34 |
| 아키텍처 | arm64·arm64_32 / v8a·v7a | 전부 포함 |
| 시간대 | 엔진 시각 == 네이티브 시각 | 일치 |
golden은 시드를 고정한 시나리오의 기대 출력이다. Node에서 돌린 결과와 각 기기의 QuickJS에서 돌린 결과가 같아야 통과다. 아키텍처 관문은 기기 CPU 아키텍처별 빌드가 빠짐없이 들어갔는지 본다. watchOS는 arm64·arm64_32, Android는 ABI(CPU 아키텍처별 바이너리 규격) 기준 v8a·v7a다.
JS 힙은 620~665KB였다. KMP(Kotlin Multiplatform, Kotlin 코드 하나를 iOS·Android 등 여러 플랫폼에서 공유하는 기술)로 엔진을 다시 쓰지 않고 TS 엔진을 그대로 쓰기로 했다. 아직 실기기에서는 측정하지 않았다.
동기화의 결정성을 "표준 재계산"으로 정의했다
폰과 워치 양쪽에서 키울 수 있으려면 두 기기의 상태를 맞춰야 한다. 동기화의 결정성은 "표준 재계산" 기준으로 정의했다. 같은 액션 로그를 같은 엔진으로 다시 계산하면 두 기기의 결과가 같아야 한다는 기준이다.
메시지에는 현재 상태 스냅샷과 체크포인트 이후의 액션을 담는다. 받는 쪽에서는 양쪽에 액션이 있는지에 따라 처리가 갈린다.
이걸 위해 액션 기록기를 만들고, replay·merge, 다음 이벤트를 예측하는 predict를 차례로 붙였다.
RN에 의존하지 않는 GameSession도 만들었다. 저장 주기, 롤백, 액션 기록을 여기서 맡는다. useGame 훅이 하던 저장·리셋·링크 처리를 GameSession으로 넘겼다.
폰과 워치를 나란히 놓으니 스케일부터 달랐다
처음 띄운 워치 화면은 레이아웃이 깨져 있었다. 버튼 시안 중 A안(둥근 버튼)으로 확정했다.
폰과 워치를 나란히 놓고 보니 스케일부터 달랐다. 손대기 전에 원인부터 찾았다.
- 격자선 불투명도 0.10 → 0.035, 유리 반사광 0.32 → 0.12
- 글자 화면은 단색 LCD 바탕에 잉크 도트를 1.5배로
- 엔진 도트를 소수 좌표 사각형으로 받아 폰의 Skia(React Native용 2D 그래픽 라이브러리)와 같은 방식으로 그렸다. 작은 화면에서도 글자 획이 살아남았다.
기기 색 이름에 붙어 있던 한글, 숫자, active 아이콘은 뺐다. 명예의 전당은 "히스토리"로 이름을 바꿔 도감 옆 아이콘으로 옮겼다.
브랜치를 합치다 LCD 배경이 롤백돼 있었다
병행하던 브랜치를 합치다가 LCD 배경 디자인이 롤백된 걸 발견했다. WIP 커밋으로 보존해 두고 다시 병합했다.
이름을 dotgimon으로 정하고 자잘한 것들을 손봤다
이름도 이날 dotgimon으로 정했다. 파일·폴더명은 kebab-case로 통일했다. 내부 DB 파일명과 저장 키는 기존 데이터와의 호환을 위해 그대로 뒀다.
RN 번들이 깨지는 일도 있었다. lcdScene에 있던 아이콘 경로 정규식을 Tailwind가 클래스 이름으로 오인했다. 이것도 고쳤다.
- ESLint
max-lines(180줄)는src/만 검사하고, 걸리면 파일을 쪼개도록 규칙으로 정했다. - i18n은 기기 언어에 따라 한국어/영어로 나눴다. LCD 안의 게임 문구는 번역하지 않았다. 게임기 감성을 지키고 싶었다.
- 알 무늬를 2px 외곽선 안쪽으로 옮겨 1px 흰 틈을 확보했다.
- 훈련은 3라운드를 1세트로 카운트하게 바꿨다. 이전에는 HIT 이상인 라운드마다 1번씩, 최대 3번이 카운트됐다. 밸런스는 "천천히 키우는" 쪽으로 잡았다.
- 도감 진화 트리를 핀치로 확대하고 드래그로 옮길 수 있게 했고, 스크롤바는 숨겼다. 안드로이드에서는 가로 제스처가 안 먹어서 따로 고쳤다.