Scriptable Object는 클래스의 인스턴스와는 별개로 자료를 저장할 수 있는 데이터 컨테이너입니다. Scrpitable Object는 주로 값의 사본이 생성되는 것을 방지하여 메모리 사용을 줄여줍니다. 예를 들어서 MonoBehaviour 스크립트에 변경되지 않는 데이터를 저장하는 프리팹이 있는 프로젝트의 경우 유용하게 사용됩니다. 프리팹을 인스턴스화 할 때마다 프리팹에 이 데이터 자체의 사본이 생성되는데, 이와 같은 방법을 사용하여 중복 데이터를 저장하는 대신에 Scriptable Object를 이용하여 데이터를 저장한 다음 모든 프리팹을 참조하여 접근할 수 있습니다. 즉, 메모리에 데이터 사본을 하나만 저장하게 되는 것입니다.

 

Scriptable Object는 유니티 오브젝트에서 파생되지만 MonoBehavior와 달리 게임 오브젝트에 연결할 수 없고 프로젝트의 에셋으로 사용하게 됩니다.

 

에디터 사용 시, Scriptable Object에 데이터를 저장하는 작업은 편집할 때나 런타임에 가능합니다. 이는 Scriptable Object가 에디터 스크립팅을 사용하기 때문입니다. 빌드 단계에서는 Scriptable Object를 사용하여 데이터를 저장할 수 없고 사용만 가능합니다. 에디터 툴에서 에셋 형태로 저장한 Scirptable Object 데이터는 디스크에 저장되서 세션 간에도 유지됩니다.

 

이제 간단한 예제로 이해를 해보겠습니다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObject/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject
{
    public string prefabName;

    public int numberOfPrefabsToCreate;
    public Vector3[] spawnPoints;
}

 

우선 데이터 컨테이너를 만들어주기 위해 MonoBehaviour 대신 Scirptable Object를 상속합니다. 이후 간편한 데이터 편집을 위해서 Create Asset Menu 속성을 사용해 Assets > Create > ScriptableObjects > SpawnManagerScriptableObject 경로로 인스턴스를 생성할 수 있게 만들어줍니다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Spawner : MonoBehaviour
{
    public GameObject entityToSpawn;

    public SpawnManagerScriptableObject spawnManagerValues;

    int instanceNumber = 1;

    private void Start()
    {
        SpawnEntities();
    }

    private void SpawnEntities()
    {
        int currentSpawnPointIndex = 0;

        for (int i = 0; i < spawnManagerValues.numberOfPrefabsToCreate; i++)
        {       
            GameObject currentEntity = Instantiate(
            entityToSpawn, 
            spawnManagerValues.spawnPoints[currentSpawnPointIndex], 
            Quaternion.identity);

            currentEntity.GetComponentInChildren<Text>().text = spawnManagerValues.prefabName;

            currentEntity.name = spawnManagerValues.prefabName + instanceNumber;	

				/* Scriptable Object의 데이터 수정 환경 테스트 */
            spawnManagerValues.prefabName = "TEST";

            currentSpawnPointIndex = (currentSpawnPointIndex + 1) % spawnManagerValues.spawnPoints.Length;

            instanceNumber++;
        }
    }
}

 

이제 만들어둔 Scriptable Object 데이터를 사용할 수 있는 스크립트를 만들어 줍니다. SpawnManagerScriptableObject 인스턴스에서 설정해둔 Prefab을 설정한 위치에서 원하는 개수만큼 생성하는 스크립트입니다. 중간에 Scriptable Object의 수정 환경 테스트를 위해서 spawnManagerValues.prefabName = "TEST"; 의 코드를 삽입했습니다.

 

Scriptable Object 인스턴스 생성

이제 실제로 Scriptable Object 인스턴스를 생성하고 테스트를 진행하겠습니다. 처음은 Scriptable Object의 인스턴스를 생성하고 원하는 대로 데이터를 저장하면 됩니다. (에디터 상의 데이터 저장)

 

에디터 상의 데이터 수정 및 저장

 

이후 플레이 버튼을 눌러 테스트를 진행하면  아래와 같은 형태로 오브젝트들이 구성됩니다.

 

런타임 상의 데이터 수정
런타임 상의 데이터 저장
데이터 수정 후의 테스트

위에서 코드 중간에 Scriptable Object의 변수인 prefabName을 런타임 도중 수정하도록 코드를 삽입했었습니다. 실제로 테스트를 해보면 런타임 도중 수정이 가능하고 저장 또한 가능하다는 것을 알 수 있습니다.

 

마지막으로 빌드 단계에서는 데이터의 사용만 가능하고 수정이 불가능한데 실제로 그렇습니다. 테스트를 해보시면 게임 실행 중에는 Cube - Test - Test로 데이터의 일시적인 변경이 가능하나, 다시 게임을 시작하면 Test - Test - Test가 아닌 Cube - Test - Test의 형태로 출력됩니다. 즉, 빌드 단계에서의 데이터의 수정이 불가능합니다.

 

지금까지 Scirptable Obejct 클래스에 대해서 간단하게 알아보았습니다.

블로그 이미지

NIA1995

,

인스펙터에서 나타나는 정보들을 원하는대로 수정하려면 Editor 클래스를 상속받아 스크립트를 작성하면 됩니다. 이 때 반드시 Project Folder/Assets/Editor 경로 상에 스크립트를 저장해야 동작하게 됩니다.

 

Properties

 

1. target : 검사 대상을 Object로 반환합니다.

 

Public Methods

 

1. OnEnable : 맞춤 제작한 에디터의 인스턴스가 생성될 때 호출됩니다.

 

2. OnInspectorGUI : 인스펙터 상에 표시되는 GUI들을 맞춤 제작할 때 사용합니다.

 

Message

 

1. OnSceneGUI : Scene 상에 표시되는 GUI들을 맞춤 제작할 때 사용합니다.

블로그 이미지

NIA1995

,

일반적으로 인자로 오는 변수들은 메모리에서 값을 복사해서 가져오기 때문에 함수 안에서 인자의 원본 값을 변경하지 못합니다. 그러나 out 키워드, ref 키워드는 메모리의 주소를 복사하여 변수를 직접 가리킵니다. 즉, 함수 안에서 인자 원본 값을 수정할 수 있게 됩니다. 

 

따라서 기본적으로 ref, out 키워드 둘 다 C++에서 참조자의 역할을 하게 됩니다.  여기서 out 키워드는 중요한 하나의 특징을 더 가지고 있는데 out 키워드가 붙은 인자는 해당 함수가 끝나기 전에 값을 할당 받아야 합니다. 

 

이를 간단한 예제를 통해 활용하는 방법을 알아보겠습니다. / 21.01.01 수정

 

public class ItemData
{
    public int buffValue;
    
    public ItemData(int buffValue)
    {
    	this.buffValue = buffValue;
    }
    
    public void AddValue(ref int v)
    {
    	v += buffValue;
    }
}

public class Character
{
   public int agility;
   public int intellect;
   public int stamina;
   public int strength;
   
   ……….
}

 

예를 들어서 아이템이 캐릭터의 특정 스탯을 올려준다고 가정할 때 캐릭터의 다양한 스탯마다 스탯을 증가하는 함수를 만들어 줄 수는 없습니다. 이 때 ref 키워드를 사용하면 해당 메모리에 접근하여 값을 수정할 수 있게 됩니다. 따라서 ref 인자로 원하는 스탯만 넣어준다면 아이템의 효과만큼 스탯을 증가시킬 수 있습니다.

 

- 참조

1. 인생 유니티 교과서

'프로그래밍 > C#' 카테고리의 다른 글

[C#] Boxing과 Unboxing  (0) 2021.03.15
[C#] 값형식과 참조형식의 예시  (1) 2021.03.15
[C#] var 데이터 형식  (0) 2021.03.15
[C#] SerializableAttribute 클래스  (0) 2021.01.01
[C#] Null 조건 부 연산자  (1) 2020.12.26
블로그 이미지

NIA1995

,