ROGUEFORGE / 개발 일지

업데이트 한 번에
114메가를 받게 하고 있었다

2026.09.15 · 9월 14~15일 작업 기록

밤에 모닥불이 주변을 밝히는 게임 화면

새 버전을 올릴 때마다 게임이 알아서 받아서 갈아 끼웁니다. 편해 보이지만 속을 열어 보면 매번 zip 하나를 통째로 받고 있었습니다. 114메가입니다.

글자 하나 고친 날도 114메가, 아무것도 안 바뀐 파일도 114메가.

바뀐 것만 받게 하는 작업입니다.

바뀐 파일만 받게 하기

실제로 배포했던 두 버전을 풀어서 파일 단위로 비교했습니다. 232개 중 153개가 한 바이트도 안 바뀌어 있더군요. 됐다 싶었습니다.

그런데 안 바뀐 153개를 다 합쳐도 93메가고, 바뀐 75개가 289메가입니다. 설치본 383메가의 대부분이 딱 두 파일에 들어 있었습니다. 게임의 모든 그림과 소리가 담긴 통짜 파일 두 개요. 스프라이트 한 장만 바꿔도 그 안의 바이트가 밀려서 파일 전체가 달라집니다.

파일 단위로는 답이 없다는 뜻입니다.

그럼 파일 안을 잘게 쪼개서 같은 조각은 건너뛰면 되겠다 싶었습니다. 64킬로바이트씩 끊어서 재 봤습니다. 188메가짜리 파일에서 재사용할 수 있는 게 29메가, 나머지 159메가는 다시 받아야 했습니다. 84%입니다.

이유는 단순합니다. 데이터가 밀렸기 때문입니다. 앞쪽에 뭐가 하나 끼어들면 그 뒤로 전부 한 칸씩 밀리고, 칸에 맞춰 자른 조각은 하나도 안 맞습니다.

세 번째 방법

결국 제대로 된 바이너리 차분을 써야 했습니다. 옛 파일을 사전 삼아 새 파일을 압축하는 방식입니다.

188메가짜리가 12메가가 됐습니다. 만드는 데 0.6초 걸렸고요.

전부 합치니 114메가가 15.3메가가 됐습니다. 그것도 세 버전을 건너뛴 경우라, 한 버전씩 올라오면 더 작아집니다.

대신 압축을 푸는 도구를 게임에 같이 넣어야 했습니다. 순수 C#으로 해 보려고 위의 블록 방식을 먼저 재 본 거였는데, 84%라는 숫자를 보고 포기했습니다. 1.5메가짜리 실행 파일 하나와 100메가를 바꾼 셈입니다.

재 보기 전에는 "바뀐 파일만 받으면 되지"라고 확신하고 있었습니다. 그대로 만들었으면 하루를 들여 아무것도 못 줄일 뻔했습니다.

연기를 지면에 붙이기

모닥불 연기가 불에서 한참 위에 떠 있었습니다. 불은 여기서 끝나는데 연기는 저 위에서 시작합니다.

3D 파티클이라 그런가 싶어 회전값을 만져 봤는데 아니었습니다.

가져다 쓴 이펙트가 용이 불을 뿜는 효과였습니다. 앞으로 쭉 뿜는 거니까 불꽃·광원·연기가 뿜는 방향을 따라 일렬로 놓여 있고, 연기는 제일 끝인 2.37만큼 앞에 있었습니다. 그걸 세워서 모닥불로 쓰니 2.37이 그대로 높이가 된 겁니다.

회전을 줄이면 연기는 내려오는데 이번엔 불이 화면 안쪽으로 눕습니다. 각도 문제가 아니라 간격 문제였습니다.

연기만 따로 떼서 높이를 지정했습니다. 그랬더니 연기가 불을 통째로 덮어 버렸는데, 세워 놓으면 자식의 위아래 값이 앞뒤가 된다는 걸 그때 알았습니다. 연기가 불 앞으로 나와 있던 거죠.

상태가 보이지 않으면 규칙을 배울 수 없습니다

평소·젖음·빙결·감전 상태의 캐릭터를 나란히 비교한 게임 화면
왼쪽부터 평소 · 젖음 · 빙결 · 감전.

젤다의 전설을 하면서 제일 좋았던 건 큰 장치가 아니라 사소한 것들이었습니다. 비 오는 날 절벽이 미끄럽고, 금속 무기를 들고 벌판에 서 있으면 벼락이 떨어지고, 젖은 풀에는 불이 안 붙는 것. 누가 설명해 주지 않는데 해 보면 알게 되는 것들요.

그 감각을 넣고 싶어서 재료마다 물성을 정해 뒀습니다. 나무·금속·돌·유기물·얼음 다섯 가지입니다. 인화성과는 다른 축입니다. 인화성이 "얼마나 타는가"라면 물성은 "무엇인가"입니다. 회벽은 인화성 0.15인 돌이고 통나무는 인화성 1.0인 나무입니다.

그래서 비를 맞으면 8초 동안 젖어 있고, 젖으면 추위를 1.6배로 느끼는 대신 불이 안 붙습니다. 젖은 채로 전기를 맞으면 1.6배로 아프고, 금속 무기를 들고 있으면 거기에 1.3배가 더 붙고, 손에서 무기를 놓칩니다. 얼면 3초 못 움직이고 그 상태에서 맞으면 1.5배로 산산조각이 납니다. 불에 닿으면 바로 풀리고요.

다 만들어 놓고 한참 뒤에 알았습니다. 화면에 아무것도 안 나오고 있었습니다.

젖음도 빙결도 감전도 시뮬레이션 안에서만 돌고 있었습니다. 코드를 뒤져 보니 그 값을 읽는 렌더 코드가 한 줄도 없더군요. 젖어서 추위를 1.6배로 느끼고 있어도 화면은 평소와 똑같았습니다.

이러면 반응을 아무리 정교하게 짜도 소용이 없습니다. 젖었는지도 모르는데 왜 얼었는지 알 도리가 없으니까요. 해 보고 알게 되는 재미는 인과가 보일 때만 생깁니다.

색을 섞는 방식의 한계

제일 간단한 방법부터 했습니다. 젖으면 푸르스름하게, 얼면 하늘색으로, 감전되면 노랗게. 셰이더에서 색을 섞는 거죠.

세 번 조정했는데 계속 이상했습니다. 젖음은 아무리 세게 넣어도 안 보이고, 빙결은 조금만 올려도 캐릭터가 통째로 하얘져서 누구인지 알 수 없었습니다.

재 보니 젖음은 어둡게 곱한 값을 윤기로 다시 더하면서 서로 상쇄되고 있었습니다. RGB로 +0.7, +2.7, +6.1. 어둡게 하려던 게 오히려 밝아진 겁니다.

근본 문제는 따로 있었습니다. 이 게임 아트가 원래 채도가 높아서, 무슨 색을 섞든 상태가 읽히는 게 아니라 그냥 바래 보입니다. 생각해 보니 젤다도 젖은 링크를 색으로 표현하지 않습니다. 물방울이 떨어지죠.

그래서 얹기로 했습니다

색 섞기는 아주 옅게만 남기고, 상태를 알리는 일은 얹는 요소에 맡겼습니다. 젖으면 물방울이 떨어지고, 얼면 발밑에 얼음 결정이 생기고, 감전되면 스파크가 튑니다.

위 사진이 그 결과입니다. 왼쪽 평소 모습과 비교하면 이제 무슨 상태인지 보입니다.

만드는 동안 좌표계를 조심해야 합니다. 아이소메트릭은 화면의 위아래와 월드의 앞뒤가 섞여 있어서, 3D 감각으로 좌표를 더하면 엉뚱한 곳으로 갑니다. 대표적인 경우가 캐릭터 위치라고 넘긴 값이 화면 좌표가 아니라 월드 좌표였습니다. 둘 다 타입이 같아서 컴파일은 멀쩡히 됩니다. 물방울이 캐릭터에서 대각선으로 4유닛 떨어진 곳에 생기고 있었습니다.

디버그 로그 한 줄 찍고 나서야 알았습니다. 조각 위치와 캐릭터 위치를 나란히 출력하니 바로 보이더군요. 추측으로 세 번 고칠 시간에 한 번 재는 게 빨랐습니다.

다음은 같이 하는 것

화면 분할 협동, 최대 4명 온라인, 그리고 모드 지원. 설계를 정리하고 첫 단계를 시작했습니다.

셋이 따로인 줄 알았는데 파고 보니 뿌리가 하나였습니다. 코드 전체가 "이 컴퓨터를 쓰는 사람은 한 명"이라고 가정하고 있었거든요. 그 가정을 슬롯으로 바꾸면, 화면 분할은 슬롯 둘, 온라인은 슬롯 중 일부가 멀리 있는 것이 됩니다.

재미있는 건 멀티플레이 코드가 이미 절반쯤 있었다는 점입니다. 주석에 "멀티 게스트는 이 값으로 걷는다"라고 적혀 있기까지 했고요. 막혀 있던 건 방향을 실어 보낼 칸 하나뿐이었습니다.

화면은 처음부터 나뉘어 있을 예정입니다. 가까이 있으면 합쳐졌다가 멀어지면 갈라지는 방식도 생각해 봤는데, 협동하는 내내 화면 경계가 생겼다 사라졌다 하면 계속 신경 쓰일 것 같아서요. 그쪽이 만들기는 더 쉬운데, 쉬운 게 좋은 건 아니니까요.