[TIL] Unity - Scriptable Object Manager - day 86

뭉크의 개발·2023년 11월 17일
0

Unity - Camp

목록 보기
55/70
post-thumbnail

🐧 오늘 배운 것

프로젝트를 진행하면서, Scriptable Object를 사용하게 되는데 이를 관리하는 코드를 팀원분께서 만들어 주셨다.

나도 Scripatable Object를 사용하게 되는데,

내가 다른 사람이 만든 코드를 이해하고 프로젝트에 함께 적용할 수 있어야 개선점도 파악할 수 있고,

프로젝트 협업의 장점이 극대화 되기 때문이다.

https://github.com/kksoo0131

using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.ResourceManagement.AsyncOperations;
using static Enums;

public class ScriptableObjectManager
{
    #region Singleton
    private static ScriptableObjectManager _instance;
    public static ScriptableObjectManager Instance { get { return _instance ?? (_instance = new ScriptableObjectManager()); } }
    #endregion

    ScriptableObjectManager()
    {
        SOBaseDic = new Dictionary<AddressableLabel, List<ScriptableObject>>();
    }

    public Dictionary<AddressableLabel, List<ScriptableObject>> SOBaseDic;

    public async Task LoadScriptableObject(AddressableLabel label)
    {
        if(SOBaseDic.ContainsKey(label))
        {
            return;
        }

        SOBaseDic.Add(label, new List<ScriptableObject>());
        foreach (var asset in ResourceManager.Instance.ReferenceDic[label])
        {

            var handle = asset.Value.LoadAssetAsync<ScriptableObject>();
            await handle.Task;

            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                SOBaseDic[label].Add(handle.Result);
            }
            else
            {
                Debug.Log("리소스 로딩에 문제");
            }
        }

    }

    public async Task<ScriptableObject> SelectRandomSO(AddressableLabel label, int i)
    {
        if (!SOBaseDic.ContainsKey(label))
        {
            await LoadScriptableObject(label);
        }

        return SOBaseDic[label][i];
    }
}

🐧 기억할 것 & 진행

이 클래스의 주요 기능과 구조는 다음과 같다.

생성자 (Constructor)

생성자에서는 SOBaseDic이라는 사전을 초기화한다. 이 사전은 AddressableLabel을 키로 하고 ScriptableObject의 리스트를 값으로 하는 구조를 가진다.

LoadScriptableObject 메서드

이 메서드는 주어진 AddressableLabel에 대응하는 ScriptableObject들을 비동기적으로 로드한다.
이미 로드된 레이블에 대해서는 아무 작업도 수행하지 않는다.
ResourceManager 인스턴스를 통해 해당 레이블에 대한 에셋들을 가져온 다음, 각 에셋에 대해 LoadAssetAsync< ScriptableObject >를 호출하여 비동기적으로 로드한다.
로드가 성공하면 결과를 SOBaseDic 사전에 추가한다.

SelectRandomSO 메서드

주어진 레이블에 대응하는 ScriptableObject 리스트에서 무작위 객체를 선택하여 반환한다.
먼저 해당 레이블이 SOBaseDic에 존재하지 않는 경우, LoadScriptableObject를 호출하여 해당 레이블의 객체들을 먼저 로드한다.
그 다음, 리스트에서 지정된 인덱스의 ScriptableObject를 반환한다.


🐧 왜 사용할까?

사용한 경우와 사용하지 않는 경우를 생각해보자.

💡 사용한 경우

중앙화된 관리

ScriptableObject 인스턴스들은 중앙화된 관리자를 통해 관리된다. 데이터 접근과 수정을 보다 체계적으로 만들며, 게임 내에서 데이터의 일관성을 유지하는 데 도움이 된다.

비동기 로딩

ScriptableObjectManager는 비동기 로딩을 사용하여 ScriptableObject들을 로드한다. 이는 게임의 성능에 긍정적인 영향을 미칩니다. 예를 들어, 게임의 초기 로딩 시간을 단축시키고, 게임 플레이 중에 발생할 수 있는 렉을 감소시킬 수 있다.

메모리 관리 최적화:

중앙 관리 시스템을 사용하면 메모리 사용을 더 효율적으로 할 수 있다. 필요할 때만 ScriptableObject를 로드하고, 사용하지 않는 객체는 메모리에서 해제할 수 있다.

재사용성 및 확장성:

이러한 관리 시스템은 코드의 재사용성과 확장성을 높인다. 새로운 유형의 ScriptableObject를 추가하거나, 관리 방식을 변경하는 것이 훨씬 간단해진다.

💡 사용하지 않는 경우

직접 관리

ScriptableObject를 직접 관리해야 한다. 이는 프로젝트가 커지면서 관리의 복잡성이 증가할 수 있다.

동기적 로딩의 단점

만약 비동기 로딩을 사용하지 않는다면, ScriptableObject를 로딩하는 동안 게임이 멈추거나 렉이 발생할 수 있다. 특히 대규모 데이터를 로딩할 때 문제가 될 수 있다.

메모리 효율성 감소:

ScriptableObject들이 개별적으로 관리될 경우, 불필요하게 많은 데이터가 메모리에 상주할 수 있으며, 이는 특히 모바일 게임과 같이 메모리 제한이 엄격한 환경에서 문제가 될 수 있다.

코드 유지보수 어려움

데이터 관리 코드가 게임 전반에 흩어져 있을 경우, 유지보수가 어려워질 수 있다. 예를 들어, ScriptableObject 로딩 방식을 변경하고자 할 때, 관련 코드가 여러 곳에 존재하면 모든 부분을 수정해야 한다.


🐧 내일 할 일

0개의 댓글