스크립터블 오브젝트로 더 나은 씬 워크플로 달성하기

AMEL NEGRA / UNITY TECHNOLOGIESContributor
Jul 1, 2020|9 분
스크립터블 오브젝트로 더 나은 씬 워크플로 달성하기
이 웹페이지는 이해를 돕기 위해 기계 번역으로 제공됩니다. 기계 번역으로 제공되는 콘텐츠에 대한 정확도나 신뢰도는 보장되지 않습니다. 번역된 콘텐츠의 정확도에 관해 의문이 있는 경우 웹페이지의 공식 영어 원문을 참고해 주시기 바랍니다.

Unity에서 여러 씬을 관리하는 것은 어려운 작업이며, 이 워크플로를 개선하는 것은 게임의 성능과 팀의 생산성 모두에 중요합니다. 여기에서는 대규모 프로젝트에 맞게 확장할 수 있는 방식으로 씬 워크플로를 설정하는 몇 가지 팁을 공유합니다.

대부분의 게임에는 여러 레벨이 포함되며, 레벨에는 하나 이상의 씬이 포함되는 경우가 많습니다. 씬의 크기가 상대적으로 작은 게임에서는 프리팹을 사용하여 씬을 여러 섹션으로 나눌 수 있습니다. 하지만 게임 중에 활성화하거나 인스턴스화하려면 이러한 모든 프리팹을 참조해야 합니다. 즉, 게임이 커지고 이러한 레퍼런스가 메모리 공간을 더 많이 차지할수록 씬을 사용하는 것이 더 효율적이 됩니다.

레벨을 하나 또는 여러 개의 Unity 씬으로 세분화할 수 있습니다. 이 모든 것을 관리할 수 있는 최적의 방법을 찾는 것이 핵심입니다. 에디터에서 여러 씬을 열고 런타임에 멀티 씬 편집을 사용하여 여러 씬을 열 수 있습니다. 또한 레벨을 여러 개의 씬으로 분할하면 Git, SVN, Unity 콜라보레이트 등과 같은 협업 툴에서 병합 충돌을 방지할 수 있어 팀워크가 더 쉬워진다는 장점이 있습니다.

여러 씬을 관리하여 레벨 구성하기

아래 동영상에서는 게임 로직과 레벨의 여러 부분을 여러 개의 개별 Unity 씬으로 분할하여 레벨을 보다 효율적으로 로드하는 방법을 보여줍니다. 그런 다음 이러한 씬을 로드할 때 애디티브 씬 로딩 모드를 사용하여 게임 로직과 함께 필요한 부분을 로드하고 언로드하며, 이는 지속적입니다. 프리팹은 씬의 '앵커' 역할을 하는데, 모든 씬이 레벨의 일부를 나타내며 개별적으로 편집할 수 있기 때문에 팀 작업 시 유연성이 뛰어납니다.

편집 모드에 있는 동안에도 이러한 씬을 로드하고 언제든지 플레이를 누르면 레벨 디자인을 만들 때 모두 함께 시각화할 수 있습니다.

이러한 씬을 로드하는 두 가지 방법을 보여드립니다. 첫 번째는 거리 기반이며, 오픈 월드와 같이 내부가 아닌 레벨에 적합합니다. 이 기술은 로딩 및 언로딩 과정을 숨기기 위한 일부 시각 효과(예: 안개)에도 유용합니다.

두 번째 기법은 트리거를 사용하여 로드할 씬을 확인하므로 인테리어 작업 시 더 효율적입니다.

이제 모든 것이 레벨 내부에서 관리되므로 레벨 위에 레이어를 추가하여 레벨을 더 잘 관리할 수 있습니다.

스크립터블 오브젝트를 사용하여 게임 내 여러 레벨 관리하기

각 레벨의 다양한 장면은 물론 게임플레이가 진행되는 동안 모든 레벨을 추적하고 싶습니다. 이를 수행하는 한 가지 가능한 방법은 모노비헤이비어 스크립트에서 정적 변수와 싱글톤 패턴을 사용하는 것이지만, 이 솔루션에는 몇 가지 문제가 있습니다. 싱글톤 패턴을 사용하면 시스템 간에 견고한 연결이 가능하므로 엄밀히 말해 모듈식이 아닙니다. 시스템은 분리되어 존재할 수 없으며 항상 서로 의존하게 됩니다.

또 다른 문제는 정적 변수의 사용과 관련이 있습니다. 인스펙터에서 볼 수 없기 때문에 코드를 변경하여 설정해야 하므로 아티스트나 레벨 디자이너가 게임을 쉽게 테스트하기 어렵습니다. 서로 다른 씬 간에 데이터를 공유해야 하는 경우 DontDestroyOnLoad와 결합된 정적 변수를 사용하지만, 후자는 가능하면 피해야 합니다.

다양한 씬에 대한 정보를 저장하려면 주로 데이터를 저장하는 데 사용되는 직렬화 가능한 클래스인 스크립터블 오브젝트를 사용할 수 있습니다. 게임 오브젝트에 첨부된 컴포넌트로 사용되는 모노비헤이비어 스크립트와 달리 스크립터블 오브젝트는 게임 오브젝트에 첨부되지 않으므로 전체 프로젝트의 여러 씬 간에 공유할 수 있습니다.

이 구조를 레벨뿐만 아니라 게임 내 메뉴 장면에도 사용할 수 있기를 원합니다. 이렇게 하려면 레벨과 메뉴 간의 다양한 공통 프로퍼티를 포함하는 GameScene 클래스를 생성합니다.

알 수 없는 블록 유형 "codeBlock"인 경우 `serializers.types` 프롭에서 해당 블록에 대한 직렬화기를 지정하세요.

이 클래스는 MonoBehaviour가 아닌 ScriptableObject에서 상속된다는 점에 유의하세요. 게임에 필요한 만큼의 프로퍼티를 추가할 수 있습니다. 이 단계가 끝나면 방금 만든 GameScene 클래스를 상속하는 Level 및 Menu 클래스를 만들 수 있으며, 이 클래스도 스크립터블 오브젝트입니다.

알 수 없는 블록 유형 "codeBlock"인 경우 `serializers.types` 프롭에서 해당 블록에 대한 직렬화기를 지정하세요.

상단에 CreateAssetMenu 어트리뷰트를 추가하면 Unity의 에셋 메뉴에서 새 레벨을 생성할 수 있습니다. 메뉴 클래스에 대해서도 동일한 작업을 수행할 수 있습니다. 인스펙터에서 메뉴 유형을 선택할 수 있도록 열거형을 포함할 수도 있습니다.

알 수 없는 블록 유형 "codeBlock"인 경우 `serializers.types` 프롭에서 해당 블록에 대한 직렬화기를 지정하세요.


이제 레벨과 메뉴를 만들 수 있으므로 쉽게 참조할 수 있도록 레벨과 메뉴를 나열하는 데이터베이스를 추가해 보겠습니다. 인덱스를 추가하여 플레이어의 현재 레벨을 추적할 수도 있습니다. 그런 다음 새 게임을 로드하고(이 경우 첫 번째 레벨이 로드됨), 현재 레벨을 재생하고, 다음 레벨로 이동하는 메서드를 추가할 수 있습니다. 이 세 가지 메서드 간에는 인덱스만 변경되므로 인덱스로 레벨을 로드하는 메서드를 생성하여 여러 번 사용할 수 있습니다.

알 수 없는 블록 유형 "codeBlock"인 경우 `serializers.types` 프롭에서 해당 블록에 대한 직렬화기를 지정하세요.


메뉴에 대한 메서드도 있으며, 이전에 만든 열거 형을 사용하여 원하는 특정 메뉴를 로드할 수 있습니다. 열거 형의 순서와 메뉴 목록의 순서가 동일한지 확인하기만 하면 됩니다.

이제 프로젝트 창에서 우클릭하여 에셋 메뉴에서 레벨, 메뉴 또는 데이터베이스 스크립터블 오브젝트를 생성할 수 있습니다.

이미지

거기에서 필요한 레벨과 메뉴를 계속 추가하고 설정을 조정한 다음 장면 데이터베이스에 추가하기만 하면 됩니다. 아래 예는 Level1, MainMenu 및 Scenes 데이터가 어떻게 보이는지 보여줍니다.

이미지

이제 그 메서드를 호출할 차례입니다. 이 예제에서 플레이어가 레벨의 끝에 도달하면 표시되는 사용자 인터페이스(UI)의 다음 레벨 버튼은 다음 레벨 메서드를 호출합니다. 메서드를 버튼에 연결하려면 버튼 컴포넌트의 On Click 이벤트의 더하기 버튼을 클릭하여 새 이벤트를 추가한 다음, 아래와 같이 Scenes Data ScriptableObject를 개체 필드에 끌어다 놓고 ScenesData에서 NextLevel 메서드를 선택합니다.

이미지

이제 레벨을 재생하거나 메인 메뉴로 이동하는 등 다른 버튼에 대해서도 동일한 과정을 거칠 수 있습니다. 다른 스크립트에서 스크립터블 오브젝트를 참조하여 배경 음악용 오디오 클립이나 포스트 프로세싱 프로파일과 같은 다양한 프로퍼티에 액세스하고 레벨에서 사용할 수도 있습니다.

프로세스 오류 방지를 위한 팁
  • 로딩/언로딩 최소화

비디오에 표시된 ScenePartLoader 스크립트를 보면 플레이어가 계속해서 콜라이더에 여러 번 들어오고 나가면서 씬의 반복적인 로딩과 언로딩을 트리거할 수 있음을 알 수 있습니다. 이를 방지하려면 스크립트에서 씬의 로딩 및 언로딩 메서드를 호출하기 전에 코루틴을 추가하고 플레이어가 트리거를 벗어나면 코루틴을 중지할 수 있습니다.

  • 명명 규칙

또 다른 일반적인 팁은 프로젝트에서 견고한 이름 지정 규칙을 사용하는 것입니다. 팀은 스크립트, 씬, 머티리얼 등 프로젝트의 다양한 유형의 에셋에 이름을 지정하는 방법에 대해 미리 합의해야 합니다. 이렇게 하면 본인뿐만 아니라 팀원들도 프로젝트를 더 쉽게 작업하고 유지 관리할 수 있습니다. 이는 항상 좋은 생각이지만, 이 특별한 경우에는 스크립터블 오브젝트를 사용한 씬 관리가 중요합니다. 이 예제에서는 씬 이름을 기반으로 하는 간단한 접근 방식을 사용했지만 씬 이름에 덜 의존하는 다양한 솔루션이 있습니다. 특정 컨텍스트에서 Unity 씬의 이름을 변경하면 게임의 다른 부분에서는 해당 씬이 로드되지 않으므로 문자열 기반 접근 방식은 피해야 합니다.

  • 맞춤형 툴링

게임 전체에서 이름 종속성을 피하는 한 가지 방법은 씬을 오브젝트 유형으로 참조하도록 스크립트를 설정하는 것입니다. 이렇게 하면 인스펙터에서 씬 에셋을 끌어다 놓은 다음 스크립트에서 안전하게 이름을 가져올 수 있습니다. 하지만 에디터 클래스이므로 런타임에 AssetDatabase 클래스에 액세스할 수 없으므로 에디터에서 작동하고 인적 오류를 방지하며 런타임에도 작동하는 솔루션을 위해서는 두 가지 데이터를 결합해야 합니다. 직렬화 시 씬 에셋에서 문자열 경로를 추출하여 런타임에 사용할 수 있도록 저장할 수 있는 오브젝트를 구현하는 방법에 대한 예제는 ISerializationCallbackReceiver 인터페이스를 참조하세요.

또한 커스텀 인스펙터를 생성하면 해당 메뉴를 통해 수동으로 씬을 추가하고 동기화 상태를 유지해야 하는 대신 버튼을 사용하여 빌드 설정에 씬을 빠르게 추가할 수 있습니다.

이러한 유형의 툴의 예로 개발자 JohannesMP가 구현한 훌륭한 오픈 소스 구현을 확인해 보세요(공식 Unity 리소스는 아님).

의견을 공유해 주세요

이 게시물은 스크립터블 오브젝트가 프리팹과 결합된 여러 씬으로 작업할 때 워크플로를 향상시킬 수 있는 한 가지 방법을 보여줍니다. 게임마다 씬을 관리하는 방식이 크게 다르기 때문에 모든 게임 구조에 적합한 단일 솔루션은 없습니다. 프로젝트의 조직에 맞게 사용자 지정 도구를 구현하는 것은 매우 합리적입니다.

이 정보가 프로젝트에 도움이 되거나 자신만의 씬 관리 도구를 만드는 데 영감을 줄 수 있기를 바랍니다.

궁금한 점이 있으면 댓글로 알려주세요. 게임 내 씬을 관리하는 데 어떤 방법을 사용하시는지 알려주세요. 향후 블로그 게시물에서 다루었으면 하는 다른 사용 사례도 자유롭게 제안해 주세요.