🤖
2498 in / 2000 out / 4498 total tokens
다리건너기를 1명씩 순차 도전하는 구조에서 M명이 동시에 다리 위에 올라가는 병렬 모델로 갈아엎었다. 기존엔 도전자 한 명이 6열을 다 건너거나 떨어질 때까지 나머지는 대기 plat에서 가만히 서 있어야 했다. M명이면 worst case M×16초. 4명만 있어도 1분 넘게 아무것도 안 하고 바라보는 시간이 발생한다. 병렬 구조로 바꾸니 모든 runner가 동시에 진행해서 30초면 한 판이 끝난다.
핵심은 state.current를 날리고 state.actives[]와 state.startQueue를 도입한 거다. 기존엔 현재 도전자 하나만 추적하면 됐지만, 이제는 다리 위에 뛰어든 모든 runner의 위치, 상태, 점프 진행도를 개별적으로 관리해야 한다. col마다 모든 살아있는 runner가 도착한 뒤 동시에 점프하는 wave gating 방식을 썼다. deterministic 시차를 줘서 자연스럽게 보이지만, 실제로는 같은 col의 runner들이 한 번에 움직이는 거다. 이렇게 하면 "누가 먼저 안전 발판을 밟았나"로 결과가 갈리지 않고, 같은 열의 runner들은 동일한 타이밍에 운명을 공유하게 된다.
시각 쪽에서 가장 신경 쓴 건 식별성이다. 여러 캐릭터가 같은 발판에 서면 겹쳐서 누가 누군지 분간이 안 된. 그래서 같은 발판 위 다인 배치에 jitter ±36/22을 줬고, col마다 다른 분산 패턴을 적용했다. tileSize를 1.5배(450×214) 키우고 rowStep도 비례 확장, 캐릭터 스케일은 살짝 줄였다. 발판이 넓어지니까 캐릭터들이 퍼져 서 있을 공간이 생기고, col마다 분산 패턴이 다르니까 어느 열에서든 위치로 누군지 유추할 수 있다.
물리 cascade fall도 추가했다. 발판이 깨질 때 그 위에 있던 모든 active runner가 동시에 추락하는데, winner는 면역이고 avatar는 freeze 상태로 처리한다. UE5에서 타이밍 기반 cascade destroy 구현하던 감각이 여기서도 살짝 묻어났다. 발판 파괴 → 위에 있던 runner 감지 → 동시 추락 애니메이션 트리거, 이 시퀀스를 프레임 단위로 맞추는 게 은근 까다로웠다.
이전엔 점프 전에 5단계 와리가리(prelback)를 넣었는데 이걸 다 뺐다. 도전 점프만 남기고 천천히(0.55초) 날아가는 모션 하나로 통일. 와리가리가 긴장감을 준다기보다 그냥 기다림이었고, 병렬로 여러 명이 동시에 와리가리하면 시각적으로 난장판이 된다. 과감히 잘라냈다.
카메라는 pathIndex 내림차순으로 가장 앞서가는 runner를 추적한다. 여러 명이 동시에 달리니까 가장 진척이 빠른 사람 기준으로 화면이 따라간다. 뒤처진 runner가 떨어지는 건 화면 밖에서 일어나는데, 이게 의외로 몰입감에 좋다. 앞서가는 사람 시점에서 뒤에서 떨어지는 소리만 들리는 연출.
jitter 동기는 mulberry32 deterministic seed로 처리했다. 호스트와 게스트가 같은 seed로 같은 jitter 값을 계산하니까 네트워크 동기가 맞는다. 클라이언트 Math.random은 순수 시각 효과에만 쓰이고 게임 결과에는 0회 관여. Socket 이벤트명이나 payload 포맷은 한 건도 안 건드렸다. DB, bridgeCrossHistory, ranking 형식도 그대로. 서버 endDelay만 기존 M×8초+8초(최대 120초)에서 30초 고정으로 바꿨다. 병렬 진행이니까 runner 수에 무관하게 한 판이 30초 안에 끝난다.
js // 병렬 진행 모드 — 모든 runner가 동시에 다리 위에 있으므로 M에 무관. // 한 runner 풀 path = ~16초 + jitter 마진 → 30초 캡 (impl §4-8) const endDelay = 30000;
가장 고민했던 건 wave gating의 타이밍 계산이다. 각 runner의 점프 속도가 같으니까 같은 col에서 출발하면 같은 col에 같이 도착하는 건 보장되는데, 시작 시차(0.2~0.5초) 때문에 첫 번째 runner가 col 3에 있을 때 마지막 runner는 col 1에 있을 수 있다. 이걸 col별로 "이 col에 아직 도착 안 한 active가 있으면 대기"로 제어했다. 모든 active가 같은 col에 도달한 순간, 그 col의 발판 점프가 일제히 시작된다. 구현 명세 문서에 시퀀스 다이어그램까지 그려놨으니 나중에 다시 볼 일 있으면 docs/meeting/impl/2026-04-29-bridge-cross-parallel-run-impl.md 참고.
단일 state.current → state.actives[] 병렬 모델 전환, wave gating으로 동시성 보장하면서도 deterministic하게.