택배 데이터 설계: ScriptableObject로 종류·점수·표시 규칙 관리하기
6강. 택배 데이터 설계: ScriptableObject로 종류·점수·표시 규칙 관리하기
1. 이번 강의에서 해결할 문제
Blue와 Orange 분기를 여러 스크립트에 쓰면 새 종류 추가 때 생성·판정·UI 코드를 모두 고쳐야 합니다. 택배별 규칙을 에셋 하나로 묶습니다.
2. 학습 목표
3. 핵심 개념
ScriptableObject는 MonoBehaviour처럼 GameObject에 붙이지 않고 Project 에셋으로 만듭니다. 여러 택배 인스턴스가 읽기 전용 정의를 공유하므로 종류마다 Prefab을 복제하지 않아도 됩니다. 런타임 점수와 현재 위치는 데이터 에셋에 기록하지 않습니다.
처음 등장하는 Unity 데이터 용어
| 용어 | 무엇인가 | 이 강의에서의 역할 |
|---|---|---|
| ScriptableObject | GameObject 없이 Project 에셋으로 저장할 수 있는 Unity 객체 | 택배 종류별 고정 정의를 보관합니다. |
| Asset | Assets 폴더에 저장되고 프로젝트가 참조하는 파일 | BluePackage.asset, OrangePackage.asset |
| Prefab | Component 구성을 재사용하는 GameObject 템플릿 | 다음 강의의 Package.prefab |
| Instance | Scene 또는 실행 중 만들어진 실제 객체 | 화면에서 내려오는 택배 한 개 |
| 직렬화 | Inspector 값을 에셋이나 Scene 파일에 저장하고 다시 읽는 과정 | Id·Sprite·Color·Score를 유지합니다. |
| 런타임 상태 | 한 번의 플레이 중 계속 바뀌는 값 | 현재 점수, 위치, 남은 목숨 |
ScriptableObject는 “전역 변수”가 아닙니다. 같은 에셋을 여러 인스턴스가 참조하므로 런타임에 현재 점수를 써 버리면 모든 참조자가 같은 값을 보게 됩니다. 여러 객체가 공유해도 되는 정의만 두고, 플레이마다 달라지는 상태는 GameSession 같은 런타임 객체가 소유합니다.
에셋을 쓰는 기준은 값의 수가 아니라 변경 책임입니다. 디자이너가 종류별 점수·색·속도를 코드 재컴파일 없이 조정하고, 여러 Prefab 인스턴스가 같은 정의를 읽어야 할 때 적합합니다. 인스턴스 하나만 잠깐 사용하는 값이라면 MonoBehaviour 필드가 더 단순할 수 있습니다.
4. 단계별 실습
C# · 파일 경로: Assets/_Project/Scripts/Data/PackageDefinition.cs
using UnityEngine;
namespace DeliverySorter.Data
{
[CreateAssetMenu(fileName = "Package", menuName = "Delivery Sorter/Package Definition")]
public sealed class PackageDefinition : ScriptableObject
{
[field: SerializeField] public string Id { get; private set; } = "blue";
[field: SerializeField] public string DisplayName { get; private set; } = "파란 택배";
[field: SerializeField] public Sprite Sprite { get; private set; }
[field: SerializeField] public Color Color { get; private set; } = Color.blue;
[field: SerializeField, Min(0)] public int Score { get; private set; } = 100;
[field: SerializeField, Min(0.1f)] public float SpeedMultiplier { get; private set; } = 1f;
}
}
코드 한 줄씩 이해하기
[CreateAssetMenu]는 Project 창의 Create 메뉴에 에셋 생성 항목을 추가합니다. 클래스가 자동으로 GameObject Component가 되는 것은 아닙니다.[field: SerializeField]는 자동 프로퍼티가 내부적으로 만드는 backing field를 Unity 직렬화 대상으로 지정합니다.get; private set;은 다른 클래스가 값을 읽을 수 있지만 실행 중 마음대로 교체하지 못하게 합니다. Inspector는 직렬화된 backing field를 편집합니다.Id는 코드와 저장 데이터에서 종류를 식별할 안정적인 값입니다. 화면에 보이는DisplayName과 분리해 이름을 번역해도 식별자가 바뀌지 않게 합니다.Sprite와Color는 표시 규칙,Score와SpeedMultiplier는 게임 규칙에 전달할 정의입니다.[Min]은 Inspector에서 잘못된 범위를 입력하지 않게 돕지만 중복 Id나 비어 있는 참조까지 검사하지는 않습니다.
가장 작은 소비 예제로 화면 확인하기
정의 에셋이 실제 표시를 바꾸는지 확인할 임시 Probe를 만듭니다. 다음 강의의 PackageActor가 이 책임을 정식으로 맡으면 Probe는 제거할 수 있습니다.
using DeliverySorter.Data;
using UnityEngine;
public sealed class PackageDefinitionProbe : MonoBehaviour
{
[SerializeField] private PackageDefinition definition;
[SerializeField] private SpriteRenderer targetRenderer;
private void Start()
{
if (definition == null || targetRenderer == null)
{
Debug.LogError("Definition and Target Renderer are required.", this);
enabled = false;
return;
}
targetRenderer.sprite = definition.Sprite;
targetRenderer.color = definition.Color;
Debug.Log($"{definition.DisplayName}: {definition.Score}점", this);
}
}
Inspector에서 definition만 BluePackage에서 OrangePackage로 바꾸면 코드 수정 없이 Sprite·Color와 Console 이름·점수가 달라져야 합니다.
Unity Editor에서 에셋과 Probe 연결
- Project 창에서
Create > Delivery Sorter > Package Definition으로BluePackage.asset,OrangePackage.asset을 만듭니다. - Id는 각각
blue,orange, Display Name과 Color도 서로 다르게 입력합니다. Score는 100과 150처럼 차이를 둡니다. - 직접 만든 Sprite가 없으면 두 에셋 모두 기본 Square를 사용하고 Color로 구분합니다.
- Scene의 Square에
PackageDefinitionProbe를 붙이고 같은 오브젝트의 Sprite Renderer를 Target Renderer에 연결합니다. - Definition에 BluePackage를 연결해 Play하고 화면 색과
파란 택배: 100점로그를 확인합니다. OrangePackage로 바꿔 결과도 달라지는지 확인합니다.
무료 에셋을 사용하면 Assets/_Project/Docs/ThirdPartyAssets.md에 출처와 라이선스를 기록합니다. Sprite를 비워도 색만 적용할 정책인지, Sprite가 필수라면 시작 전에 오류로 막을지도 프로젝트 규칙으로 정합니다.
5. 코드가 동작하는 이유
각 에셋은 같은 C# 타입이지만 서로 다른 직렬화 값을 갖습니다. Prefab은 생성될 때 정의를 받아 Sprite·색·점수 규칙을 적용하므로 종류 추가가 코드 분기로 이어지지 않습니다.
6. 자주 하는 실수와 해결법
7. 직접 실습
실습 목표
동일한 Probe와 SpriteRenderer가 서로 다른 ScriptableObject 정의를 받아 화면과 로그를 바꾸는 것을 확인합니다.
시작 전 상태
BluePackage와 OrangePackage의 Id·이름·색·점수가 서로 다르고, Probe의 두 참조가 모두 Inspector에서 연결돼 있어야 합니다.
1단계: 따라 하기
BluePackage를 연결해 Play합니다. 화면 색, Sprite, Console의 이름과 점수를 에셋 Inspector 값과 한 줄씩 대조합니다.
2단계: 값 바꿔 보기
코드는 그대로 둔 채 OrangePackage로 참조만 바꿉니다. 표시와 로그가 Orange 정의로 바뀌어야 합니다. Score만 250으로 바꿔 화면 색은 유지되고 로그 점수만 바뀌는지도 확인합니다.
3단계: 직접 적용
세 번째 FragilePackage.asset을 만들고 고유한 Id, 이름, 색, 200점, 0.8 Speed Multiplier를 설정합니다. C# 분기문을 추가하지 않고 Probe에서 선택할 수 있어야 합니다.
4단계: 스스로 확인
막혔을 때
| 증상 | 원인 | 해결 |
|---|---|---|
| Create 메뉴에 항목이 없음 | 컴파일 오류 또는 CreateAssetMenu 누락 | Console의 첫 오류를 해결하고 스크립트 재컴파일을 기다립니다. |
| Play해도 색이 안 바뀜 | Probe의 definition 또는 renderer가 비어 있음 | Inspector 두 슬롯과 Console 오류를 확인합니다. |
| 모든 종류가 같은 값으로 보임 | 같은 에셋을 복제하지 않고 한 에셋만 계속 편집 | Project 창에서 서로 다른 .asset 파일인지 확인합니다. |
| 실행 뒤 에셋 값이 예상치 않게 변함 | 공유 에셋에 런타임 상태를 씀 | 정의는 읽기 전용으로 사용하고 상태는 별도 런타임 객체로 옮깁니다. |
8. 이해 점검 질문 3개
9. 핵심 요약
다음 강의 연결
다음 강의: 택배 생성 시스템에서는 Spawner가 여러 PackageDefinition 중 하나를 선택해 PackageActor 인스턴스에 전달하는 전체 실행 흐름을 만듭니다.
택배 데이터 설계: ScriptableObject로 종류·점수·표시 규칙 관리하기 미니 퀴즈
선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.
학습을 마쳤나요?
직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.