여러 에이전트와 Claude 코딩 세션으로 Godot 게임 만들기
요약
본 글은 Godot 엔진을 사용하여 'Spring Rider'라는 탑다운 산악자전거 게임을 개발한 과정을 다룹니다. 특히 Multica 에이전트 팀과 Claude Code 세션을 활용하여 리서치, 아트워크 제작, 메커니즘 구현 등 전 과정에 AI를 적용했음을 설명합니다. 코딩 이전에 레퍼런스 수집 및 방향 설정에 집중하는 것이 중요함을 강조합니다.
핵심 포인트
- AI 에이전트 팀을 구성하여 게임 개발의 리서치와 구조화 작업을 수행했다.
- Claude Code 세션을 통해 실제 스포츠 이벤트 자료를 조사하고 게임 메커니즘으로 구현했다.
- Midjourney로 아트워크를 생성한 후, Claude Code가 Blender에서 3D 모델링 및 애니메이션 코드를 담당했다.
Spring Rider는 Godot 4.5로 제작된 Powder 스타일의 탑다운 다운힐 산악자전거 게임입니다. 끝없는 스프링 숲을 달리거나, 실제 Mégavalanche에서 가져온 빙하부터 계곡까지의 코스를 따라 15명의 라이벌과 경주합니다. 저는 이 코드 중 거의 작성하지 않았습니다. Multica 에이전트 팀과 터미널에서의 Claude Code 세션이 13일 만에 이를 구축했으며, 저의 역할은 리서치, 방향 설정, 그리고 승인 여부를 결정하는 것이었습니다.
본 게시물에서는 우리가 어떻게 작업했는지 다룹니다: 코딩 전에 진행한 리서치, 게임이 사용하는 무료 에셋과 사운드, 제가 에이전트 팀을 구성한 방법, 그리고 에이전트들이 서로 간섭하지 않도록 관리한 방법에 대해 설명합니다.
첫 코드 라인보다 앞선 리서치
게임은 메커니즘이 아닌 비주얼에서 시작했습니다. 저는 스키 게임 Powder의 탑다운 뷰를 원했고, 이것을 스프링 숲에서의 산악자전거로 옮겼습니다. 레포지토리의 첫 커밋에는 이미 docs/references/ 폴더가 있었습니다: Powder 스크린샷, 숲 팔레트, 트레일, 경로, 지형 고도(terrain relief), 그리고 자전거 포즈 시트 두 장이었습니다. 모든 에이전트는 실행 시작 시 이 이미지들을 열어보고, 모든 시각적 변화를 이것들과 비교하여 판단합니다. 하루가 지나자 색상들이 여기저기 흩어졌기 때문에, 저는 아트 바이블과 팔레트 파일 하나를 요청했고, 그 이후로는 어떤 에이전트도 임의로 색상을 만들지 않았습니다.
첫 Multica 에이전트의 첫 임무 역시 리서치였습니다. 저는 이 에이전트에게 레포지토리의 모든 Markdown 파일을 읽고 아무것도 건드리기 전에 깊이 파고들도록 요청했습니다. 그 결과물인 첫 PR은 188개의 Kenney 모델을 크기와 재질 이름과 함께 목록화하고 컨택 시트를 렌더링하여, 이후 작업들이 추측하는 대신 목록에서 에셋을 가져갈 수 있게 했습니다.
경주 역시 같은 패턴을 따랐다. 9월 27일, 나는 Claude Code에게 알프 드 위즈(Alpe d'Huez)의 메가발란슈(Mégavalanche)에 대해 조사해 달라고 요청했다. 이곳은 피크 블랑(Pic Blanc) 빙하에서 계곡까지 이어지는 대규모 출발 경주이다. 이틀 후, 나는 에이전트들에게 선수들이 그 경주에서 어떻게 추락하는지 읽어오게 하고, 그러한 시나리오들을 게임에 추가하도록 했다. 이것이 연구 과제가 되었고, 결과는 반드시 출처를 인용해야 한다는 규칙을 갖게 되면서, 빙하에서의 휩쓸림(washouts), 무리 속의 복잡함(tangles in the pack), 그리고 낙상 후 자전거까지 걸어오는 과정 등 게임에 생동감을 불어넣었다. 내가 경주에 더 많은 선수를 원했을 때, 에이전트들에게 실제 이벤트를 확인하여 적절한 숫자를 찾아보라고 지시했다.
Midjourney가 선수와 동물들을 그렸다
'스프링 라이더(Spring Rider)'의 첫 버전은 2D 스프라이트 게임이었으며, 아트워크는 Midjourney 스프라이트 시트를 통해 얻었다. 청록색, 주황색, 흰색의 로우폴리곤(low-poly) 선수가 그곳에서 시작했으며, 그의 포즈 시트는 여전히 레퍼런스 폴더에 있다. 우리는 스프라이트로서는 긴 그림자와 빛나는 나무를 표현할 수 없었기 때문에 3D로 전환했다. Claude Code는 이전에 얻은 포즈들을 따라 Blender에서 3D 선수와 자전거를 모델링했고, 애니메이션 코드가 손으로 만든 프레임 없이 모든 사지(limb)를 구동하게 했다.
야생 동물들도 같은 방식으로 진행되었다. 나는 Midjourney로 새, 사슴, 여우, 곰의 이미지를 만들고 이를 채팅에
게임 속의 모든 나무, 바위, 절벽, 그루터기, 통나무 더미, 양치식물, 그리고 다리 널빤지는 Kenney Nature Kit에서 가져왔습니다. Kenney는 자신의 에셋 팩을 CC0 라이선스로 배포하여 누구나 출처 표기 없이 상업용 게임에 사용할 수 있게 했습니다. 저희는 어쨌든 저장소와 여기에 그에게 공로를 돌립니다. 감사합니다, Kenney. 혼자 개발하는 사람이라도 당신 같은 팩 없이는 이 정도 규모의 숲을 출시할 수 없었을 겁니다.
이 키트는 또한 2D 버전을 좌초시켰던 문제까지 해결해 주었습니다. 빌드 로그에는 그 교훈이 '충실도보다 응집력이 중요하다(cohesion beats fidelity)'라고 기록되어 있습니다. 사진처럼 사실적인 커팅 아웃은 단순하고 일관된 로우폴리 아트보다 게임에서 더 나쁘게 보였습니다. 수백 개의 모델에 걸쳐 하나의 스타일을 유지한 것이 그 문제를 해결했습니다. 이 게임은 런타임 시 각 Kenney 모델의 재질 이름으로부터 색상을 변경하여, 전체 숲이 참고 자료의 팔레트와 일치하도록 합니다.
모든 사운드는 실제 녹음입니다
에이전트는 노이즈로부터 첫 자전거 소리를 합성했습니다. 저는 그에게 유료 사운드 라이브러리를 확인하게 했고, 그것은 거기서의 배포 권한을 얻으려면 상위 플랜 중 하나와 출처 표기 라인이 필요하다고 보고했습니다. 저는 Claude Code에게 합성된 소리가 좋지 않다고 말했고, 그것은 freesound.org에서 가져온 실제 CC0 녹음으로 대체했습니다: 흙과 자갈 위 타이어 소리, 제동 소리, 빙하를 구르는 눈덩이 아래서 나는 눈 삐걱거리는 소리, 봄 숲의 오후, 시냇물 소리, 피레네 산맥 정상에서의 바람 소리, 그리고 세 마리의 곰을 포효음, 추격 으르렁거림, 그리고 으르렁거림에 사용했습니다. 저장소의 스크립트가 이들을 다운로드하고 자르고, 크레딧 파일은 모든 녹음가를 명시합니다.
저는 추가한 것보다 더 많은 사운드를 제거했습니다. 프리휠 클릭 소리와 체인 딸깍거리는 소리가 사라졌고; 웅덩이 물방울 소리는 항상 터지기 때문에 짧은 '쉬익' 소리로 바뀌었으며, 음악은 다시 시작했다가 끝났습니다. PR 제목 전체는
실제 Mégavalanche의 경로가 레이스를 형성하다
저는 이 레이스가 Mégavalanche처럼 느껴지기를 원했기 때문에, 에이전트들이 실제 코스를 기반으로 그 과정을 만들었습니다. World & Art 에이전트는 실제 라인을 검색하여 Komoot의 공개 투어인 “La Mégavalanche - Allemond depuis Pic Blanc”을 찾았습니다. 이 경로는 빙하에서 3,261m부터 알레몽(Allemond)까지 770m에 이르기까지 총 20.5km이며, 실제 코스 라인과 고도를 포함합니다. 에이전트는 SRTM 30m 지형 데이터로부터 경사도 데이터를 추가하고 이를 25m 간격으로 리샘플링하여 결과물을 저장소의 데이터 파일로 남겼습니다.
파이썬 스크립트가 이 20.5km를 게임의 3.1km 레이스에 적용하여 코스를 생성했습니다. 각 지점에서 언덕이 얼마나 강하게 당기는지, 트레일이 산비탈을 가로질러 지나가는 곳, 페달을 밟아야 하는 알프드위(Alpe d'Huez) 위의 고원 지대, 짧은 오르막길, 그리고 실제 코스가 오즈(Oz)까지 급커브하는 지점의 두 개의 숲속 스위치백 등을 보여줍니다. 스크립트 설정을 수정하고 다시 실행하면 코스를 재생성할 수 있습니다.
이 문제에서 나온 한 세부 사항은 제가 에이전트가 질문하도록 하는 이유를 보여줍니다. 저는 Trailforks 링크를 출처로 보냈습니다. 그런데 에이전트가 그 링크가 알프드위의 것이 아니라 리유니옹(Réunion)의 Mégavalanche에 관한 것임을 알아차리고, 구축하기 전에 질문했습니다. 저는 우리의 산과 그 구역을 유지했고, 그것이 알프드위 코스를 따라 레이스를 형성하도록 했습니다.
에이전트 팀을 구축하는 방법, 그리고 우리가 우리 팀을 구축한 방법
우리는 팀으로 시작하지 않았습니다. 단 하나의 에이전트와 하나의 규칙으로 시작했고, 거기서부터 성장했습니다.
첫 번째로는 범용 에이전트(generalist) 한 개를 만들고 파일을 분할하는 것부터 시작했습니다. 첫날 저녁, 저는 원하는 바를 설명하며 에이전트 빌더에서 Multica 에이전트 하나를 구축했습니다: 문서를 읽고, 깊이 파고들며(deep dive), 풀 리퀘스트(pull requests) 형태로 결과물을 제출하고, 우리가 선택한 아트 디렉션(art direction)을 보호하는 것이었습니다. 그러자 저는 클로드 코드(Claude Code)가 이미 터미널에서 작동하고 있었기 때문에 두 에이전트가 어떻게 하나의 코드베이스를 공유할 수 있는지 빌더에게 물어보았습니다. 답은 파일별 분할(split by file)이었습니다. 터미널에는 game3d.gd, terrain.gd, 그리고 biker_rig.gd가 남아 있었고, 에이전트(저는 이 이름을 "Get shit done"이라고 지었습니다)는 나머지 모든 것을 맡았습니다.
규칙을 각 작업에 넣지 않고 에이전트에 직접 입력해야 합니다. 빌더는 제가 보낸 일곱 개의 채팅 메시지를 약 8,800자 분량의 지침으로 만들었습니다. 이 지침에는 무엇부터 읽어야 하는지, 편집하기 전에 병렬 작업을 어떻게 확인할지, 모든 브랜치에 agent/로 접두사를 붙여야 한다는 것, 절대 머지(merge)하지 말아야 하며 자신이 생성하지 않은 브랜치로는 푸시(push)해서는 안 된다는 것, Godot에서 변경 사항을 검증하는 방법 등이 포함되어 있습니다. 또한 이 지침에는 아트 디렉션이 글로 담겨 있는데: 낮은 따뜻한 태양빛, 평면 음영의 로우 폴리곤 모양, 색상 팔레트, 그리고 거친 가장자리를 가진 땅에 그려진 흔적입니다. 만약 어떤 작업이 시각적인 부분을 변경할 것이라면, 에이전트는 멈추고 질문을 합니다.
공유 작업을 계약(contract)으로 분할했습니다. 레이스 모드의 경우, 터미널 세션은 먼저 작은 PR을 열면서 계약을 체결했습니다: 각 측이 소유하는 파일과 각 측이 서로 호출하는 정확한 함수들입니다. 예를 들면 start_race()와 set_race(time_s, speed_kmh, zone_label) 같은 것입니다. 터미널은 월드와 레이스 흐름을 구축했고; 에이전트는 HUD, 최고 기록, 사운드 및 테스트를 구축했습니다. 그리고 이 두 부분은 하나의 통합 PR로 합쳐졌습니다.
이미 작동하는 에이전트로부터 팀을 확장했습니다. 5일 후에 저는 하나의 이슈를 제기했습니다: "이 게임을 구축할 에이전트 분대(squad)를 만들어 주세요. 분대에 있는 에이전트는 당신이 가진 모든 지식과 기술을 가질 수 있습니다." 그 에이전트는 동일한 지침, 모델, 런타임으로 자신을 네 개 복제했고, 각각에 짧은 역할 섹션을 추가했습니다:
- Lead는 들어오는 작업을 분류하고, 이를 순서가 지정된 하위 이슈로 분할하며, PR(Pull Request)을 검토합니다. 작업 자체가 너무 작아 위임하기 어려울 때를 제외하고는 스스로 코드를 작성하지 않습니다.
- Gameplay는 라이더 물리(rider physics), 조작(controls), 트릭, 경쟁자(rivals), 그리고 HUD 로직을 담당합니다.
- World & Art는 지형(terrain), 트레일, 소품(props), Kenney 에셋, 팔레트, 조명 및 셰이더를 담당하며, 프레임을 렌더링하고 이를 참고 자료와 비교해야 합니다.
- QA & Docs는 테스트 하네스(test harnesses), 성능, 내보내기(exports), 그리고 문서를 담당하며, 다른 멤버들의 PR을 건드리지 않고 검사합니다.
- Get shit done은 작고 여러 분야에 걸친 작업의 일반적인 처리자 역할을 수행합니다.
팀 자체 규칙은 외형이나 범위를 변경할 모든 것에 대해 저에게 먼저 문의하는 것입니다. Godot 관련 지식을 위해, 에이전트는 리포지토리(repo)에 네 가지 오픈 소스 스킬(최적화, 멀티쓰레딩, 디버깅 및 내보내기, from GodotPrompter, MIT)을 추가하여, 모든 멤버가 체크아웃 시 이를 받게 됩니다.
Lead에게 브리프 작성 권한을 맡겼습니다. 저는 채팅 메시지처럼 요청을 작성합니다. 한 번 읽으면 전체 내용은 다음과 같습니다:
GODOT=/Applications/Godot.app/Contents/MacOS/Godot
$GODOT --headless --path . --quit-after 40 # SCRIPT ERROR가 출력되지 않아야 함
$GODOT --headless --path . --script res://tests/autopilot.gd
...
자율 주행 에이전트(autopilot)는 시드된 월드를 따라 이동하며 얼마나 멀리 갔는지, 그리고 무엇에 부딪혔는지 보고합니다. 다른 테스트 스크립트는 회전, 점프, 벽 타기, 경쟁자, 프레임 시간 등을 확인합니다. 시각적인 모든 것의 경우, 에이전트가 스크린샷을 열어 참조 이미지와 비교합니다. 빌드 로그에서 얻은 교훈은 다음과 같습니다: 테스트들이 물리적 퇴행(physics regressions)을 포착했으며, 시각적 버그는 누군가가 프레임을 캡처해서 보고서로 확인했을 때만 나타났습니다.
QA & Docs 에이전트는 초기에 그 자리를 확보했습니다. 이 에이전트는 게임플레이 에이전트(Gameplay agent)가 제출한 낙하(falls) PR에서 라이더들이 빙하를 따라 씻겨 내려가는 현상을 발견했는데, 이는 레이스 규칙상 허용되지 않는 것이었습니다. 또한, 필드를 확장하기 전에 추가 경쟁자 한 명이 프레임당 얼마의 비용이 드는지 측정했고, 이 사이트의 플레이 페이지에 배포될 빌드가 어떤 것인지 결정하는 Chrome에서의 웹 빌드 벤치마킹을 수행했습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
