클래스의 인스턴스 자신에서 정보를 초기화하는 것과 다르게, 여러 객체의 인스턴스에서 사용 가능한, 개발자가 정의한 형식의 데이터를 담는 컨테이너이다.
장점
- 값의 불필요한 복사를 하나의 ScriptableObject에 대한 참조로, 메모리 효율을 높인다.
- 에디터에서 생성하고, 값을 수정할 수 있다.
ScriptableObject의 사용
- ScriptableObject를 상속 받는다.
- [CreateAssetMenu]를 통해서 파일의 이름과 메뉴 구조를 지정한다. 지정된 메뉴 구조를 통해서 새로운ScriptableObject를 생성한다.
using UnityEngine;
[CreateAssetMenu(fileName = "Example", menuName = "Create New ScriptableObject/Create New Example_SO")]
public class Example_SO : ScriptableObject
{
public GameObject exampleGameObject;
public float exampleFloat;
public int exampleInt;
}
ScriptableObject에서 Struct, Tuple 사용하기
ScriptableObject에서 직렬화 없이 Struct, Tuple을 사용하면, 생성한 ScriptableObject의 인스펙터 창에서 보이지 않는다.
using UnityEngine;
[CreateAssetMenu(fileName = "Example", menuName = "Create New ScriptableObject/Create New Example_SO")]
public class Example_SO : ScriptableObject
{
public struct ExampleStruct
{
public float TestA;
public int TestB;
}
public (string, float) exampleTuple;
public ExampleStruct exampleStruct;
}//그 어떤 데이터도 표시되지 않는다.
Struct, Tuple은 기본적으로 직렬화 되는 타입이 아니다. 인스펙터에 보이기 위해서는 별도로 직렬화가 가능한 타입으로 만들어 주어야 한다.
직렬화 방법
- Struct : 기본적으로 직렬화 타입이 아니기 때문에 [System.Serializable]를 이용해 직렬화 가능하게 해주어야 한다.
- Tuple : 유니티에서 tuple은 c#에서 지원하는 기능으로, 유니티에서는 기본적으로 직렬화 되지 않으므로, 만들고자 하였던 tuple과 같은 구조의 직렬화 가능한 클래스로 대체하여서, 해당 클래스의 변수를 통해서 tuple과 같이 사용할 수 있다.
using UnityEngine;
[System.Serializable]
public class ExampleTuple
{
public int valueA;
public int valueB;
}
[CreateAssetMenu(fileName = "Example", menuName = "Create New ScriptableObject/Create New Example_SO")]
public class Example_SO : ScriptableObject
{
[System.Serializable]
public struct ExampleStruct
{
public float TestA;
public int TestB;
}
public ExampleTuple exampleTuple;
public ExampleStruct exampleStruct;
}
사용하는 메모리 줄이기
using UnityEngine;
[CreateAssetMenu(fileName = "Stat", menuName = "Create New ScriptableObject/Create New Stat_SO")]
public class Stat_SO : ScriptableObject
{
public int damage;
public int health;
public int speed;
}//예시 ScriptableObject인 Stat이다.
잘못된 예시
using UnityEngine;
public class Monster : MonoBehaviour
{
private int damage;
private int health;
private int speed;
public void set(Stat_SO stat)
{
damage = stat.damage;
health = stat.health;
speed = stat.speed;
}
}
4byte인 int 변수 3개를 가지고 있기 때문에 총 12byte를 사용하였다. 100마리의 몬스터가 있을 경우, 차지하는 메모리 양은 총 1200byte가 된다.
옳은 예시
using UnityEngine;
public class Monster : MonoBehaviour
{
private Stat_SO stat;
private int health;
public void set(Stat_SO stat)
{
this.stat = stat;
health = stat.health;
}
}
4byte인 int 변수 1개를 가지고 있기 때문에 총 4byte를 사용하였다. 100마리의 몬스터가 있을 경우, 차지하는 메모리 양은 총 400byte가 된다.
비교
위의 두 예시의 차이점은 값의 복사와 참조이다. 공통된 값을 가져와 사용하는 것이기에, 한번 가져온 ScriptableObject를 참조하여서 사용한 것으로, 불필요한 값의 복사가 없다.
복사와 참조의 기준
우선 빌드가 된 게임에는, 생성된 ScriptableObject에 저장된 값은 ‘불변’한다. 여기서 불변하는 값을 기준으로 복사와 참조의 사용을 정하면 된다.
복사
- ScriptableObject를 통해서 가져온 값으로 초기화 이후에 실시간으로 변하는 값일 때 사용한다
참조
- ScriptableObject를 통해서 가져온 값이 바뀌지 않을 때 사용한다.
주의할 점
- 에디터의 플레이 모드에서 SO의 값을 변경하는 과정에서, 기존에 설정해 놓은 ScriptableObject의 값이 변경될 수 있다.
- 빌드가 된 게임에서는 ScriptableObject의 값을 변경할 수 없기 때문에, 변경 가능한 정보를 저장하려는 목적으로 사용되어서는 안된다.
ScriptableObject는 인스턴스의 값을 에디터에서 스크립트보다 편하게 생성, 관리, 저장, 수정할 수 있게 해줍니다. 이를 통해서 개발자가 아닌 누구라도 쉽게 값을 효율적이고, 이해하기 쉽게 다룰 수 있습니다.
'유니티 > 개념 정리' 카테고리의 다른 글
| UI 사이의 Event 전달 (0) | 2024.10.07 |
|---|---|
| EventSystem (0) | 2024.10.07 |
| 관찰자 패턴(Observer Pattern) (0) | 2024.09.26 |
| 상속 (0) | 2024.09.23 |
| StopWatch와 Time.deltaTime (0) | 2024.09.12 |