게임 키친, 광기의 돌을 만들기 위한 3가지 기술적 도전

/ THE GAME KITCHENGuest Blog
Mar 6, 2025|11 분
광기 의 돌의 주요 아트 - 더 게임 키친 | 유니티로 제작됨
이 웹페이지는 이해를 돕기 위해 기계 번역으로 제공됩니다. 기계 번역으로 제공되는 콘텐츠에 대한 정확도나 신뢰도는 보장되지 않습니다. 번역된 콘텐츠의 정확도에 관해 의문이 있는 경우 웹페이지의 공식 영어 원문을 참고해 주시기 바랍니다.

올해 초, The Game Kitchen은 다섯 명의 수감자가 심문형 감옥에서 탈출하도록 돕는 전술 RPG인 The Stone of Madness를 출시했습니다. 이 게스트 포스트에서 스튜디오의 세 명의 개발자가 개발 중 렌더링, UI 및 테스트 문제를 어떻게 해결했는지 공유합니다.

우리는 더 게임 키친입니다. 최근에 PC와 콘솔에서 미친 돌을 출시했습니다. 우리는 최신 프로젝트 개발 중에 직면한 가장 시급한 문제 중 일부를 공유하고자 하며, 이를 기술적 관점에서 실용적인 예를 들어 접근하고자 합니다. 이 협업 기사에서, 우리의 프로그래밍 팀은 성능과 개발 효율성을 최적화하기 위해 Unity에서 구현한 주요 솔루션을 분석합니다.

먼저, 아드리안 데 라 토레(그래픽 프로그래머)가 게임의 독특한 시각적 스타일을 달성하기 위해 아트 파이프라인을 어떻게 설계하고 렌더링했는지 설명할 것입니다.

다음으로, 알베르토 마르틴(UI 프로그래머)이 사용자 피드백을 기반으로 UX 개선을 통해 UI 개발을 간소화하기 위해 Noesis를 어떻게 활용했는지 자세히 설명할 것입니다.

마지막으로, 라울 마르톤(게임플레이 프로그래머)은 서버에서 복잡한 인게임 액션에 대한 테스트를 어떻게 외부화하고 자동화했는지 보여줄 것입니다. 이를 통해 여러 모서리 케이스가 통합을 방해하지 않고 처리되도록 보장했습니다.

미친 짓을 멋지게 보이게 만들기 사용자 정의 렌더 파이프라인 살펴보기

아드리안 드 라 토레, 그래픽 프로그래머, 더 게임 키친
광기의 돌은 2D 비주얼과 3D 게임 플레이 메커니즘을 결합하여 독특한 기술적 도전을 제공합니다. 플레이어는 2D 세계를 보지만, 게임의 기본 시스템은 3차원 공간에서 작동하여 디자인에 독특한 이중성을 만들어냅니다.

이 문제를 해결하기 위해, 우리 개발 팀은 3D 게임 정보와 2D 시각적 표현 사이의 간극을 효과적으로 연결하는 맞춤형 렌더링 파이프라인을 만들었습니다. 이 솔루션은 시각적 일관성을 유지하면서 의도된 게임 플레이 깊이를 보존하기 위해 여러 렌더링 패스와 전문 기술을 구현하여 3D 요소를 게임의 독특한 2D 아트 스타일로 원활하게 변환할 수 있도록 합니다.

광기의 돌에서 프레임 렌더링에 기여하는 두 가지 주요 시나리오가 있습니다.

첫 번째 시나리오는 프록시 시나리오라고 부르며, 최종 프레임의 조명을 계산하는 기하학적 원시로 구성됩니다.

The Stone of Madness의 기본 3D 장면 보기 - Made With Unity
기저 3D 장면 보기 (프록시 시나리오)

두 번째 시나리오는 프록시 기하학의 형태와 위치에 맞는 스프라이트로 구성된 캔버스 시나리오입니다. 캔버스는 3D 공간을 시뮬레이션하고 움직이는 게임 요소와 함께 적절한 Z-정렬을 달성하기 위해 레이어로 배열됩니다.

다음 섹션에서는 프레임 렌더링을 위한 그래픽 파이프라인의 각 단계를 자세히 설명합니다.

1. 시야의 원뿔

시야 또는 게임 능력이 활성화될 때마다 파이프라인의 첫 번째 단계를 시작합니다. 우리는 NPC의 시점(PoV)에 카메라를 배치하여 그 시야(FoV) 내의 프록시 깊이를 렌더링합니다.

NPC의 시점에 위치한 카메라에 의해 생성된 깊이 텍스처
NPC의 시점에 위치한 카메라에 의해 생성된 깊이 텍스처

그런 다음 다른 렌더 텍스처에서 카메라는 플레이어의 원점에서의 거리의 그라디언트를 B 채널에 출력하며, 이는 스킬 영역 효과에 사용됩니다.

플레이어의 시점에 위치한 카메라에 의해 생성된 그래디언트 텍스처
플레이어의 시점에 위치한 카메라에 의해 생성된 그래디언트 텍스처

NPC의 시점 렌더 텍스처를 사용하여, 시야 카메라는 장애물 및 거리 정보를 포함하여 이전 텍스처 위에 R 및 G 채널에서 원뿔을 렌더링합니다.

그라디언트 텍스처 위에 겹쳐진 시야의 원뿔
그라디언트 텍스처 위에 겹쳐진 시야의 원뿔

최종 패스는 알파 채널에서 음파를 렌더링합니다.

그라디언트 텍스처의 알파 채널에 겹쳐진 음파
그라디언트 텍스처의 알파 채널에 겹쳐진 음파

이 단계에서 생성된 최종 텍스처로, 캔버스 카메라 단계에서 장면의 스프라이트를 렌더링하는 데 사용됩니다.

기술의 범위와 시각 원뿔을 렌더링하는 데 사용되는 최종 텍스처
기술의 범위와 시각 원뿔을 렌더링하는 데 사용되는 최종 텍스처

2. 캔버스 렌더 ID 카메라

우리 프로젝트의 각 프록시는 관련된 렌더 ID(부동 소수점 값)를 가지고 있습니다. 프록시와 관련된 스프라이트는 동일한 렌더 ID를 공유합니다. 이 단계에서는 Render ID float 값을 렌더 텍스처로 렌더링합니다.

ID 텍스처를 렌더링합니다. 각 색상은 프록시와 해당 스프라이트 간에 공유되는 고유 렌더 ID를 나타냅니다.
ID 텍스처를 렌더링합니다. 각 색조는 프록시와 해당 스프라이트 간에 공유되는 고유한 렌더 ID를 나타냅니다.

다음 단계에서는 이 텍스처를 사용하여 프록시 시나리오에서 계산된 조명 정보를 캔버스 시나리오의 스프라이트와 일치시킵니다.

3. 조명

우리 게임의 조명은 다음으로 구성됩니다:

  • 구운 조명 영구적으로 활성화된 자연광, 예를 들어 외부 조명
  • 혼합 조명 장면에서 켜고 끌 수 있는 정적 조명, 예를 들어 촛불
  • 실시간 조명 장면을 가로지르며 켜고 끌 수 있는 빛(우리는 이것을 단 한 번의 인스턴스인 알프레도의 기름 램프에 구현했습니다)

RenderID 텍스처를 사용하여 프록시 장면의 조명 정보를 포함하는 렌더 텍스처를 생성합니다.

Render ID 텍스처와 조명 계산에서 생성된 그림자 텍스처
Render ID 텍스처와 조명 계산에서 생성된 그림자 텍스처

4. 캔버스 카메라

모든 렌더 텍스처를 생성한 후, 카메라는 조명, 스킬 효과 범위, 시야 원뿔, 그리고 소음 파동에 대한 정보를 사용하여 스프라이트를 렌더링하기 시작합니다.

5. 후처리

색 보정, 비네팅 및 기타 효과는 후처리 과정에서 적용됩니다.

6. UI

마침내 UI가 오버레이되었습니다.

HUD의 광기 UI 프로세스 가속화

알베르토 마르틴, UI 프로그래머, 더 게임 키친

최종 출시 버전인 광기의 돌은 50개 이상의 유저 인터페이스를 특징으로 합니다. 그 숫자의 이유는 이 게임이 유저에게 보여줄 데이터가 많기 때문입니다. 우리의 UI 작업은 매우 시간이 많이 소요되었고, 특히 초반에 팀이 너무 작았기 때문에 우리는 가능한 한 적은 시간 안에 좋은 결과를 얻기 위해 지속적으로 프로세스를 최적화하고 있었습니다.

우리의 UI 작업은 전체 프로젝트에 걸쳐 있었기 때문에, 우리의 UI/UX 디자이너들이 우리가 구현해야 할 모든 기능을 명확하게 이해하는 것이 중요했습니다. 우리 게임이 좋은 유저 경험을 제공하고 재미있게 플레이할 수 있도록, 우리는 프로그래밍 팀과 디자인 팀 간의 열린 소통을 유지하는 데 주의했습니다.

모든 UI 구성 요소의 최상의 버전을 만들기 위해, 우리는 기술 팀과 창의적/연구 팀 간의 장벽을 제거해야 했습니다. 그래서 모든 사람이 게임 개발에 적극적으로 참여할 수 있었습니다. 이 두 부분으로 구성된 작업 흐름에 접근한 방법은 다음과 같습니다.

UI 디자인에서 연구 및 창의성의 역할

우리의 UI/UX 디자이너는 UI 요소가 최종 게임에서 어떻게 보일지를 정의하고, 만족스러운 사용자 경험을 제공하도록 보장하는 책임이 있습니다. 이 점을 염두에 두고, 그들은 각 요소를 최소한의 기술적 부담으로 만들고 잠재 사용자와 함께 검증하는 것부터 시작했습니다. 그 과정은 다음과 같았습니다:

  1. 필수 조건: 플레이어의 요구를 이해하고 게임의 요구 사항 및 유저 목표 목록을 작성하기
  2. 조사 유사한 문제를 어떻게 처리했는지 다른 게임들을 살펴보는 중입니다.
  3. 와이어프레임 도면과 구조 작업 중 (현재 최종 아트 없음)
  4. 모형 이 시점에서 우리는 이전에 생성된 요소(버튼, 스크롤, 프레임 등)로 거의 완전히 설계된 인터페이스를 장착하여 많은 노력 없이 반복할 수 있습니다.
  5. 프로토타입 우리는 모형을 사용하여 Figma에서 프로토타입을 구축하고, 게임패드와 키보드/마우스와의 상호작용을 시뮬레이션하여 실제 환경에서 어떻게 작동할지를 보여줍니다.
  6. 유저 테스트 이전에 만든 프로토타입을 사용하여 사용자 테스트를 시작하고, 1단계에서 확인한 요구 사항과 목표를 검증합니다.
  7. 반복 단계: 사용자 테스트가 기대에 부합하면 기술 부문 프로세스로 전달되어 더 많은 반복을 하거나 편리하다면 추가 테스트를 수행합니다.

기술 UI 구현

앞서 언급했듯이, "광기의 돌"의 UI 요소 수는 방대합니다. UI 엔진 개발은 비용이 많이 들기 때문에, 우리는 배우기 쉽고 괜찮은 도구와 워크플로우를 갖춘 프레임워크를 사용해야 했습니다. 미들웨어 범위를 평가한 후, 우리는 모델-뷰-뷰모델(MVVM) 패턴을 따르는 Noesis GUI를 선택했습니다.

우리는 Noesis를 선택했습니다. 왜냐하면 Noesis는 WPF(Windows Presentation Framework)를 기반으로 하고 있으며, MVVM 모델을 따르기 때문에 대부분의 문서, 참고 문헌, 포럼 항목 등을 재사용하여 대부분의 문제를 해결할 수 있기 때문입니다. 이 프레임워크는 한동안 존재해 왔습니다. 첫 출시 이후 18년이 지났으며, 많은 UI 개발자들에게 익숙합니다. 이는 우리 스튜디오가 프로젝트를 위한 인터페이스와 도구를 구현하기 위해 비교적 더 큰 인재 풀에서 채용할 수 있는 옵션을 제공합니다. 노에시스에 대한 또 다른 중요한 점은 WPF에서 동일한 도구를 사용할 수 있다는 것입니다.

XAML을 사용하여, 우리의 UI 크리에이티브 팀은 레이아웃 작업에 참여하고 모든 요소를 최소한의 기술적 개입으로 다듬었습니다. MVVM 접근 방식 덕분에, 우리의 기술 UI 프로그래머들은 기능에 집중할 수 있었고 필요할 때 창의적인 팀을 특정 분야에서 지원할 수 있었습니다.

테스트(또는, 시스템 디자인으로 게임을 만들면서 미치지 않는 방법)

라울 마르톤, 게임플레이 프로그래머, 테쿠 스튜디오

광기의 돌에서의 게임 플레이는 세 가지 기본 기둥을 기반으로 합니다: 플레이어 기술, NPC AI 및 장면 상호작용. 이 세 가지 시스템은 근본적으로 얽혀 있어 플레이어가 제어해야 하는 상황의 수가 기하급수적으로 증가하며, 우리가 테스트해야 하는 시나리오의 수도 증가합니다.

우리가 프로젝트를 시작하자마자 전통적인 QA 시스템이 부족할 것이라는 것을 깨달았습니다. 서로 특정한 방식으로 상호작용하는 여러 요소에 의존하는 시나리오가 너무 많아 통제되지 않는 상황이 발생했습니다. 게다가 이러한 상황은 QA 팀이 편안하게 테스트하기에는 너무 짧은 시간의 창에서 발생할 수 있습니다.

이 문제를 해결하기 위해 우리는 자동 테스트 모음을 만들었습니다. 우리 개발 팀이 특정 시스템과 관련하여 발생할 수 있는 모든 가능한 시나리오/상황을 고려하고, 이를 시뮬레이션된 게임 환경에서 훨씬 더 효율적으로 자동으로 테스트할 수 있다는 아이디어였습니다.

예를 들어, "광기의 돌"의 주요 캐릭터 중 하나인 아멜리아 엑스포지토는 소매치기 능력을 가지고 있습니다. 이 기술을 구현하는 동안, 우리는 다음을 보장하기 위해 일련의 테스트를 시작했습니다:

  • 기술의 기본 작동은 올바른 것이었습니다. NPC에게서 훔칠 때, 소매치기 미니게임이 열리고 게임은 끝날 때까지 일시 중지됩니다.
  • 덜 일반적인 상황도 다루어집니다: NPC가 당신을 지켜보고 있는 동안 다른 NPC(예: 경비원)에게서 훔치려고 하거나 NPC가 달리고 있다면, 그 행동은 불가능합니다.
자동화된 플레이 테스트를 통해 소매치기 기술이 다양한 인게임 시나리오에서 예상대로 작동하는지 확인합니다.
자동화된 플레이 테스트를 통해 소매치기 기술이 다양한 인게임 시나리오에서 예상대로 작동하는지 확인합니다.

통합 테스트 생성

우리가 만든 각 통합 테스트는 다음 요구 사항을 기반으로 설정이 필요했습니다:

1. 이 특정 상황을 만들기 위해 특별히 준비된 장면

소매치기 기술을 테스트하기 위해 두 명의 경비원과 한 명의 플레이어가 있는 장면을 만들었습니다. 우리는 각 캐릭터를 상황을 정확하게 테스트하기 위해 필요한 방향을 향하도록 배치했습니다 (기억하세요, 플레이어는 경비원의 시야 안에 있을 경우 소매치기를 사용할 수 없습니다).

추가적으로, 장면은 시나리오를 테스트하는 데 필요한 최소한의 구성 요소만 포함해야 하며, 불필요한 요소는 측정에 잡음을 추가할 수 있습니다. 이것이 우리의 예제 장면에 HUD, 수동 입력 시스템, 음향 효과 등이 없는 이유입니다.

  • 이 단계에서는 게임 구조가 잘 구분되어 있어야 하며, 이는 약간의 노력이 필요할 수 있지만, 일단 달성되면 그만한 가치가 있습니다! 😉

2. 테스트할 상황을 강제로 만들 수 있는 테스트 코드

우리가 테스트해야 했던 많은 상황은 수동으로 만들기 어렵고 시간이 많이 소요되며, 시작하기 위해 코드 푸시가 필요합니다.

예를 들어, NPC가 움직이지 않는 한 결코 덫에 발을 디디지 않도록 테스트 시나리오를 만들고 싶다면, 지침의 연쇄는 다음과 같습니다:

  1. 장면을 시작하다
  2. 잠시만 기다려 주세요
  3. NPC 아래에 쥐덫을 설치하세요.
  4. 잠깐만 기다려
  5. NPC에게 어떤 방향으로든 걷기 시작하라고 명령하세요.

이 프로젝트의 이 부분은 개발 중에 발생하는 모든 변화에 매우 민감합니다(게임 사양 변경 및 다양한 예상치 못한 시나리오와 같은 요인에 의존). 따라서 테스트 코드와 결과 피드백이 가능한 한 명확해야 합니다.

명확한 정보 없이 실패하는 시험보다 더 나쁜 것은 없습니다. 실제로 무엇이 잘못되고 있는지.

3. 시나리오가 의도한 대로 작동하고 있는지, 아니면 테스트가 논리 오류를 감지했는지 확인하는 신뢰할 수 있는 방법

자동화된 테스트는 여전히 감독이 필요합니다. 테스트되는 항목에 대한 더 높은 특이성을 가진 테스트의 수가 증가하면 모니터링하기 어려워질 수 있으며, 시나리오가 통계적으로 유의미할 만큼 충분히 테스트되지 않을 수 있습니다. 이러한 문제를 해결하기 위해 우리는 맞춤형 도구를 만들었습니다.

예를 들어, 우리의 일부 테스트는 장면에서 여러 NPC 간의 결합된 상호작용을 포함했습니다. 이러한 사례를 적절히 모니터링하기 위해, 우리는 테스트 중 NPC가 순환하는 다양한 AI 상태를 기록하는 시스템을 만들었습니다.

자동화 테스트 중 경비원 NPC가 예상된 추적 순서를 따르지 못합니다.
자동화 테스트 중 경비원 NPC가 예상된 추적 순서를 따르지 못합니다.

우리는 또한 현재 게임 상태에 대한 가시성을 제공하는 좋은 API가 필요했습니다 (NPC가 기절했나요?) NPC가 라우팅 상태에 들어갔습니까? 몇 번? 어떤 플레이어 캐릭터가 포로로 잡혔나요? 그리고 계속.

4. 모든 이러한 테스트를 신속하게 실행할 수 있는 시스템:

단위 테스트와 달리, 자동화된 테스트는 게임이 실시간으로 실행되는 상태에서 수행되어야 합니다. 이로 인해 이러한 테스트를 실행하는 데 매우 느려질 수 있습니다.

이러한 상황에서 우리는 우리의 게임이 Unity의 표준 업데이트 시스템을 사용하지 않는다는 사실을 활용할 수 있습니다. 대신, 우리의 모든 구성 요소는 Tick() 함수를 사용하여 Unity 업데이트를 시뮬레이션하지만, 우리의 게임 엔진에 의해 제어된 방식으로 시작됩니다.

이것은 우리의 테스트로 여러 가지 목표를 달성하는 데 도움이 되었습니다:

  • 먼저, 게임의 각 프레임마다 여러 코드 프레임을 실행하는 강제 기능으로 그들의 실행 속도를 높일 수 있습니다.
  • 둘째, 이러한 테스트가 실시간으로 수행되기 때문에, 테스트 시나리오를 실행하는 컴퓨터의 프레임 속도로 인한 변동에 매우 민감합니다. 제어된 프레임 속도로 변환함으로써, 우리는 이러한 변동을 피할 수 있습니다: 한 기계에서 테스트가 통과하면 모든 기계에서 통과하고, 그 반대도 마찬가지입니다.

그리고 이것이 결과가 될 것입니다.

안전한 테스트가 우리가 깨진 빌드를 피하는 데 어떻게 도움이 되는가

이 테스트 스위트를 생성하면서, 우리는 또한 버그가 포함된 경우 브랜치의 병합을 자동으로 중단하는 안전 장치를 구현해야 했습니다. 이를 보장하기 위해, 우리는 메인 프로젝트 브랜치에 변경 사항이 커밋될 때마다 실행되는 자동 병합 스크립트를 생성합니다.

이 스크립트는 모든 테스트를 실행하고 그 결과를 모니터링하도록 합니다. 테스트가 실패하면 오류 감지가 발생하고 병합이 중단됩니다.

이 시스템을 통해, 겉보기에는 고립된 시스템의 변화가 상호작용하는 다른 메커니즘을 망치는 상황을 피할 수 있습니다.

이 스톤 오브 매드니스 개발의 비하인드 스토리를 공유해 주신 더 게임 키친에 감사드립니다. Steam Curator 페이지에서 더 많은 Made With Unity 게임을 탐색하고 Unity의 리소스 페이지에서 더 많은 개발자 통찰력을 얻으세요.