4일 만에 게임 만들기 (GMTK 잼 참여작)
요약
GMTK jam에 참여하여 'loop' 테마로 가족 구성원 관점의 시간 반복 경험 게임을 제작한 과정을 공유합니다. 초기에는 컴퓨트 셰이더를 이용해 낙서 기능을 구현했고, 점차 아기 캐릭터 애니메이션, 실로폰 모델링, 하노이의 탑 등의 요소를 추가하며 개발 역량을 보여주었습니다.
핵심 포인트
- 컴퓨트 셰이더와 스크립트를 활용한 초기 낙서 기능 구현
- Blender를 이용한 아기 및 고양이 캐릭터 리깅/애니메이션 작업
- 하노이의 탑 등 게임 메커니즘을 추가하며 '시간 루프' 테마 구현 시도
여러분 안녕하세요! 저는 최근 GMTK jam에 참가했는데, 이 잼은 'loop'라는 테마로 4일 동안 게임을 만드는 것이었습니다. 그래서 저는 간단한 1인칭 컨트롤러부터 시작했습니다. 제 아이디어는 플레이어가 가족 구성원들의 관점에서 같은 몇 분의 시간을 반복해서 경험하는 것이었기 때문입니다. 처음에는 아기로서 시작해 보았습니다. 그래서 종이에 레이(ray)를 발사하여 교차점을 찾고, 그 점의 좌표를 종이를 기준으로 계산한 다음, 해당 지점 주변의 특정 반경 내 픽셀을 채우는 작은 compute shader를 실행하는 간단한 낙서 스크립트를 만들었습니다.
이제 우리는 마음껏 낙서할 수 있게 되었는데, 너무 빨리 하면 좀 얼룩지기는 합니다. 그러고 나서 버튼을 누르면 이 낙서 보기로 전환되도록 설정하고, 어떤 크레용을 클릭했는지에 따라 색상을 변경하도록 했습니다. 저는 밤 10시경에 게임 작업을 시작했고, 이미 자정이 넘었기 때문에 그날을 'Day 0'이라고 부르기로 하고 잠자리에 들었습니다. 하지만 다음 날 아침에는 진행 상황의 느낌을 주기 위해 낙서 점수 시스템을 설정할 시간이 되었고, 예전에 만들었던 작은 드로잉 감지기(doodle detector)를 사용해서 플레이어가 실제로 특정 무언가를 그리도록 해보는 아이디어를 가지고 놀았습니다. 하지만 결국 아기의 스타일을 너무 제약하고 싶지는 않았고, 대신 페이지에 최소한의 색이 채워져 있는지 여부와 모든 색상을 적어도 조금씩 사용했는지에 따라 작품을 점수화했습니다.
참고로 이 프로젝트의 코드가 정말 엉망진창이라는 점은 양해 부탁드립니다. 하지만 어쨌든, 이런 방식으로 원하시는 거의 모든 것을 그리고 목표를 달성할 수 있습니다. 예를 들어, 이것은 모나리자입니다. 다음으로, 드디어 블렌더(Blender)에서 아기를 만들었습니다. 실제로 애니메이션을 해본 지 정말 오래되었기 때문에, 제 실력이 조금도 줄지 않았다는 것을 보니 당연히 엄청난 안도감이 들었습니다.
그리고 여기 아기가 있습니다—세상을 돌아다니며 왈츠를 추고 있죠. 분명 이 애니메이션은 나중에 다른 캐릭터들을 다룰 때의 이점을 위한 것이지만, 움직일 때 자신의 그림자를 볼 수 있고, 혹시 아래를 똑바로 내려다본다면 배가 출렁이는 것을 볼 수 있다는 점도 아기에게는 상당히 도움이 된다고 생각합니다. 그러다가 지난번에 방문했을 때 조카들이 실로폰(xylophone)을 두드리며 즐거워하던 것이 생각나서 게임용으로 하나 빠르게 모델링하기로 결정했습니다. 그래서 여기서는 단순히 다른 음표를 클릭하면 작은 효과음이 재생됩니다.
심지어 이것을 위해 특별한 애니메이션까지 만들었는데—안타깝게도 최종 게임에 추가하는 것을 잊었습니다. 저는 여기서 정말 저 자신을 초월했다고 생각해서 슬픕니다. 어쨌든, 아기가 마지막 활동으로 지적인 무언가를 좋아할지도 모른다고 생각했고, 그래서 하노이의 탑(towers of Hanoi)으로 향했습니다. 아마도 알고 계시겠지만, 여기서는 항상 큰 블록을 작은 블록 아래에 놓아 탑의 구조적 안정성을 유지하면서 시작 기둥에서 끝 기둥까지 모든 블록을 옮겨야 합니다.
이 작업들은 현재는 다소 평범해 보이지만, 나중에 시간을 내서 더 흥미롭게 만들 수 있기를 바랄 뿐입니다. 왜냐하면 지금은 그저 다른 캐릭터를 빠르게 추가해서 일종의 ‘시간 루프’가 어떻게 작동할지 알아보기만 원했기 때문입니다. 그래서 poly.pizza에서 귀여운 아기 고양이 한 마리를 찾았는데, 이곳은 제가 상당히 많이 사용하게 된 사이트입니다. 멋진 크리에이티브 커먼즈 자료를 공유해 주신 모든 분들께 정말 감사합니다.
제가 이 고양이에게 추가한 것은 간단한 리깅과 애니메이션이며, 제 역량을 믿으실 수 있는 수준의 품질입니다. 그리고 여기 게임에 임포트되었습니다. 그래서 돌아다닐 수 있고, 아래를 내려다보면 작은 발들이 바스락거리는 것을 볼 수 있습니다. 하지만 약간의 문제가 하나 있는데, 위를 올려다보면 우리 머릿속을 보게 됩니다. 제가 빠르게 고친 방법은 우리가 실제로 위를 보고 있는지 확인하고, 그 경우에만 렌더러(renderer)를 그림자(shadows)로 설정하는 것이었습니다. 그래서 지금 아래를 내려다볼 때는 모든 것이 정상적이지만, 위를 올려다보면 우리 자신의 눈알이 보이는 것을 피하기 위해 메시가 사라지게 됩니다.
하지만 이 짧은 우회 과정 이후에, 저는 플레이어의 위치와 회전(rotation)을 저장하는 키프레임 구조와 애니메이션 매개변수들을 위한 작은 구조체를 만들었고, 그 데이터를 초당 약 30번 기록했습니다. 이것을 재생하기 위해, 제가 빠른 전진(fast-forward)이나 되감기(rewind) 기능이 필요할지, 아니면 전체 시간을 녹화해야 할지에 대한 많은 세부 사항을 아직 확신하지 못했기 때문에, 가장 유연하다고 느낀 방식으로 진행했습니다. 바로 현재 재생 시간(current playback time)을 가져와서 이진 검색(binary search)을 수행하는 것입니다. 따라서 중간 키프레임에서 시작하여 그것이 너무 이르거나 너무 늦은지 확인하고, 남아 있는 탐색 프레임의 양을 절반으로 줄여나갑니다.
그리고 그 절반화 과정을 두 프레임만 남을 때까지 반복합니다. 현재 시간은 그 사이 어딘가에 있어야 하므로, 저는 거기에 저장된 값들을 선형 보간(linear interpolation)합니다. 이를 통해 잠시 돌아다니는 것을 시도할 수 있고, 버튼을 누르면 캐릭터가 재생 모드(playback mode)로 들어가서 우리가 걸었던 발자취를 되짚어 보는 것을 볼 수 있습니다. 어떤 이유에서인지 저는 가장 급한 일이 게임에 메인 메뉴를 추가하는 것이라고 결정했는데—비록 제가 다시 산만해지기 전에 실제로 버튼들이 아무것도 작동하게 만들지는 못했지만 말입니다.
하지만 또한 'Family Time'이라는 이름을 정하고, 이 예전 캐릭터를 다시 재활용하여 가족 속의 십대 역할을 맡겼습니다. 제가 산만했던 것은 작은 거실/주방 놀이 공간을 꾸미기 위해 더 많은 모델을 찾는 것이었습니다. 마우스 좌표(mouse coordinates) 같은 것을 낙서할 때 저장하거나, 실로폰(xylophone)을 연주할 때 음표 인덱스(note indices)를 저장하는 녹화 코드(recoding code)로 돌아와 그것들을 구현하고, 간단한 재생 기능도 추가했습니다.
마침내 저는 플레이 버튼이 작동하도록 만들고, 아기를 초록색으로 바꾸는 작업을 시도했지만—제가 어떤 배경 이야기를 상상했는지 기억나지 않습니다. 어쨌든, 이제 우리는 아기 목표를 달성하기 위해 돌아다닐 수 있고, 장면이 다시 로드되면 고정 카운터(static counter)가 증가하여 이제 고양이가 조종 가능한 캐릭터라는 것을 알려줍니다. 당장 우리만의 목표가 없으므로, 그냥 아기를 따라다니며 무엇을 하는지 보고 듣기만 하면 됩니다.
이 시점에서는 메뉴부터 캐릭터 목표 화면, 그리고 해당 캐릭터로 플레이하는 기본 흐름을 갖추게 되어 상당히 만족스러웠습니다. 게다가 목표가 완료되면 다음 캐릭터로 전환되는 부분까지도요. 궁금하시다면 git 커밋에서 진행 상황을 확인하실 수 있는데, 이것은 1일차의 끝이었습니다. 다음 날 아침에는 집안에 약간의 혼란을 주기로 결정했습니다. 바로 고양이 중 하나가 양초를 넘어뜨려 불을 피우는 큰 야망을 갖게 하는 것이었죠.
저는 이 모든 것을 완전히 물리 기반(physics based)으로 만드는 것에 대해 약간 우려했습니다. 나중에 그 불들을 실제로 끄는 작업이 필요했기 때문에, 만약 불이 TV 뒤 같은 어색한 곳에 생긴다면 그 작업 자체가 불가능해질 수도 있었거든요. 그래서 저는 힘을 가할 수 있는 몇 가지 벡터(vector)를 미리 정의하는 것으로 타협했습니다. 고양이가 양초의 트리거 구체(trigger sphere) 안으로 들어오면, 단순히 이 중 고양이 방향과 가장 잘 맞는 것을 보고 그것을 사용하도록 했습니다.
또한 에디터 내에서 키를 누르면 각 가능성을 빠르게 테스트할 수 있도록 만들었습니다. 모든 것이 제대로 작동하는지 확인하기 위해서였죠. 방화 외에도, 고양이가 파리를 사냥하는 것을 좋아할 수도 있을 것 같아 제가 오래된 베지에 곡선(bezier) 도구를 가져와 이 파리들이 따라갈 경로를 정의했고, 관련 경로 함수를 호출하고, 아래로 날 때 속도를 약간 증가시키며—어느 정도 중력 부스트 같은 느낌으로—날개도 사인파(sin wave)로 퍼덕이게 하는 간단한 컨트롤러 스크립트도 작성했습니다.
이제는 끝없이 돌아다니며 그것을 잡으려고 애쓰거나—이것은 매우 까다롭죠—아니면 기다렸다가 지나갈 때 완벽하게 덮치는 것이 더 좋은 전략일 수도 있습니다. 그런 건 아니고, 감만 잡아주세요… 물론 고양이들은 악명 높은 낮잠 자는 동물들이기도 해서, 그것을 위한 과제를 만들었고 졸고 있다는 것을 나타내는 몇 개의 z 메쉬를 생성하기 위해 파티클 시스템을 추가했습니다. 적절한 애니메이션은 당연히 아직 빠져있습니다. 하지만 저는 실제로 플레이 가능한 캐릭터 세트를 완성하고 싶었기 때문에 어머니의 모델을 찾아서 blender에서 약간 수정했고, 게임에 가져왔습니다.
어머니는 소화기까지 가야 할 텐데, 그건 냉장고 위에 있는 누구에게도 손이 닿지 않는 곳에 잘 숨겨져 있습니다. 거기에 도달하려면 일종의 점프 퍼즐이 있을 것 같지만, 아직 그것을 위한 레벨 디자인은 구상해야 합니다. 마지막으로 아버지가 있는데, 그는 이 혼란을 작은 홈 무비 몽타주로 기록하거나, 시간이 상당히 촉박해지기 시작했으니 아마도 스틸컷 몇 장을 찍는 역할을 끝에서 할 것입니다.
사실 저는 가족 물고기가 될 만한 플레이 가능한 캐릭터가 하나 더 있었는데, 거기서 예전의 boids 프로젝트를 사용해서 다른 물고기 친구들이 뛰어노는 모습을 만들 수 있었고, 여러분은 헤엄쳐 지나갈 작은 장애물 코스가 있거나 이전 캐릭터 중 한 명이 떨어뜨린 먹이를 잡아야 했을 수도 있습니다. 하지만 제가 가진 모든 아이디어를 계속 추가하는 것보다는 범위가 더 좁지만 더 완성도 높은 것을 만드는 편이 나을 것 같아서요. 그래서 다시 아버지로 돌아와서, 이 스크린샷을 캡처 함수로 사용해서 화면을 잡아 몇 초 동안 미리 보기로 표시했습니다.
하지만 제대로 작동하게 만드는 데 어려움을 겪었습니다. 거꾸로 되어 있고 중앙에서 벗어났으며, 한 지점에서 이미지의 절반이 누락되어 있었습니다. 그래서 대신 여기에 렌더 텍스처 배열을 할당하고, 카메라용 별도의 카메라를 두기로 결정했습니다. 이 카메라는 클릭할 때 수동으로 트리거하여 그 텍스처 중 하나에 렌더링할 수 있습니다. 테스트해 보니, 이 버전은 바로 작동하더군요. 그래서 좋았습니다. 실제로 이 작은 집을 거닐며 사물을 사진 찍는 것이 놀라울 정도로 재미있습니다.
어쩌면 현실 세계에서 사진 촬영을 취미로 삼아야 할지도 모르겠습니다. 어떻게 생각하세요… 제가 잠재력이 있을까요? 그런데 이것은 게임을 위해 급하게 만든 승리 화면입니다. 아무튼, 다시 본론으로 돌아가서, 언급했던 작은 점프 퍼즐을 만들었습니다. 약간 열린 문 위로 뛰어올라야 하고, 그 다음 병들 위를 지나, 마지막으로 토스터를 통해 냉장고까지 올라가야 합니다. 또한 소화기를 집어 들고 구체들을 발사할 수 있게 했는데, 이것은 제가 만든 거품 효과에 대한 멋진 시도입니다. 물론 여기에도 같은 종류의 녹화 기능을 설정했지만, 이번에는 모든 입자의 위치 대신 입력 타이밍을 캡처하고 재생하도록 했습니다. 왜냐하면 그게 다소 과한 것 같았기 때문입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 YouTube Sebastian Lague (절차적 생성)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기