Skip to content

Latest commit

 

History

History
426 lines (318 loc) · 26 KB

File metadata and controls

426 lines (318 loc) · 26 KB

렌더링과 아트 디렉션

원작의 규칙은 가져오되 표현은 전면 재설계한다. 이 문서는 "고급스럽게"를 구체적인 지시로 바꾼 것이다.

렌더러는 sim/을 읽기만 한다 (절대 규칙 6). 보기 좋게 만들려고 지형을 보정하는 코드가 들어가는 순간 클라이언트마다 다른 게임이 된다.


1. 아트 디렉션 — 파괴가 색을 드러낸다

1.1 발상

이 게임의 지형은 지질 단면이다. 흙을 파면 안쪽이 보인다. 이건 다른 게임에는 없고 우리에게만 있는 자산이다.

그래서 아트의 중심 명제를 이렇게 잡는다.

손대지 않은 풍경은 어둡고 채도가 낮다. 파괴한 자리에서만 색이 나온다.

  • 지표면은 풍화된 무채색에 가까운 흙빛. 실루엣만 읽히는 어두운 덩어리
  • 폭발이 파낸 단면은 훨씬 밝고 채도가 높다. 산화철 붉은 점토, 창백한 모래, 푸른 슬레이트 암반
  • 새로 노출된 면은 턴이 지나면서 서서히 지표색으로 풍화된다

결과적으로 라운드가 진행될수록 맵에 전투의 흔적이 색으로 축적된다. 5라운드째 맵을 보면 그 판에서 무슨 일이 있었는지 그림만 보고 알 수 있다. 지형이 기록 매체가 된다.

이건 예쁜 효과가 아니라 메카닉의 직접적 시각화다. 파괴가 유일하게 색을 만드는 행위이므로, 플레이어가 지형을 부수고 싶어진다.

1.2 시대·분위기

황혼의 먼지 협곡. 파란 하늘 없음. 대기에 먼지가 가득한 늦은 오후. 낮은 태양이 지평선에서 옆으로 들어오고, 지형은 역광으로 검게 선다.

레퍼런스로 삼지 않는 것: 카툰 렌더링, 파스텔, 저폴리, 픽셀아트 리메이크. 셋 다 "옛날 게임 리메이크"의 기본값이고, 우리가 하려는 것과 정반대다.

1.3 팔레트

/* 하늘 — 위에서 아래로 */
--sky-zenith:   #3A2E4A;   /* 탁한 자주. 파란색을 쓰지 않는다 */
--sky-mid:      #7E5A50;   /* 먼지 낀 장밋빛 갈색 */
--sky-horizon:  #D99A5B;   /* 지평선 황토빛 광원 */

/* 지형 — 표면 */
--crust:        #4A4038;   /* 풍화된 지표. 거의 검은 따뜻한 회색 */

/* 지형 — 노출 단면 (밝고 채도 높음) */
--sand-fresh:   #C9A56B;   /* SAND */
--soil-fresh:   #A6472E;   /* SOIL — 산화철 점토 */
--scree-fresh:  #8A8175;   /* SCREE — 각진 자갈 */
--rock-fresh:   #55606B;   /* ROCK — 푸른 슬레이트 */
--bedrock:      #2C3239;   /* BEDROCK — 거의 검음. 절대 안 변함 */

/* 팀 — 지형이 따뜻하므로 팀색은 차갑고 강하게 */
--team-a:       #3DDCFF;   /* 전기 시안 */
--team-b:       #B4FF3D;   /* 애시드 라임 */
--team-c:       #FF3D9E;   /* 마젠타 */
--team-d:       #9D6BFF;   /* 바이올렛 */

/* 화면에서 유일한 흰색 */
--blast:        #FFF4E0;   /* 폭발 코어. 다른 어디에도 흰색을 쓰지 않는다 */

팀색이 차가운 이유: 지형 팔레트 전체가 황토~적갈 계열이다. 팀색까지 따뜻하면 탱크가 지형에 묻혀 안 보인다. 차가운 고채도 색은 이 팔레트 안에서 유일한 이질적 존재가 되어 어디서든 즉시 읽힌다.

흰색을 폭발에만 쓰는 이유: 화면에 흰색이 폭발밖에 없으면, 흰색이 등장하는 순간 눈이 반드시 그리로 간다. UI에도 순백을 쓰지 않는다. UI 텍스트는 #E8DFD2.

1.4 타이포그래피

이 게임에서 가장 중요한 활자는 숫자다. 각도, 파워, 바람, 데미지, HP. 전부 숫자다.

역할 서체 근거
수치 Martian Mono (또는 IBM Plex Mono) 고정폭 필수. 파워가 999→1000으로 바뀔 때 레이아웃이 흔들리면 안 된다. 슬래시 0 필수
라틴 디스플레이 Chakra Petch 각지고 기계적. 측량 장비의 각인 같은 인상. SF가 아니라 계측기
한글 본문·UI Pretendard 한글 UI 서체의 현실적 최선. Chakra Petch와 굵기 체계가 잘 맞는다
  • 수치는 tabular-nums 고정
  • 라벨은 전부 대문자 + 자간 확대 (letter-spacing: 0.12em). 계측기 각인의 인상
  • 한글 UI에 Chakra Petch를 억지로 섞지 않는다. 숫자·영문 라벨만 라틴 서체, 나머지는 Pretendard

1.5 시그니처 — 이 게임을 기억하게 만들 세 가지

보수적으로 셋만 한다. 나머지는 전부 조용히.

  1. 지층 노출과 풍화 (§1.1) — 이게 첫 번째이자 가장 중요하다
  2. 탄도 잔상 — 지난 턴들의 궤적이 팀색 가는 선으로 화면에 남아 턴이 지날수록 옅어진다. 기능(조준 보조, game-design.md §5.1)이자 연출이다. 라운드 후반의 화면은 궤적들이 겹쳐 만든 드로잉이 된다
  3. 바람은 먼지로 읽는다 — 화면 전체를 가로지르는 미세 먼지 입자가 바람 방향·세기로 흐른다. 상단 게이지도 있지만 숫자를 안 봐도 체감되는 것이 목표다

하지 않는 것: 화면 흔들림 남발, 파티클 도배, 네온 글로우, 히트마커, 데미지 숫자 팝업 난무. 폭발 하나가 압도적이려면 나머지가 조용해야 한다.


2. 왜 WebGL2인가

Canvas 싱글플레이 화면 개편

tools/prototype/의 작전실은 자체 SVG 협곡 아트, 크림색 활자와 황동색 강조, 얇은 계측선으로 구성한다. 전투 화면은 상단 내비게이션 / 전체 지형이 보이는 16:9 전장 / 하단 무기·조준 조작부로 구분한다. PC에서는 21:9 와이드 시야를 기본으로 하고 하단 깊은 지층 일부를 크롭한다. 이미지 비율이나 물리 좌표는 늘이지 않는다. 활성 포대의 하강을 세로 시야로 따라가며, 상단의 전체 지형 버튼으로 16:9 전체 맵을 볼 수 있다. 풍향계는 전장 캔버스가 아닌 고정 HUD에 표시한다. 현재 바람의 세기와 방향은 항상 보이고, 다음 턴 예보는 기존처럼 측풍계 보유 시에만 보인다. 카메라 이동으로 풍향계가 잘려서는 안 된다. 전장 컨테이너는 overflow: clip으로 비스크롤 영역을 만들어 키보드 포커스가 HUD를 밀어 올리지 않게 한다. 모바일은 전체 맵을 유지하고 작은 화면에서는 세로 스크롤을 허용한다. 모바일 관측경은 114px 폭으로 줄여 포대 체력·중앙 풍향계·파워 바와 겹치지 않게 한다. 시작 화면·작전 선택·결과·상점은 DOM이며 키보드 포커스와 reduced-motion을 지원한다. 렌더 전용 원경의 높이와 명암, 지층의 질감·경계 조명을 개선하되 시뮬레이션 격자는 그대로 읽는다. 이는 아래 WebGL2 최종 파이프라인과 별개인 Canvas 개선이다.

전장 내부 디테일 패스 (B19). battlefield-art.js에서 시드 기반 재질 질감과 원경을 캐시한다. 모래는 잔결, 흙은 얇은 퇴적층, 자갈은 각진 입자, 암반은 불규칙 절리와 광물맥으로 구분한다. 색상은 실제 MaterialGrid를 읽어 정하고 표면 아래의 깊이·주변 빈 셀에 따라 광량만 바꾼다. 원경도 단색 언덕 대신 여러 명도의 바위 면·층리와 안개를 사용한다. 허공에 충돌할 것처럼 보이는 가짜 근경 바위는 만들지 않는다. 탱크 모델의 포구는 물리의 shotPose에서 가져온 좌표와 맞춘다. 장갑판·궤도·포방패·공구·해치·배기구를 그리는 엔티티 레이어는 2배 해상도로 렌더한다. 차체는 기존 대비 시각적으로 12% 확대하지만 충돌 크기·포구 좌표·사거리는 확대하지 않는다. 폭발 조명은 실제 고체 셀 마스크에만 합성한다. 동작 줄이기 설정에서는 섬광·포신 반동·피해 연기를 생략한다. 관측경은 조준 중 탱크 4배 확대 → 비행 중 포탄 추적 → 착탄·붕괴 관측으로 전환한다. 비행 중에는 기본 2배로 넓혀 주변을 보여주고, 분열탄은 현재 탄들의 범위에 맞춰 자동 축소한다. scope-camera.js가 본 전장과 같은 비행 샘플 보간을 사용한다. 아직 재생되지 않은 궤적이나 미래 착탄 정보는 참조하지 않는다. 화면 밖 포탄도 같은 현재 샘플을 그리며 미지의 지형은 추가하지 않는다. 착탄 뒤 최소 1.4초 및 붕괴 중에는 결과를 보여준다. 새 발사는 대기 없이 추적하며 게임 진행은 지연시키지 않는다. 전체 전장을 대체하지 않으며 접을 수 있고, 좁은 화면에서는 처음부터 접어둔다.

전투 시네마틱 패스. combat-art.js는 시뮬레이션과 분리된 Canvas 표현 계층이다. 표준탄(황동 탄대), 파쇄탄(붉은 중량 장갑), 분열탄(분절 캐니스터와 소형 자탄), 굴착탄(나선형 관통두), 전복탄(회전 구형 탄체), 성형탄(긴 구리 관통두), 적층탄(녹색 적재통), 핵포탄(대형 안정핀과 위험 표식)을 실제 탄체 실루엣으로 구분한다. 발사 당시의 유효 무기를 트랙에 보관하고 현재 보간 위치와 경로 접선으로 탄체를 회전한다. 무기 선택 카드·본 전장·화면 밖 관측경은 동일한 탄체 렌더러를 사용한다. 팀색은 지난 궤적과 표식에 남기고, 비행 중에는 얇은 응축 흔적·연무·금속 반사를 사용한다. 분열 시 캐니스터 개방, 굴착 중 토사 분출, 전복 중 회전과 접지 불꽃을 별도로 표현한다. 착탄은 무기별 화염·지면 충격파·방사형 파편·후속 연무를 겹치되 실제 파괴 범위를 바꾸지 않는다. 흙을 쌓는 적층탄은 화염 대신 토사 기둥, 성형탄은 좁고 강한 섬광을 사용한다. 연기·먼지 스프라이트는 최초 1회 캐시하며 입자 상한 1,200개를 유지한다. 바람은 풍향에 따라 이동하는 공중 흐름·능선 먼지·차체 안테나·배기 연기에 반영한다. 표면 장식은 실제 지표를 읽고 갱신하며 격자를 수정하지 않는다. 무풍에는 흐름을 멈춘다. 동작 줄이기에서는 대기 이동·포신 반동·화면 섬광·충격파 확장을 생략하고 짧은 정적 착탄 표식을 남긴다.

Phase 3.5 검증 구현. tools/prototype/index.html에 Canvas 2D로 황혼 원경, 신규 노출면, 대기 먼지, 동시 탄막, 폭발·붕괴 입자, 전장 HUD와 절차적 음향을 먼저 구현했다. 아트 방향과 손맛을 검증하기 위한 버릴 수 있는 코드이며, 이 문서의 WebGL2 최종 구현을 대체하지 않는다. 렌더 전용 노출 나이와 입자 샘플링이 시뮬레이션으로 되먹이지 않는다는 경계도 이 단계에서 확인한다.

Bulwark는 Canvas 2D였다. 여기서는 다르게 간다.

요구 Canvas 2D WebGL2
519k 셀을 매 프레임 색으로 변환 CPU에서 픽셀 루프. 불가능 텍스처 1장 + 프래그먼트 셰이더. 무료
지형 법선 조명 불가능 셰이더 몇 줄
폭발 광원이 지형을 비춤 불가능 무료
모래 입자 수천 개 버거움 인스턴싱으로 무료
블룸/그레인 불가능 포스트 패스

§1의 아트 디렉션 전체가 셰이더 없이는 성립하지 않는다. 그래서 스택을 바꾼다.

WebGL2로 하되 엔진(Three.js/PixiJS)은 쓰지 않는다. 우리가 필요한 건 텍스처 몇 장과 셰이더 대여섯 개뿐이고, 엔진을 얹으면 번들 크기와 추상화 비용만 늘어난다. 직접 작성한다.


3. 파이프라인

[1] 배경        원경 능선 3레이어 + 하늘 그라디언트          (전체 화면 쿼드)
[2] 지형        MaterialGrid 텍스처 → 색 + 조명              (전체 화면 쿼드)
[3] 탄도 잔상   지난 궤적 라인                              (라인 배치)
[4] 엔티티      탱크, 비행 중인 포탄                         (인스턴싱)
[5] 입자        모래, 먼지, 파편, 연기                       (인스턴싱)
[6] 광원 합성   폭발 광원을 [2]의 법선에 적용                 (가산 블렌딩)
[7] 포스트      블룸 → 비네트 → 그레인                       (전체 화면 쿼드)
[8] HUD         DOM 오버레이 (WebGL 아님)                     

HUD는 DOM으로 만든다. 접근성, 한글 폰트 렌더링, 상점 UI 복잡도 전부 DOM이 압도적으로 유리하다. 캔버스 안에 텍스트를 그리려는 시도를 하지 않는다.


4. 지형 렌더링

4.1 텍스처

텍스처 포맷 크기 갱신
uMaterial R8UI 960×540 변경된 행만 texSubImage2D
uAge R8 960×540 턴 종료 시 1회 (§4.4)
uPalette RGBA8 16×2 최초 1회

uMaterialsim/Uint8Array를 그대로 올린다. 변환하지 않는다.

업로드 대상은 terrain.md §5.1 의 활성 행 비트마스크다. 단, 이름을 정확히 써야 한다 — §5.1 이 정의한 것은 "변경된 행"이 아니라 "가동(움직일 수 있는) 셀이 있는 행" 이다.

집합 무엇 관계
변경 행 이번 프레임에 실제로 값이 바뀐 행 업로드에 필요한 최소 집합
활성 행 (§5.1) 가동 셀이 있는 행 ∪ 그 8이웃 변경 행의 상위집합

활성 행을 업로드 집합으로 쓰는 것은 안전한 과대 근사다 — 안 바뀐 행을 다시 올려도 그림은 같고 비용만 조금 더 든다. 반대로 변경 행을 계산하려고 격자 사본을 들고 diff 를 뜨면 519 KB 와 프레임당 전수 비교가 추가된다. 그럴 가치가 없다.

프레임당 마스크는 SUBSTEPS 개 스텝의 합집합이다. 한 프레임에 여러 스텝이 돌기 때문이다. 소유권을 명시한다: sim 은 스텝마다 별도 버퍼에 OR 로 누적만 하고 절대 지우지 않는다. 렌더러가 업로드 후 consumeDirtyRows() 로 지운다. 누적 버퍼를 sim 상태와 분리해 두면 렌더러가 sim 을 쓰지 않으므로 절대 규칙 6 을 지킨다.

4.2 경계 부드럽게

셀이 2px이므로 그대로 그리면 계단이 보인다. 프래그먼트 셰이더에서 편다.

// 3×3 이웃의 채움 여부로 커버리지 근사
float coverage = 0.0;
for (int dy=-1; dy<=1; ++dy)
  for (int dx=-1; dx<=1; ++dx)
    coverage += isSolid(dx, dy) * kernel[dy+1][dx+1];
// coverage를 알파로 → 경계가 자연스럽게 녹는다

이 부드러움은 순수 렌더 효과다. 충돌 판정은 항상 원본 셀 값으로 한다 (절대 규칙 6). 눈에 보이는 경계와 실제 충돌 경계가 1셀 어긋날 수 있는데, 2px이라 체감되지 않는다. 어긋남이 문제가 되면 커널을 좁힐 것이지 sim을 건드리지 않는다.

4.3 조명

vec2 grad   = sobel(coverage);          // 커버리지 기울기 = 표면 법선
vec3 N      = normalize(vec3(grad, 0.6));
vec3 L      = KEY_LIGHT_DIR;            // 지평선에서 옆으로 들어오는 저각 광원
float lam   = max(dot(N, L), 0.0);
vec3 color  = base * (AMBIENT + lam * KEY_COLOR) + rim(N) * RIM_COLOR;

저각 키라이트가 이 게임 조명의 전부다. 지형이 옆에서 조명받으면 요철이 극도로 과장되어 보이고, 구덩이 하나하나가 깊이감을 갖는다. 정면 조명이면 지형이 납작한 색면이 된다.

4.4 풍화 — 시그니처 구현

uAge는 각 셀이 노출된 지 몇 턴이 지났는지를 담는다.

노출 시점(폭발/붕괴로 재질이 드러남) → age = 0
턴 종료마다 age += 1  (상한 255)

셰이더:  base = mix(FRESH[material], CRUST_COLOR, weather(age))

uAge는 렌더 전용 상태다. sim/에 넣지 않고, sim으로 되먹이지 않는다. 턴 이벤트에서 결정론적으로 파생되므로 클라이언트마다 동일하지만, 게임 규칙에는 영향이 없어야 한다. 여기서 게임 로직을 읽는 순간 절대 규칙 6 위반이다.

weather() 곡선은 처음 2~3턴에 빠르게, 이후 천천히 수렴시킨다. 방금 판 자리는 확실히 밝고, 오래된 상처는 흐릿하게 남는 것이 목표다.

4.5 비행 중 포탄 보간

탄도 재생은 경로 길이에 상관없이 초당 48틱이다. 물리 적분의 60Hz는 유지하고 표현만 20% 느리게 한다. 짧은 탄도를 2.4초로 늘이던 최소 재생 시간과 긴 탄도를 9초에 맞추던 정규화는 제거한다. 고속 보기만 8배로 재생한다. 렌더러는 현재 재생 시각에 해당하는 두 탄도 샘플 사이를 선형 보간해 매 화면 프레임의 포탄 위치와 잔상 끝점을 계산한다.

보간값은 렌더 전용 부동소수점이다. 충돌·착탄·폭발 시각과 위치는 원본 정수 샘플만 사용하며, 보간 결과를 sim/ 상태로 되먹이지 않는다.

4.6 지형에 붙는 탱크 차체

탱크 차체는 sim이 좌우 궤도 지지점에서 계산한 tankTilt10을 목표 각도로 사용한다. 표시 각도는 매 프레임 목표값 쪽으로 보간해 붕괴 중 갑자기 꺾이지 않게 한다. 반원형 각도계의 중심·기준선과 포신도 같은 경사를 따르며, 입력 마커는 차체 기준 상대각을 표시한다.

경사 목표와 실제 탄도각은 정수 sim 값이다. 렌더러가 보간하는 것은 차체의 화면 회전뿐이며, 이 부동소수점 표시값을 탄도에 되먹이지 않는다.


5. 입자

종류 발생 수량
모래 낙하 자동자에서 이동한 셀의 일부만 샘플링 (1/20) ~2000
먼지 구름 붕괴 규모 비례 ~500
파편 폭발 시 ~300
대기 먼지 (바람) 상시 ~800

이동한 셀 전부에 입자를 만들지 않는다. 대형 붕괴는 셀 수만 개가 동시에 움직이는데 그걸 다 그리면 죽는다. 샘플링 비율은 시각적으로 충분하며, 샘플링에 사용하는 난수는 렌더 전용이라 결정론과 무관하다.

입자는 인스턴싱된 쿼드. 물리는 CPU에서 간단하게 (GPU 파티클 시스템까지 갈 필요 없다).


6. 폭발 연출

폭발 하나에 압도적인 무게를 실어야 한다. 순서:

0ms    화면 전체 미세 플래시 (--blast)
0ms    폭발 광원 점등 → 지형 법선에 즉시 반영
50ms   충격파 링 (화면 왜곡 살짝)
80ms   파편 + 먼지 분출
150ms  광원 감쇠 시작
200ms  카메라: 가장 큰 폭발로 짧게 줌인 (동시 폭발 중 하나만)
~2s    먼지 잔류

화면 흔들림은 최대 폭발 1회에만. 6발이 동시에 터진다고 6번 흔들면 화면을 볼 수 없다. prefers-reduced-motion이 켜져 있으면 흔들림과 왜곡을 전부 끄고 광원만 남긴다.

6.1 Canvas 핵포탄 버섯구름

nuclear-art.js는 핵 전용 연출과 관측 범위를 공유한다. 착탄 후 0.2초부터 불기둥이 올라오고, 0.6~2.5초에는 좁은 기둥 위에 넓고 둥근 머리가 펼쳐진다. 밝은 상단·어두운 하부·안쪽의 주황색 잔열·말려 올라가는 구름 덩어리·바닥 먼지 고리를 겹쳐 6.2초에 걸쳐 소멸한다. 구름은 충돌하지 않는 순수 시각 효과이며 착탄 당시 바람을 사용한다. 다음 턴의 바람 변화로 갑자기 꺾이지 않는다. 핵포탄의 관측경은 구름 최대 높이를 포함하는 고정 범위와 큰 패널로 넓히고 최대 6.2초까지 착탄 관측을 유지한다. 지도 바깥으로 솟은 부분도 관측경에 그리되 미지의 지형은 만들지 않는다. 새 발사는 즉시 추적한다. 닫힌 관측경도 핵폭발 중에만 자동으로 펼쳤다가 원래 상태로 돌아간다. 사용자가 그 폭발의 관측경을 닫으면 다시 강제로 열지 않는다. 메인 와이드 뷰는 핵구름을 보는 동안 위쪽 시야를 확보한다. 화면 흔들림과 섬광 세기는 더 올리지 않는다. 동작 줄이기에서는 확대·상승·점멸을 생략하고 1.4초 동안 정적인 버섯구름 실루엣을 표시한다. 핵 전용 연무는 시작할 때 생성한 128×128 노이즈 텍스처 4장을 재사용하고, 프레임당 최대 56개의 덩어리로 그린다. 텍스처 회전은 덩어리별 고정값이라 움직일 때 질감이 떨리지 않으며 기존 입자 상한 1,200개도 늘리지 않는다. 핵포탄으로 라운드가 끝나면 승패·정산은 즉시 적용하고 보급/결과 모달만 착탄 후 2.8초까지 기다려 구름이 만들어지는 장면을 가리지 않는다. 빠르게 넘기기·동작 줄이기에서는 이 대기를 생략한다.


7. HUD

지형이 주인공이므로 HUD는 화면 가장자리로 물러난다.

상단 중앙    바람 게이지 (방향 바 + 현재 숫자, 강풍 강조)
상단 좌우    플레이어 카드 (HP, 팀색, 이름)  — 인원수에 따라 배치
하단 좌측    각도 / 파워 수치 (Martian Mono, 크게)
하단 우측    무기 선택 (아이콘 행)
하단 중앙    긴 파워 충전 바 + 클릭/드래그 목표 핀
중앙         없음. 절대 아무것도 두지 않는다

조준 중에는 탱크 뒤쪽에 반원형 각도계가 뜬다. 포신은 실제 곡사포처럼 짧고 굵게 유지하며, 각도계의 외곽 눈금·마커가 정확한 조준 방향을 대신 보여준다. 중심에서 길게 뻗는 가이드 선은 쓰지 않는다. 각도계 기준선은 차체 경사를 따라 회전하고 마커는 그 기준선에서 잰 상대 조준각을 표시한다. 이것만이 화면 중앙부에 나타나는 UI이며 확정하면 즉시 사라진다.

7.1 색각 접근성

팀색 4종(시안/라임/마젠타/바이올렛)은 대부분의 색각 유형에서 구분되지만 완전하지 않다. 탱크 상단에 팀별 도형 마커(●/■/▲/◆)를 함께 표시한다. 색만으로 구분되게 두지 않는다.


8. 성능 예산

항목 목표
프레임 60 fps (1920×1080)
지형 텍스처 업로드 < 1 ms (변경 행만)
자동자 — 프레임당 소비 스텝 전체 < 5 ms (§8.1)
드로우콜 총합 < 40
초기 로딩 < 3 s
번들 (gzip) < 400 KB

8.1 이 예산은 SUBSTEPS 를 지키는 값이 아니라 정하는 값이다

terrain.md §3.4 가 SUBSTEPS 를 시뮬레이션 상수에서 표현 상수로 격하했다. 격자 결과와 체크섬에 영향이 없고, 서버는 아예 쓰지 않으며, 클라이언트마다 값이 달라도 안전하다. 따라서 위 표의 항목은 "SUBSTEPS 를 정해놓고 지켜야 하는 예산"이 아니라 프레임당 소비할 수 있는 스텝 수의 상한이다.

terrain.md §11.2 의 스텝당 실측을 그대로 대입한다.

상황 스텝당 SUBSTEPS = 26 5 ms 로 가능한 최대 스텝
반경 40 (기본 무기) 0.054 ms 1.4 ms 92
반경 80 (최대 무기) 0.143 ms 3.7 ms 35
대형 붕괴 (§11.3) 0.44 ms 11.4 ms ❌ 11

실전 규모에서는 예산이 넉넉하다. SUBSTEPS = 26 이면 최대 무기가 3초에 정착하면서 프레임당 3.7 ms 로 예산 안에 들어간다. 예산을 넘는 것은 terrain.md §11.3 의 대형 붕괴뿐이고, 그 규모는 가장 큰 무기의 10배라 실전에 나오지 않는다 — roadmap.md Phase 0 이 완료 조건에서 그 시나리오를 뺀 이유가 같다.

프레임당 스텝 수를 고정값으로 둘지 예산 기반으로 동적 클램프할지는 §9 미결이다.

저사양 대응: 아래 순서로 끈다. 포스트 그레인 → 대기 먼지 → 블룸 → 입자 샘플링 비율 하향 → 렌더 해상도 0.75배 지형 셰이딩은 절대 끄지 않는다. 그게 이 게임의 정체성이다.


9. 미결 항목

  • 서체 3종 실제 확정 — 브라우저에서 숫자 가독성 실측 후 결정
  • weather() 곡선
  • 원경 능선을 절차적으로 생성할지, 아트로 그릴지
  • 탱크 실루엣 최종 디자인 — Phase 3.5 Canvas 후보는 궤도·차체·반구형 포탑, 팀색 액센트로 구현
  • 관전 카메라 정책 (동시 폭발 중 무엇을 따라갈지)
  • 모바일 지원 여부 — 지형 텍스처 업로드가 모바일 GPU에서 견디는지 미검증
  • 프레임당 자동자 스텝 수를 무엇이 정하는가 (§8.1) — 고정 SUBSTEPS + 공통 압축 곡선 vs 프레임 예산 기반 동적 클램프. terrain.md §3.4 대로 클라마다 달라도 체크섬은 안전하다
  • 폭발 광원 합성 방식 — §3 의 [6][2] 의 법선을 쓰는데 그 법선은 §4.3 대로 [2] 의 프래그먼트 셰이더 안 지역 변수다. [6] 시점에는 존재하지 않는다. 광원을 [2] 의 유니폼으로 넘겨 한 패스로 합칠지, G-buffer 를 만들지 결정이 필요하다
  • uPalette 두 번째 행의 용도 (§4.1) — 재질별 풍화색 5개를 둘지, --crust 하나로 갈지. §4.4 셰이더 식은 지금 1행만 쓴다. terrain.md §10 미결 7 과 함께 정한다
  • 입자 발생원 인터페이스 — Phase 3.5는 자동자의 일시적 moved 버퍼를 1/20 이하로 샘플링한다. 이 버퍼는 체크섬 상태가 아니며 렌더러가 읽기만 한다. 최종 클라이언트 API 이름과 수명은 terrain.md §10 미결 11

9.1 다른 문서의 결정이 이 문서의 서술을 좌우하는 것들

여기서 임의로 확정하지 않는다. 선택지와 근거는 docs/decisions.md.

항목 이 문서의 어디 어디에
uAge 노출 판정 · 셀 이동 시 전파 · 초기값 · 재접속 복원 §4.1, §4.4 decisions.md B8
라운드 경계에서 지형과 uAge 를 누적 유지하는가 §1.1 decisions.md B6
탄도 잔상 유지 턴 수 (game-design.md §5.1 은 직전 1발) §1.5 decisions.md C8
입자 절대 상한과 초과분 처리 §5 decisions.md C8
6인 개인전의 팀색·도형 마커 (지금 4종) §1.3, §7.1 decisions.md C8
SCREE 로 바뀐 ROCK 의 지층색을 유지하는가 §4.4 terrain.md §10 미결 7