오늘은 객체의 상태에서 행위와 공통 데이터를 분리하여 메모리를 최적화하는 아키텍처 패턴인 ScriptableObject에 대해 정리해보겠습니다.
기존의 모노비헤이어의 방식으로 데이터를 관리할 때 다음의 문제점이 존재하빈다.
메모리 낭비
다수의 적을 생성할 때, 그만큼의 프리팹이 동일한 수치를 메모리에 중복해서 생성하기 때문에 메모리 소모가 심하게 발생합니다.
수정의 번거로움
만약 무기의 데미지 조정과 같은 행위가 발생하면 해당 스크립트가 붙은 프리팹을 일일이 찾아 수정하거나 코드를 수정해야하는 번거로움이 존재합니다.
데이터와 로직이 섞임
변수 선언 이외에 로직이 추가되면 데이터와 로직이 한 곳에 존재하여 코드가 비대해지고 스크립트의 재사용성이 떨어집니다.
스크립터블 오브젝트는 게임오브젝트에 부착하지 않고도 데이터를 에셋 형태로 저장할 수 있는 클래스입니다.
수백 개의 인스턴스가 동일한 데이터를 참조하게 하여 메모리 낭비를 줄이며, 씬이 파괴되어도 파괴되지않아 프로젝트 전반에서 공유 가능합니다.
스크립터블 오브젝트를 선언하는 폴더는 시스템 단위가 아닌 데이터 타입 단위로 나눕니다.
이때, 에셋의 이름은 타입 접두어 + 식별명으로 통일합니다.
ITEMS
ㄴITM_HealthKit.asset
ㄴITM_AmmoBox.asset
Weapons
ㄴWPN_AR_Raven.asset
ㄴWPN_SG_Aegis.asset
실시간으로 변하는 체력과 같은 수치를 SO에 직접 저장하면 파일이 오염됩니다. 따라서 이를 해결하기 위해 다음과 같은 방법으로 스크립트를 분리합니다.
<style>
// SO로 고정 데이터 관리
using UnityEngine;
[CreateAssetMenu(fileName = "NewCharacterConfig", menuName = "SO/CharacterConfig")]
public class CharacterConfigSO : ScriptableObject
{
public string characterName;
public int maxHp;
public float moveSpeed;
}
---
// Runtime을 통해 가변 데이터 설정 및 생성자를 통한 데이터 초기화
[System.Serializable]
public class PlayerRuntimeState
{
public int currentHp;
public int score;
// 생성자나 초기화 함수를 통해 SO 데이터를 기반으로 세팅
public PlayerRuntimeState(CharacterConfigSO config)
{
this.currentHp = config.maxHp;
this.score = 0;
}
}
---
// Controller를 Monobehaviour 스크립트로 지정.
// 데이터 변화 로직 구현
public class PlayerController : MonoBehaviour
{
[SerializeField] private CharacterConfigSO configData;
private PlayerRuntimeState runtimeState;
public void TakeDamage(int damage)
{
runtimeState.currentHp -= damage;
}
}
</style>