같은 에셋을 읽어도 현재 체력은 따로 둡니다
Unity 6000.5.3f1 빈 프로젝트에서 EnemyStats.asset을 두 번 불러오자 두 참조는 같은 오브젝트였습니다. 두 소비자도 같은 에셋을 가리켰습니다. 에셋의 maxHealth는 120이었고 A에게 30 피해를 주자 A의 현재 체력만 90, B는 120, 공유 기본값도 120으로 남았습니다. ScriptableObject에는 여러 오브젝트가 같이 읽을 기본값을 두고, 현재 체력처럼 인스턴스마다 달라지는 값은 컴포넌트에 둬야 합니다. Project 창에서 EnemyStats 에셋을 만든 뒤 Enemy Prefab의 defaults 슬롯에 할당합니다.
// EnemyStats.cs
using UnityEngine;
[CreateAssetMenu(fileName = "EnemyStats", menuName = "Game/Enemy Stats")]
public sealed class EnemyStats : ScriptableObject
{
[Min(1)] public int maxHealth = 120;
[Min(0f)] public float moveSpeed = 3.5f;
}
// Enemy.cs
public sealed class Enemy : MonoBehaviour
{
[SerializeField] private EnemyStats defaults;
public int CurrentHealth { get; private set; }
private void Awake()
{
if (defaults == null)
{
Debug.LogError("EnemyStats is not assigned.", this);
enabled = false;
return;
}
CurrentHealth = defaults.maxHealth;
}
public void Damage(int amount)
{
CurrentHealth = Mathf.Max(0, CurrentHealth - amount);
}
}원본 에셋을 바꾸면 모든 소비자가 새 값을 봅니다
통제 실험에서 공유 에셋의 maxHealth를 120에서 200으로 바꾸자 두 에셋 참조가 즉시 200을 읽었습니다. 이미 만들어 둔 A와 B의 현재 체력 90과 120은 그대로였습니다. 기본값과 현재 상태를 분리했기 때문입니다. 반대로 Damage가 defaults.maxHealth를 직접 줄였다면 한 적이 맞을 때 다른 적의 기본값까지 바뀝니다.
Play Mode 변경은 무조건 되돌아온다는 Prefab 규칙을 ScriptableObject 에셋에 그대로 적용하면 안 됩니다. Unity 6.5 매뉴얼은 Editor에서 Edit Mode와 Play Mode 모두 ScriptableObject 에셋 데이터를 저장할 수 있다고 설명합니다. Inspector나 에디터 도구가 에셋을 저장할 수 있으므로, 원본 에셋을 세션 상태처럼 바꾸지 않는 구조가 먼저입니다.
세션 설정을 바꿔야 하면 런타임 복제본을 만듭니다
같은 실험에서 Instantiate(authoringStats)로 만든 복제본은 원본과 다른 참조였고 AssetDatabase.GetAssetPath도 빈 문자열이었습니다. 복제본의 maxHealth를 250으로 바꿔도 원본과 두 번째 에셋 참조는 120을 유지했습니다. AssetDatabase 확인은 Editor 실험용일 뿐 런타임 코드에는 넣지 않습니다.
모든 소비자가 같은 세션 설정을 봐야 하면 이 복제본 하나를 공유하고, 소비자마다 다른 설정이 필요하면 각각 복제합니다. 복제 시점의 원본 값이 출발점이며 이후 변경은 자동 동기화되지 않습니다.
using UnityEngine;
public sealed class SessionRules : MonoBehaviour
{
[SerializeField] private EnemyStats authoringStats;
public EnemyStats RuntimeStats { get; private set; }
private void Awake()
{
RuntimeStats = Instantiate(authoringStats);
}
public void ApplyDifficulty(int maxHealth)
{
RuntimeStats.maxHealth = maxHealth;
}
private void OnDestroy()
{
if (RuntimeStats != null)
Destroy(RuntimeStats);
}
}메모리 변경과 디스크 저장은 다른 단계입니다
재현 스크립트가 원본의 값을 200으로 바꿨을 때 에셋은 dirty 상태가 아니었고, SetDirty와 SaveAssets를 호출하지 않은 디스크 파일의 SHA-256도 작성값 그대로였습니다. 이 결과는 이번 Editor 스크립트 경로에 대한 관찰입니다. 에디터 도구에서 변경을 의도적으로 저장하려면 EditorUtility.SetDirty로 변경을 표시하고 AssetDatabase.SaveAssets 같은 저장 단계를 거칩니다.
이 차이를 플레이어 세이브 방식으로 이용하면 안 됩니다. Unity 매뉴얼대로 배포된 Player는 빌드에 들어간 ScriptableObject 에셋을 읽을 수 있지만 진행 상황을 그 에셋에 저장할 수 없습니다. 진행 데이터는 파일, PlayerPrefs, 서버 같은 별도 저장 경로에 둡니다.
직접 재현
검증일
검증 환경Unity 6000.5.3f1 (c2eb47b3a2a9), Windows 10 22H2 (10.0.19045) 64-bit, 기존 게임과 분리한 빈 프로젝트, Mono Editor, -batchmode -nographics -noUpm
Unity 6000.5.3f1 빈 프로젝트의 별도 Editor 프로세스 3회에서 에셋 참조 공유, 컴포넌트별 현재 체력, 공유 에셋 변경 전파, 런타임 복제 분리, SetDirty·SaveAssets 없는 디스크 유지 결과를 직접 재현했습니다. 타임스탬프를 뺀 결과는 모두 같았습니다.