포스트

위에서 찍은 그림에서 진짜 3D로

SRT 브리칭의 그림을 미리 찍어 둔 2D 그림에서 three.js 실시간 3D 장면으로 바꾼 과정. 판정은 2D 그대로 두고, 걷는 다리·총 방향·던지기 동작·섬광탄을 맞추고, 타이틀 화면을 새로 만들고, 공유 링크에서만 모델이 사라지던 문제를 고쳤다.

위에서 찍은 그림에서 진짜 3D로

한 줄 요약: 위에서 찍어 둔 그림을 돌려 그리던 캐릭터를, 게임 속 3D 모델을 그대로 움직이는 실시간 3D 장면으로 바꿨습니다. 게임 판정은 하나도 안 바꾸고 그림만 바꿨습니다.

개인적으로 즐기려고 만든 팬 게임입니다. 게임 그림과 소리가 들어 있어 공개 배포는 하지 않습니다.

그림 대신 장면을 세운다

2편에서는 블루 아카이브의 3D 모델을 바로 위에서 찍어 128픽셀 그림으로 만들고, 게임에서는 그 그림을 보는 방향으로 돌려 그렸습니다. 빠르고 단순했지만 한계가 있었습니다.

  • 동작이 초당 12장으로 고정입니다. 걷는 빠르기가 바뀌어도 다리는 같은 빠르기로 움직입니다.
  • 그림자가 없고, 벽과 바닥이 납작한 색 칸입니다.
  • 총을 쏘면서 걷는 것처럼 동작 두 개를 섞을 수 없습니다.

그래서 Door Kickers 2처럼 장면 자체를 3D로 세웠습니다. 원칙은 하나였습니다. 판정은 2D 그대로, 그림만 3D. 누가 누구를 보는지, 총알이 어디에 맞는지는 지금까지처럼 2D 좌표로 계산하고, 3D는 그 결과를 보여 주기만 합니다.

3D로 바꾼 뒤의 게임 화면 처음 3D로 띄운 블랙마켓 창고. 미야코와 사키가 게임 속 모델 그대로 서 있고, 시야 밖은 안개로 어둡습니다.

카메라는 바로 위에서 내려다보는 원근 카메라입니다. 높이를 잘 정하면 바닥이 2D 화면과 정확히 같은 크기로 보입니다. 세로 시야각 30°, 화면 높이 800픽셀이면 카메라를 1493픽셀 높이에 둡니다. 덕분에 마우스 클릭, 화면 위 표시, 디버그 그림은 하나도 고치지 않았습니다.

더블클릭으로 열리게

이 게임은 처음부터 index.html을 더블클릭해서 여는 게임입니다. 서버 없이 파일로 열면 브라우저가 막는 것이 많습니다.

  • 최신 three.js는 모듈로만 읽힙니다. 파일로 열면 모듈을 못 읽어서, 일반 <script>로 읽히는 마지막 판인 r147을 씁니다.
  • 크롬은 파일로 연 그림을 3D 텍스처로 못 쓰게 막습니다. 그래서 모델과 그림을 전부 글자(data URI)로 바꿔 .js 파일 안에 묶었습니다. 미야코 한 명이 5.3MB입니다.

장면에 들어간 것

  • 바닥, 벽, 창문, 그리고 문짝·자물쇠·C2·부서진 조각으로 나눈 문
  • 엄폐물 자리에 놓는 상자, 책상, 캐비닛, 차 모델
  • 해가 드리우는 그림자 (화면 밖 벽의 그림자도 들어오게 화면보다 160픽셀 넓게)
  • 분대가 본 곳만 밝고 나머지는 어두운 안개
  • 그림 색은 그대로 두고 해 반대쪽 면만 한 단계 어둡게 칠하는 만화 재질

총구 화염, 연기, 섬광탄은 바닥과 나란한 얇은 판으로 그립니다.

다리가 미끄러지지 않게

3D로 바꾸자마자 눈에 띈 것은 발이 바닥에서 미끄러지는 것이었습니다. 몸은 빠르게 가는데 다리는 천천히 걷거나, 그 반대였습니다.

그래서 걷기 동작에서 땅을 디딘 발이 골반에 대해 뒤로 가는 빠르기를 쟀습니다. 미야코 65px/s, 사키 64px/s였습니다. 이것이 “이 동작을 1배로 틀면 몸이 가야 하는 빠르기”입니다. 이제 걷기 동작은 실제 몸 빠르기 ÷ 64 배로 틉니다. 너무 느리거나 빠르면 어색해서 0.3~2배 사이로 자릅니다.

이런 어긋남은 눈으로 매번 찾기 어려워서 그래픽 시험을 만들었습니다. 실제 게임을 미션마다 띄워 길을 그려 걷게 하고, 벽에 몸이 파고든 거리, 서로 파고든 거리, 움직이는데 서 있는 동작이 나온 시간, 뒷걸음질한 시간 등을 픽셀과 초로 잽니다.

총은 보는 쪽을 향한다

사키의 총이 시야와 다른 쪽을 향한다는 걸 발견했습니다. 동작마다 총의 방향을 몸 앞쪽 기준으로 재 보니 이랬습니다.

 대기걷기사격
사키128°141~147°0°
미야코8°11~17°1°
미유−22°5~15°−1°

블루 아카이브에서 사키는 쉴 때 기관총을 옆이나 뒤로 듭니다. 몸은 앞을 보는데 총만 옆을 보는 셈입니다. 탑다운 화면에서는 “사키가 저쪽을 겨누고 있다”가 한눈에 보여야 하므로, 분대원은 대기·걷기 중에도 윗몸(가슴, 팔, 머리, 총)만 사격 자세로 덮고 다리는 원래 동작대로 움직이게 했습니다. 고친 뒤 세 명 모두 총과 시야 방향 차이가 −1~1°입니다.

같은 이유로 분대 시야도 좁혔습니다. 조준경을 보면서 112°를 넓게 볼 수는 없으니, 60°로 줄이고 몸 주변을 보는 원도 없앴습니다. 옆이나 뒤의 적은 총소리나 맞은 쪽으로 돌아봐야 찾습니다.

없는 동작은 만든다

게임에 필요한 동작이 모델에 다 있지는 않았습니다.

  • 사키의 C2 설치: 사키 모델에는 무릎 꿇는 동작이 없어서, 미야코의 무릎 꿇고 재장전하는 동작을 빌려 사키 뼈대에 맞춰 옮겼습니다. 빌린 동작은 총을 다른 모델 기준으로 잡아서, 설치하는 동안에는 총을 가슴에 붙여 몸에서 떨어지지 않게 했습니다.
  • 수류탄 던지기: 던지는 동작이 아무에게도 없었습니다. Door Kickers 2 영상에서 포인트맨이 던지는 0.7초를 보고, 손신호 동작의 팔 올린 자세(0.65초)와 앞으로 뻗은 자세(1.1초)를 이어 1.0초짜리 동작을 만들었습니다. 새로 만드는 동작은 한 번에 최소 30프레임이라는 규칙을 정해 두어서 31프레임입니다.
  • 잠긴 문 차기: 몸은 문 옆 벽 앞에 두고 다리만 문 앞으로 뻗게 했습니다. 문 앞에 드러나는 몸을 가장 작게 하라는 교리 그대로입니다.

미야코의 섬광탄 던지기 위: 게임 화면에서 본 던지기. 아래: 옆에서 본 같은 동작.

진짜 섬광탄

던진 섬광탄도 동그라미 대신 모델을 씁니다. 스즈미가 EX 스킬로 던지는 섬광탄의 메시를 게임에서 꺼냈습니다. 게임 화면에서 4.0 × 3.2 × 7.7픽셀이라 너무 작아서 1.5배로 키웠고, 바닥에 처음 닿을 때까지 28픽셀 높이의 포물선을 그리며 날아간 뒤 굴러갑니다.

터지는 모습도 다시 만들었습니다. 섬광탄은 스즈미 섬광의 빛줄기와 불꽃을, 9연속 섬광탄은 사키 EX 수류탄의 폭발 텍스처를 씁니다.

섬광탄과 9연속 섬광탄 위: 섬광탄 0.02~0.65초. 아래: 9연속 섬광탄 0.05~4.50초.

이 무렵 지휘차에서 C2 폭파와 포격을 지원하던 모에는 뺐습니다. C2는 이제 분대가 직접 들고 다니고, 사키가 설치합니다.

타이틀 화면

타이틀 화면도 바꿨습니다. 미야코 그림 한 장을 대표 그림으로 정하고, Call of Duty: Modern Warfare 2 타이틀처럼 위는 밝은 안개 하늘, 아래로 갈수록 어두워지게 깔았습니다. 안개는 천천히 흐르고 먼지 조각이 사선으로 날립니다.

SRT 브리칭 타이틀 화면

그림은 흰 바탕이라 그대로 얹으면 사각형이 보입니다. 그래서 바탕을 오려 냈습니다.

  1. 그림 가장자리에서 이어진 아주 밝은 픽셀(세 채널 모두 246 이상)을 바탕으로 봅니다.
  2. 후광 안쪽이나 머리카락 사이처럼 그림에 둘러싸인 흰 틈도, 40픽셀 이상 넓고 250 이상 밝으면 바탕으로 봅니다.
  3. 바탕과 닿은 3픽셀 띠는 밝을수록 투명하게 하고, 거기 묻은 흰색을 빼서 테두리가 하얗게 뜨지 않게 합니다.

그림의 75.7%가 투명해졌습니다.

공유 링크에서만 사라진 모델

3D 판을 공유 링크에 올리고 나서 “수류탄이 옛날 거”라는 말을 들었습니다. 내 컴퓨터에서는 멀쩡한데, 링크에서는 섬광탄도 캐릭터도 2편의 2D 그림으로 나왔습니다.

원인은 모델을 읽는 방법이었습니다. three.js의 모델 로더는 모델 안에 글자로 묶어 둔 바이너리와 그림을 fetch라는 기능으로 읽습니다. 그런데 공유 링크 페이지는 보안 때문에 fetch를 막습니다. 모델 15개가 전부 못 읽혔고, 게임은 모델이 없으면 그림으로 대신 그리도록 만들어 두었기 때문에 오류 없이 조용히 옛 모습으로 돌아갔습니다.

fetch를 막은 시험 페이지를 만들어 재 보니 읽기가 53번 막혔습니다. 바이너리는 직접 글자를 풀어 바이트로 바꾸고, 그림은 <img> 태그로 읽게 바꿨습니다. 같은 시험에서 막힌 읽기는 0번, 모델은 15개 모두 읽혔습니다.

“못 읽으면 그림으로 대신 그린다”는 원래 안전장치였는데, 이번에는 고장을 숨기는 쪽으로 작동했습니다. 내 컴퓨터에서 되는 것과 공유한 곳에서 되는 것은 다를 수 있다는 것도 다시 배웠습니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.