카테고리 없음

[Unity] 스크립터블 오브젝트(Scriptable Object)란?

DearGreen 2024. 10. 10. 21:00

스크립터블 오브젝트(Scriptable Object)란?


ScriptableObject클래스 인스턴스와는 별도로 대량의 데이터를 저장하는 데 사용할 수 있는 데이터 컨테이너입니다.
ScriptableObject의 주요 사용 사례 중 하나는 값의 사본이 생성되는 것을 방지하여 프로젝트의 메모리 사용을 줄이는 것입니다. 이는 연결된 MonoBehaviour 스크립트에 변경되지 않는 데이터를 저장하는 프리팹이 있는 프로젝트의 경우 유용합니다.


이러한 프리팹을 인스턴스화할 때마다 해당 데이터의 자체 사본이 생성됩니다. 이러한 방법을 사용하여 중복 데이터를 저장하는 대신 ScriptableObject를 이용하여 데이터를 저장한 후 모든 프리팹의 레퍼런스를 통해 액세스할 수 있습니다. 즉, 메모리에 데이터 사본을 하나만 저장합니다.


MonoBehaviour와 마찬가지로 ScriptableObject는 기본 Unity 오브젝트에서 파생되나, MonoBehaviour와는 달리 게임 오브젝트에 ScriptableObject를 연결할 수 없으며 대신 프로젝트의 에셋으로 저장해야 합니다.

출처 : https://docs.unity3d.com/kr/2022.3/Manual/class-ScriptableObject.html

 

유니티 공식 커뮤니티에 접속해서 더 자세한 사실을 알 수 있었다.

 

[왜 스크립터블 오브젝트를 사용할까?]

 

아래 글의 예시 내용을 더 쉽게 정리해 보았다.

아래 글은 PlayerHp에 접근하게 되는 상황을 예시로 들었다. 크게 4가지 과정으로 정리할 수 있었다.

 

[예시 정리]

  1. 변수 설정: 플레이어의 HP는 PlayerHP라는 Float형 변수를 가진다. 데미지를 입으면 PlayerHP에서 차감되고, 치유되면 가산된다.
  2. 체력 바 프리팹: 이 때, 씬에 있는 체력 바는 PlayerHP 변수를 지속적으로 감시해 UI를 변경한다. 이 프리팹은 직접 플레이어의 정보에 접근하지 않고, 변수의 값만 참조한다.
  3. 확장성: PlayerHP가 낮아질 때 음악이나 적의 공격 패턴을 변경하는 등의 시스템을 추가하기 더 쉽다.(값 참조가 훨씬 간편하므로) 이러한 시스템은 플레이어 스크립트에 메시지를 보내지 않고, 플레이어 오브젝트에 대한 정보도 필요하지 않는다.
  4. 런타임 수정: Float 변수( PlayerHP )의 값을 수정할 때는 데이터를 런타임 값으로 복사해 디스크에 저장된 값을 변경하지 않도록 할 수도 있다. 이렇게 하면 초기 값을 보존할 수 있다. (ex. 능력치 초기화 등등... 쉽게 관리가 가능하다.)

그렇다면 아래 예시 코드를 살펴보자.

 

[로컬 변수를 사용해 데이터를 저장한 예시]

public class Player : MonoBehaviour
{
    private float playerHP = 100f;

    void TakeDamage(float damage)
    {
        playerHP -= damage;
        CheckHealth();
    }

    void Heal(float healAmount)
    {
        playerHP += healAmount;
        CheckHealth();
    }

    void CheckHealth()
    {
        if (playerHP <= 0)
        {
            // 플레이어 사망 처리
        }
    }
}

 

로컬 변수 'playerHp' 에 값을 저장한 모습이다. 그러나, 해당 방법을 사용하면 여러 문제점이 발생한다.

데이터가 클래스에 종속되어 외부에서 접근이 어렵다. 또한 클래스 인스턴스들끼리 같은 데이터를 공유할 수 없다. 

 

[SO를 사용해 데이터를 저장한 예시]

[CreateAssetMenu(fileName = "PlayerData", menuName = "ScriptableObjects/PlayerData", order = 1)]
public class PlayerData : ScriptableObject
{
    public float playerHP = 100f;
}

public class Player : MonoBehaviour
{
    public PlayerData playerData;

    void TakeDamage(float damage)
    {
        playerData.playerHP -= damage;
        CheckHealth();
    }

    void Heal(float healAmount)
    {
        playerData.playerHP += healAmount;
        CheckHealth();
    }

    void CheckHealth()
    {
        if (playerData.playerHP <= 0)
        {
            // 플레이어 사망 처리
        }
    }
}

 

위 코드를 SO를 사용해 수정한다면 앞서 언급했던 문제가 해결될 것이다. 정말 간단한 경우에만 로컬 변수를 사용하고, 데이터 관리를 하는 경우 스크립터블 오브젝트를 사용하자.

 

출처 - '스크립터블 오브젝트로 게임을 설계하는 세 가지 방법'