
이 페이지는 게임 코드에서 데이터와 로직을 분리하는 데이터 컨테이너로서 ScriptableObjects를 사용하는 방법을 설명합니다.
이것은 Unity 개발자를 돕기 위해 만들어진 여섯 개의 미니 가이드 시리즈 중 두 번째입니다. 이 가이드는 전자책 ScriptableObjects로 Unity에서 모듈형 게임 아키텍처 만들기.에 수반되는 데모를 설명합니다.
이 데모는 고전적인 공과 패들 아케이드 게임 메커니즘에서 영감을 받았으며, ScriptableObjects가 테스트 가능하고 확장 가능하며 디자이너 친화적인 구성 요소를 만드는 데 어떻게 도움이 되는지를 보여줍니다.
전자책, 데모 프로젝트 및 이러한 미니 가이드는 Unity 프로젝트에서 ScriptableObject 클래스를 사용하여 프로그래밍 디자인 패턴을 사용하는 모범 사례를 제공합니다. 이 팁은 코드를 단순화하고, 메모리 사용량을 줄이며, 코드 재사용성을 촉진하는 데 도움이 될 수 있습니다.
이 시리즈에는 다음 기사가 포함됩니다:
ScriptableObject 데모 프로젝트와 이 미니 가이드 시리즈에 들어가기 전에, 디자인 패턴의 핵심은 단지 아이디어라는 것을 기억하세요. 모든 상황에 적용되지 않을 것입니다. 이 기술들은 Unity와 ScriptableObjects를 다루는 새로운 방법을 배우는 데 도움이 될 수 있습니다.
각 패턴은 장단점이 있습니다. 특정 프로젝트에 의미 있게 도움이 되는 것만 선택하세요. 디자이너들이 Unity Editor에 많이 의존하나요? ScriptableObject 기반 패턴은 그들이 개발자와 협력하는 데 좋은 선택이 될 수 있습니다.
궁극적으로, 최고의 코드 아키텍처는 당신의 프로젝트와 팀에 맞는 것입니다.

소프트웨어 개발자들은 종종 모듈성에 관심이 있습니다 - 애플리케이션을 더 작고 독립적인 단위로 나누는 것입니다. 각 모듈은 애플리케이션 기능의 특정 측면에 대한 책임을 집니다.
Unity에서 ScriptableObjects는 데이터와 로직의 분리에 도움을 줄 수 있습니다.
ScriptableObjects는 데이터를 저장하는 데 뛰어나며, 특히 정적일 때 그렇습니다. 이것은 게임 통계, 아이템 또는 NPC의 구성 값, 캐릭터 대화 등 다양한 용도로 이상적입니다.
게임 플레이 데이터를 행동 로직과 분리하면 프로젝트의 각 독립적인 부분을 테스트하고 유지 관리하기가 더 쉬워질 수 있습니다. 이 "관심사의 분리"는 필요한 변경을 할 때 의도하지 않은 원치 않는 부작용을 줄일 수 있습니다.

스크립터블 오브젝트 워크플로우에 대한 복습이 필요하다면, 이 Unity Learn 기사가 도움이 될 수 있습니다. 그렇지 않다면, 간단한 설명은 다음과 같습니다:
스크립터블 오브젝트 정의하기: 하나를 만들기 위해, 저장하려는 데이터에 대한 필드와 속성을 가진 스크립터블 오브젝트 기본 클래스를 상속하는 C# 클래스를 정의합니다. 스크립터블 오브젝트는 모노비헤이비어에 사용할 수 있는 동일한 데이터 유형을 저장할 수 있어, 다재다능한 데이터 컨테이너입니다. 에디터에서 생성 자산 메뉴 속성를 추가하여 프로젝트에서 자산을 쉽게 생성할 수 있습니다.
자산 생성하기: 스크립터블 오브젝트 클래스를 정의한 후, 프로젝트에서 해당 스크립터블 오브젝트의 인스턴스를 생성할 수 있습니다. 이는 디스크에 저장된 자산으로 나타나며, 다양한 게임 오브젝트와 장면에서 재사용할 수 있습니다.
값 설정하기: 자산을 생성한 후, 인스펙터에서 필드와 속성의 값을 설정하여 데이터를 채웁니다.
자산 사용하기: 자산이 데이터를 보유하게 되면, 변수나 필드에서 참조합니다. 스크립터블 오브젝트 자산에 대한 모든 변경 사항은 전체 프로젝트에 반영됩니다.
스크립터블 오브젝트를 게임의 다양한 부분에서 데이터 컨테이너로 재사용할 수 있습니다. 예를 들어, 무기나 캐릭터의 속성을 스크립터블 오브젝트 내에서 정의한 다음, 프로젝트의 어디에서든 해당 자산을 참조할 수 있습니다.
참고: 런타임 중에 인스턴스 생성 메서드를 통해 스크립터블 오브젝트를 생성할 수도 있습니다. 그러나 데이터 저장을 위해, 일반적으로 생성 자산 메뉴 속성를 사용하여 미리 스크립터블 오브젝트 자산을 생성합니다.

ScriptableObjects가 MonoBehaviours보다 데이터 저장에 더 적합한 이유를 더 잘 이해하기 위해 각자의 빈 버전을 비교해 보세요. 자산 직렬화를 모드로 설정했는지 확인하세요: 프로젝트 설정에서 Force Text를 설정하여 YAML 마크업을 텍스트로 볼 수 있습니다.
다른 모든 것이 비어 있는 MonoBehaviour로 새 GameObject를 만드세요. 그런 다음 빈 ScriptableObject 자산과 비교해 보세요. 나란히 배치하면 위 이미지에 표시된 비교와 같아야 합니다.
ScriptableObjects는 MonoBehaviours에 비해 가벼우며, Transform 컴포넌트와 같은 후속 오버헤드를 지니지 않습니다. 이로 인해 ScriptableObjects는 더 작은 메모리 풋프린트를 가지며 데이터 저장에 더 최적화됩니다.

ScriptableObjects는 자산으로 저장되므로 재생 모드 외부에서도 지속되며, 이는 유용할 수 있습니다. 예를 들어, ScriptableObject 데이터는 새로운 장면을 로드하더라도 어디서나 사용할 수 있습니다.
패턴 데모 예제는 직접 테스트할 수 있는 기본 크레딧 화면을 특징으로 합니다. Credits_Data ScriptableObject를 수정한 후 업데이트를 눌러 저장된 텍스트가 나타나는 것을 지켜보세요.
광범위한 대화가 있는 RPG나 미리 작성된 스크립트가 있는 튜토리얼 장면이 있다면, 이는 많은 데이터를 저장하는 일반적인 방법입니다.
ScriptableObject 내의 데이터는 수정 시 즉시 업데이트되지만, 우리의 프로젝트는 화면을 수동으로 새로 고치기 위해 업데이트 버튼이 필요합니다. UI Toolkit 기반 화면은 한 번만 생성되며 데이터가 변경되었을 때 알림을 받아야 합니다.
업데이트를 자동으로 동기화하려면 ScriptableObject 내에 이벤트를 생성하세요. 예를 들어, 이 ExampleSO 스크립트는 OnValueChanged 이벤트를 ExampleValue가 변경될 때마다 호출합니다. 아래 코드 예제를 참조하세요.
그런 다음, 청취 UI 객체가 OnValueChanged에 구독하고 그에 따라 업데이트되도록 하세요.

스크립터블 오브젝트는 많은 객체가 동일한 데이터를 공유할 때 빛을 발합니다. 예를 들어, 여러 유닛이 동일한 공격 속도와 최대 체력을 가진 전략 게임을 만들고 있다면, 각 게임 오브젝트에 이러한 값을 개별적으로 저장하는 것은 비효율적입니다.
대신, 공유 데이터를 중앙 집중식으로 통합하고 각 객체가 해당 공유 위치를 참조하도록 할 수 있습니다. 소프트웨어 설계에서 이것은 플라이웨이트 패턴으로 알려진 최적화입니다. 이렇게 코드를 재구성하면 많은 값을 복사하는 것을 피하고 메모리 사용량을 줄일 수 있습니다.
패들볼SO에서 GameDataSO 스크립터블 오브젝트는 공유 데이터 저장소 역할을 합니다.
일반 설정(속도, 질량, 물리적 반발력 등)의 별도 복사본을 유지하는 대신, 패들 및 볼 스크립트는 가능한 경우 동일한 GameDataSO 인스턴스를 참조합니다. 각 게임 요소는 위치 및 입력 이벤트와 같은 고유 데이터를 유지하지만, 가능한 경우 공유 데이터로 기본 설정됩니다.
메모리 절약이 두세 개의 객체로는 눈에 띄지 않을 수 있지만, 공유 데이터를 편집하는 것은 각 객체를 수동으로 편집하는 것보다 빠르고 오류가 발생할 가능성이 적습니다.
예를 들어, 패들 속도를 수정해야 하는 경우, 단일 위치에서 조정하면 모든 장면에서 두 개의 패들이 모두 업데이트됩니다. 만약 MonoBehaviours에 고유 필드로 저장했다면, 하나의 잘못된 클릭으로 두 값이 쉽게 동기화되지 않을 수 있습니다.
데이터를 스크립터블 오브젝트로 오프로드하면 버전 관리에 도움이 되고 팀원이 동일한 장면이나 프리팹에서 작업할 때 병합 충돌을 방지할 수 있습니다.

GameDataSO는 스크립터블 오브젝트를 데이터 컨테이너로 사용하는 방법을 보여줍니다. 패들볼SO에서, 이는 게임 플레이를 구성하는 다양한 설정을 포함합니다:
이 설정과 데이터가 중앙 위치에 모두 모여 있으면 GameDataSO는 어떤 객체든 이 공유 데이터에 접근할 수 있게 합니다. 이것은 이러한 객체를 관리하는 방식을 단순화하고 프로젝트 전반에 걸쳐 더 큰 일관성을 촉진합니다. 패들 물리학 변경? 여기에서 한 번의 변경으로 여러 스크립트를 조정하는 대신 변경하세요.

가끔은, 당신은 케이크를 가지고 먹을 수도 있습니다. 이중 직렬화를 사용하면 ScriptableObject에 데이터를 저장하면서 동시에 다른 형식으로 유지할 수 있습니다.
LevelLayoutSO 스크립트는 이 개념을 보여줍니다. 패들과 공의 시작 위치를 보유하는 것 외에도, 벽과 목표에 대한 변환 데이터를 사용자 정의 구조체에 저장합니다.
이 값들은 ExportToJson 메서드를 통해 디스크에 기록될 수 있습니다. JSON 파일은 사람이 읽을 수 있는 텍스트로, Unity 외부에서 간단한 수정을 가능하게 합니다. 이것은 에디터에서 ScriptableObjects와 작업한 다음 JSON 또는 XML 파일과 같은 다른 위치에 데이터를 저장할 수 있게 합니다.
JSON 및 XML과 같은 파일 형식은 에디터에서 작업하기 어려울 수 있지만, 텍스트 편집기에서 Unity 외부에서 수정하기는 쉽습니다. 이것은 사용자 정의 또는 유저 모드 레벨의 가능성을 열어줍니다.
게임 설정 스크립트는 LevelLayout ScriptableObject 또는 외부 JSON 파일을 사용하여 게임 레벨을 생성할 수 있습니다.
사용자 정의 모드 레벨을 로드하기 위해, 설정 스크립트는 CreateInstance로 런타임에 ScriptableObject를 생성합니다. 그런 다음, JSON 파일에서 텍스트를 읽어 ScriptableObject를 채웁니다.
당신의 사용자 정의 데이터는 ScriptableObject의 내용을 대체하고, 이 외부 모드 레벨을 다른 레벨처럼 사용할 수 있게 해줍니다. 응용 프로그램의 나머지 부분은 정상적으로 작동하며, 변경 사항을 인식하지 못합니다.

우리의 패들 볼 미니 게임이 ScriptableObject 데이터 컨테이너의 모든 사용 사례를 보여줄 수는 없지만, 당신의 응용 프로그램을 위해 다음을 고려해 보세요:
ScriptableObjects를 더 깊이 탐구하고 자신의 프로젝트에 맞게 조정하면서, 이들을 위한 더 많은 응용 프로그램을 발견하게 될 것입니다. 이들은 데이터를 관리하는 데 특히 유용하며, 다양한 게임 요소 간의 일관성을 유지하는 데 더 쉽게 만들어 줍니다.

e-book ScriptableObjects로 Unity에서 모듈식 게임 아키텍처 만들기에서 ScriptableObjects와 함께하는 디자인 패턴에 대해 더 읽어보세요. 게임 프로그래밍 패턴으로 코드를 향상시키기에서 일반 Unity 개발 디자인 패턴에 대해 더 알아볼 수 있습니다.