게임 개발

ScriptableObject에 설정값과 현재 체력을 함께 넣으면 생기는 일

공유 설정과 개체별 상태를 나누는 ScriptableObject 예제. 플레이 모드에서 남은 값과 디스크 저장의 차이, 빌드의 세이브 처리도 설명합니다.

CodingJoa 수정 2026.09.22 3분 정도 걸립니다

몬스터 두 마리가 같은 설정 에셋을 참조하면 체력과 이동 속도를 한곳에서 바꿀 수 있습니다. 편리한데, 여기에 ‘현재 체력’까지 넣으면 이야기가 달라집니다. 한 마리를 때렸을 뿐인데 다른 몬스터가 읽는 체력도 줄어들 수 있습니다. 두 개체가 같은 데이터를 보고 있기 때문입니다.

ScriptableObject를 쓸 때 먼저 정할 것은 무엇을 저장할 수 있느냐보다 누가 그 값을 공유하느냐입니다. 이 글은 Unity 6의 일반적인 에셋 참조 방식을 기준으로 합니다.

기본 체력은 공유하고, 현재 체력은 따로 둔다

아래 클래스를 EnemySettings.cs로 저장하면 Assets의 Create 메뉴에서 설정 에셋을 만들 수 있습니다.

using UnityEngine;

[CreateAssetMenu(menuName = "Game/Enemy Settings")]
public sealed class EnemySettings : ScriptableObject
{
    [Min(1)] public int maxHealth = 100;
    [Min(0f)] public float moveSpeed = 3f;
}

각 몬스터에 붙일 EnemyHealth.cs는 초기값을 읽어 자신의 상태로 보관합니다.

using UnityEngine;

public sealed class EnemyHealth : MonoBehaviour
{
    [SerializeField] private EnemySettings settings;
    public int CurrentHealth { get; private set; }

    private void Awake()
    {
        if (settings == null)
        {
            enabled = false;
            return;
        }
        CurrentHealth = Mathf.Max(1, settings.maxHealth);
    }

    public void TakeDamage(int amount)
    {
        if (!enabled || amount <= 0)
            return;
        CurrentHealth = amount >= CurrentHealth ? 0 : CurrentHealth - amount;
    }
}

설정 에셋 하나를 두 몬스터에 연결하고 한쪽에만 데미지를 주면, 다른 쪽의 CurrentHealth는 그대로여야 합니다. 예제는 Awake에서 한 번 초기화하므로 풀링으로 재사용할 때는 부활 시점에 상태를 재설정하는 메서드가 필요합니다.

반대로 전투 도중 설정의 maxHealth를 바꿔도 이미 복사한 CurrentHealth는 자동으로 바뀌지 않습니다. 밸런스 수정이 모든 개체에 즉시 반영되어야 한다면 별도 갱신 규칙을 정해야 합니다.

플레이를 멈춘 뒤 값이 남는 것과 저장은 다르다

씬의 일반 컴포넌트 값은 플레이 종료 시 되돌아가는 반면, 참조한 ScriptableObject 에셋의 변경은 에디터에 남을 수 있습니다. 그래서 실수로 원본 설정을 수정하기도 쉽습니다.

하지만 메모리에 남아 있는 값과 디스크에 저장된 에셋은 구분해야 합니다. Inspector 편집과 스크립트 대입은 저장 처리도 같지 않습니다. 커스텀 에디터 도구라면 SerializedObject, 변경 표시, 에셋 저장 시점을 고려해야 합니다. 플레이를 멈췄을 때만 보지 말고 에디터를 다시 열어 값이 유지되는지도 확인하세요.

배포된 게임에서는 이 에셋을 유저 진행 상황의 영구 저장소로 쓰지 않습니다. 설정은 에셋에서 읽고, 플레이 기록은 별도 저장 데이터로 직렬화하는 식으로 나눕니다. 런타임 복제본이 필요하면 Instantiate로 복제할 수 있지만, 복제본 안의 다른 Unity 객체 참조까지 모두 깊은 복사가 되는 것은 아닙니다.

JSON을 전부 없애야 할까

그럴 필요는 없습니다. 원격으로 배포하는 설정이나 유저 세이브에는 텍스트 포맷이 편리할 수 있습니다. 로딩 시 한 번 파싱하는 비용과 전투 중 매 프레임 파싱하는 비용도 다릅니다.

ScriptableObject를 선택하는 이유를 ‘무조건 GC가 줄어서’라고 잡기보다는 에디터에서 편집하기 편한지, Unity 에셋 참조가 필요한지, 여러 개체가 같은 설정을 공유하는지로 판단하는 편이 명확합니다. 성능을 이유로 바꾼다면 파싱 시간과 할당량을 먼저 측정해야 합니다.

참고 문서

이 글 공유하기 X Facebook 네이버

이 글은 기술 설명과 참고 자료, 글쓴이의 판단을 담고 있습니다. 예제와 실제 사용 사례의 적용 조건은 본문을 참고해주세요. 도구의 버전·가격·동작은 시간이 지나면 달라질 수 있으니, 따라 하실 때는 공식 문서를 함께 확인해주세요. 자세한 내용은 이용약관에 정리해두었습니다.