본문 바로가기
유니티/개념 정리

ScriptableObject

by fore4022 2024. 9. 30.

 클래스의 인스턴스 자신에서 정보를 초기화하는 것과 다르게, 여러 객체의 인스턴스에서 사용 가능한, 개발자가 정의한 형식의 데이터를 담는 컨테이너이다.


장점

  •  값의 불필요한 복사를 하나의 ScriptableObject에 대한 참조로, 메모리 효율을 높인다.
  •  에디터에서 생성하고, 값을 수정할 수 있다.

ScriptableObject의 사용

  1.  ScriptableObject를 상속 받는다.
  2.  [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