오늘 한 일
- 알고리즘 문제 풀기 (LeetCode 3075 문제)
- 스파르타 강의 진도 나가기 (~1-13)
- ScriptAbleObject 파헤치기
오늘은 강의 진도를 나아가면서 여러가지를 알게 되었는데 그 중에 Unity의 주요 기능 중 하나인 ScriptableObject에 대해서 자세히 알아보려고 한다.
클래스 인스턴스와는 별도로 대량의 데이터를 저장하는 데 사용할 수 있는 데이터 컨테이너입니다. 에디터 세션 동안 데이터를 저장 및 보관하고, 데이터를 프로젝트의 에셋으로 저장하여 런타임 시 사용합니다.
1. ScriptableObject를 상속받는 스크립트 작성
2. 작성 후 추가된 CreateAssetMenu에서 커스텀 에셋 생성
3. 생성된 ScriptableObject의 field 데이터들을 조정
4. ScriptableObject의 데이터 사용
1. ScriptableObject를 상속받는 스크립트 작성
개체 생성에 관한 정보를 담는 SO 스크립트
using UnityEngine; [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)] public class SpawnManagerScriptableObject : ScriptableObject { // 생성된 애들 이름 public string prefabName; // 얼마의 프리팹을 만들까? public int numberOfPrefabsToCreate; // 생성 지점 리스트 public Vector3[] spawnPoints; }
그럼 위에 있는 코드들을 하나씩 하나씩 설명해보겠다.
CreateAssetMenu
설명
ScripableObject를 상속 받은 스크립트를 Assets/Create 메뉴에 표시하여 Asset에 쉽게 생성하고 저장하기 위해 사용되는 속성(Attribute)이다.
- fileName
- 생성될 시 기본적으로 부여되는 파일 이름
- menuName
- Create 하위 메뉴바에서 보여지게 될 이름을 정한다 '/'처리를 할 때마다 새로운 하위 메뉴로 이어간다.
- order
- Create 메뉴바에서 보여지게될 우선순위를 정해주는 변수이다.
- order = 1
- 맨 상단에 위치
- order = int.MaxValue
- 맨 하단에 위치
ScriptableObject
설명
Unity의 주요 기능 중 하나로 게임에서 주로 재사용 가능한 데이터 또는 설정을 저장하는데 사용되는 클래스입니다.
기본 유니티 프로젝트에서 파생되나, MonoBehaviour과 달리 게임 오브젝트에 연결할 수 없으므로 CreateAssetMenu와 함께 사용하여 프로젝트 에셋으로 저장한 후에 활용해야합니다.
Unity 에디터와 통합되어 인스펙터 창에서 직접 수정하고 관리할 수 있습니다.
- 사실 CreateAssetMenu를 제외한 딱히 사용되는 코드는 없고, 이걸 상속을 받게된다면 Create 메뉴바로 해당 오브젝트를 빠르게 생성하고 해당 스크립트 내 작성한 클래스의 필드들을 마음껏 커스터마이징할 수 있는 기능을 얻게 된다.
! 주의할 점
- 필드들의 접근 제한자는 최소한 public 범위 정도는 되어야한다.
(protected 이하로 내려가면 다른 오브젝트에서 해당 필드의 사용이 힘들다.)
+ [Head("Category")]
- 인스펙터 창에서 데이터를 관리할 때 가독성을 높여주는 역할을 해준다.
- 사용하는 방법
[Header("Attack Info")] public float size; public float delay; public float power; public float speed; public LayerMask target; [Header("Knock Back Info")] public bool isOnKnockback; public float knockbackPower; public float knockbackTime;
- 사진처럼 데이터들을 목록화하여 보기 좋게 관리해줄 수 있다.
2. 작성 후 추가된 CreateAssetMenu에서 커스텀 에셋 생성
1. 파일을 이와 같이 만들어 정리를 쉽게 한다
- Scripts는 ScriptableObject 스크립트들이 모이는 곳, 뒤에 SO 붙은 게 ScriptableObject 에셋을 생성하는 곳이다.
2. Scriptable Object 생성
- 우클릭 -> Create -> menuName 지정해준대로 찾아가주면 이와 같이 생성된다.
3. Scriptable Object 이름 지정
- F2를 눌러 이름을 지어주자
사실 이 항목은 크게 중요한 거 없고 정리를 잘해주자
3. 생성된 ScriptableObject의 field 데이터들을 조정
- 생성한 오브젝트의 Inspector 보면 밑의 사진처럼 나와있을 것이다.
- 원하는대로 필드를 마음껏 조종해보자
+ Range(minValue, maxValue)
- 값의 범위를 지정해 제한 시키는 속성이다.
[Range(1, 10)] public int numberOfPrefabsToCreate;
위와 같이 접근 제한자보다 앞세워서 사용하는 속성(Attribute)인데 이처럼 코드를 짜주면,
- Inpsector에서 위와 같이 슬라이더가 생기는데
- 최소값과 최대값의 범위만큼 움직일 수 있다.
4. ScriptableObject의 데이터 사용
- SO를 참조하는 스크립트
public class Spawner : MonoBehaviour { // 생성에 사용될 게임 오브젝트 public GameObject entityToSpawn; // 정의되었던 SO를 담아줄 변수이다. public SpawnManagerScriptableObject spawnManagerValues; // 생성 매니저 SO // 개체를 생성할 때마다 이름에 같이 붙여줄 숫자로, 개체가 생성될 때마다 증가한다. int instanceNumber = 1; void Start() { SpawnEntities(); // 스폰 메서드 } void SpawnEntities() { int currentSpawnPointIndex = 0; // 소환 작업에 쓰일 인덱스 for (int i = 0; i < spawnManagerValues.numberOfPrefabsToCreate; i++) // SO에 있는 Prefabs 소환 횟수 { // SO에서 정해진 스폰 지점에서 프리팹의 인스턴스 생성 GameObject currentEntity = Instantiate(entityToSpawn, spawnManagerValues.spawnPoints[currentSpawnPointIndex], Quaternion.identity); // SO에 정의된 문자열에 고유한 숫자를 덧붙여줘서 생성된 개체의 이름을 지어준다. currentEntity.name = spawnManagerValues.prefabName + instanceNumber; // 다음 소환 지점의 인덱스로 이동한다.. 인덱스의 범위를 넘어간다면, 시작점으로 다시 되돌아간다. currentSpawnPointIndex = (currentSpawnPointIndex + 1) % spawnManagerValues.spawnPoints.Length; instanceNumber++; } } }
- SO를 담아줄 변수와 그 SO를 사용하는 메서드를 구현하고자 하는 기능대로 짜주면 된다.
! 스크립트 파일의 이름이 클래스와 동일해야한다.(Unity의 규칙)
Inspector 세팅
- SpawnManagerSO
- Spawner Object
실행 결과
- 위와 같이 지정한 스폰 횟수와 스폰 지점대로 생성이 잘 된다.
장점
1. 클래스 필드의 다양한 값들을 쉽고 빠르게 만들 수 있다.
- Create 메뉴바에서 SO를 만든 후 Inspector에서 원하는 대로 건들여주면 된다.
2. 기존에 프리팹을 생성할 때마다 해당 데이터의 자체 사본이 생성되는데, 이러한 방법을 사용하여 중복 데이터를 저장하는 대신 ScriptableObject를 이용하여 데이터를 저장한 후 모든 프리팹의 참조를 통해 동일한 정보를 전달할 수 있다.
- 기존 방식
- 같은 값을 계속해서 만들고 있다.
- SO를 이용한 방식
- SO를 참조하여 값을 가져오고 있다. (불필요한 중복 데이터로 인한 데이터 낭비 감소)
- 그래서 많은 양의 동일한 정보를 사용하는 오브젝트를 생성할 때 유리하다.
3. 생성된 스크립터블 오브젝트는 asset파일로 저장되기 때문에 다른 Unity 프로젝트로 복사할 수 있다.
- 1주차 팀프로젝트 과제로 옮겨보면 잘 옮겨진다.
- 주의사항
- SO의 본체인 스크립트를 빼먹으면 안된다.
- 동일한 파일 경로를 유지해줘야한다. (Unity가 에셋의 상대 경로를 사용한다.)
- 버전 간 호환성 및 의존되는 스크립트나 플러그인을 잘 봐줘야한다.
- 파일을 이동할 때는 Unity의 Export Package 기능을 사용하면 다른 프로젝트로 쉽게 이동할 수 있다.
단점
1. 런타임 중에서는 ScriptableObject의 데이터를 수정할 수 없다. 그래서 런타임 중에 매번 변동되어야하는 데이터는 사용을 지양하는 것이 좋다.
1. 게임에서 재사용이 잦은 스텟이 있을 때 (Hp, Attack, Speed와 같은 데이터들)
2. 아이템 사전이나 몬스터 사전과 같이 오브젝트끼리 같은 변수를 사용하지만 서로 간의 값을 달리하려할 때 (아이템 enum, 몬스터 enum 등)
3. 오브젝트의 생성을 담당하는 오브젝트가 존재할 때 (생성 지점, 생성 횟수, 생성 조건 등등)
- Attribute(속성)
이 녀석들은 []"대괄호"를 주로 사용하고 접근 제한자보다 앞서 사용하는 특징을 가졌다.
위 내용을 작성하면서 알게된 게, 내가 처음에 정확한 자료가 별로 없는 줄 알고 Unity 매뉴얼과 봤던 유튜브 영상만으로 내용을 작성하다가 뭔가 부족하다 싶어서 더 조사해본 결과 스크립터블 오브젝트를 전문적으로 다룬 자료를 찾아내게 되었다. 아무래도 TIL을 작성하기 전에 자료 조사 측면에서 좀 더 신경을 쓰고 조사를 해야겠다는 생각이 들었다.
그리고 추후에 시간이 된다면 이벤트 설계와 런타임 세트를 활용한 스크립터블 오브젝트도 한번 활용해볼 생각이다.