Addressables Assets 연동: 무거운 장비, 스킬 VFX, 몬스터 모델의 동적 메모리 로딩/해제

Addressables Assets 연동: 무거운 장비, 스킬 VFX, 몬스터 모델의 동적 메모리 로딩/해제

Unity Addressable Asset System을 사용하여 고용량 3D 몬스터 모델, 스킬 VFX, 장비 에셋을 비동기로 로드하고 참조 카운팅 기반으로 메모리를 해제하는 런타임 구조를 구현합니다.

1. 런타임 리소스 관리의 한계와 Addressables 도입 이유

대규모 3D 모바일 및 PC 게임을 개발할 때 고화질 몬스터 모델, 화려한 스킬 VFX, 다양한 장비 아이템은 메인 메모리(RAM)와 비디오 메모리(VRAM)를 가장 많이 차지하는 원인입니다. 기존의 Resources.Load 방식을 사용하면 앱 실행 시점에 자산 인덱스가 메모리에 전수 로드되며 Resources 폴더 내부 에셋이 모두 빌드에 포함되어 초기 앱 용량이 비대해지는 문제가 발생합니다. 또한 직관적인 직접 참조(Direct Reference) 방식은 씬 인스펙터에 연결된 에셋이 씬 전환 전까지 메모리에서 누수되는 원인이 됩니다.

Addressables Asset System은 에셋의 물리적 위치와 상관없이 ‘주소(Address)‘를 기반으로 비동기 로딩을 수행하게 해줍니다. 에셋 번들 파이프라인을 효율적으로 관리할 수 있으며 참조 카운팅(Reference Counting) 기법으로 더 이상 사용하지 않는 자산을 명시적으로 해제할 수 있습니다.

Addressables 비동기 로딩과 참조 카운트 기반 메모리 관리 개요

2. 참조 카운팅 동작 원리와 메모리 모델

Addressables 메모리 관리의 핵심은 참조 카운팅(Reference Counting)입니다. 특정 에셋이나 프리팹을 로드하거나 인스턴스화할 때 참조 카운트가 1 증가하고 해제 메서드를 호출할 때 1 감소합니다. 해당 에셋에 대한 참조 카운트가 0에 도달하면 Addressables는 해당 자산과 관련된 에셋 번들을 메모리에서 Unload 처리합니다.

프로젝트의 런타임 총 메모리 점유량은 다음과 같은 수식 구조로 나타낼 수 있습니다.

Mtotal=Mbase+i=1n(Masseti×Ci)M_{total} = M_{base} + \sum_{i=1}^{n} \left( M_{asset_i} \times C_i \right)

여기서 MbaseM_{base}는 씬의 필수 기본 메모리이고 MassetiM_{asset_i}는 동적으로 로드된 개별 에셋(몬스터, VFX, 장비)의 크기이며 CiC_i는 해당 에셋의 현재 참조 카운트 상태(Ci>0C_i > 0일 때 메모리 유지)를 의미합니다.

3. 동적 자산 로더 매니저 설계

비동기 로딩 작업 핸들(AsyncOperationHandle)을 체계적으로 추적하지 않으면 참조 카운트가 꼬여 메모리 누수나 널 참조 예외가 발생합니다. 아래는 몬스터 모델과 VFX를 주소 기반으로 동적 생성하고 관리하는 싱글톤 핸들러 예제 코드입니다.

using System.Collections me;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class AddressableManager : MonoBehaviour
{
    private Dictionary<string, AsyncOperationHandle> _loadedAssetHandles = new Dictionary<string, AsyncOperationHandle>();

    public async Awaitable<GameObject> SpawnInstanceAsync(string address, Vector3 position, Quaternion rotation)
    {
        AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync(address, position, rotation);
        GameObject instance = await handle.Task;

        if (handle.Status == AsyncOperationStatus.Succeeded)
        {
            return instance;
        }

        Debug.LogError($"Failed to instantiate Addressable asset: {address}");
        return null;
    }

    public void ReleaseInstance(GameObject instance)
    {
        if (instance == null) return;
        Addressables.ReleaseInstance(instance);
    }
}

다음 흐름도는 몬스터가 씬에 생성되고 전투 중 스킬 VFX를 재생한 뒤 사망하여 메모리가 해제되는 과정 전체를 보여줍니다.

flowchart TD
    A["몬스터 스폰 요청"] --> B{"Addressables 로드 여부"}
    B -- "미로드 상태" --> C["InstantiateAsync 실행 & Ref Count 증가"]
    B -- "이미 로드됨" --> D["메모리 에셋 재사용 & Ref Count 증가"]
    C --> E["몬스터 및 VFX 씬 생성"]
    D --> E
    E --> F["스킬 VFX 파티클 재생 완료"]
    F --> G["VFX ReleaseInstance 호출 & Ref Count 감소"]
    E --> H["몬스터 사망 및 Despawn"]
    H --> I["몬스터 ReleaseInstance 호출"]
    I --> J["Ref Count 0 도달 시 에셋 번들 메모리 해제"]

4. 장비 및 VFX 라이프사이클 최적화

무거운 3D 장비 모델과 파티클 VFX는 런타임에 자주 생성되고 파괴됩니다. 단순히 Object.Destroy()를 호출하면 씬에서 게임 오브젝트만 제거될 뿐 자산이 참조하던 메시와 텍스처는 메모리에 그대로 남아있게 됩니다.

장비 교체 시에는 이전 장비의 인스턴스를 Addressables.ReleaseInstance()로 정합성 있게 해제한 뒤 새 장비를 로드해야 합니다. 스킬 VFX의 경우 파티클 재생이 끝나는 시점을 감지하여 자동으로 인스턴스를 반환하는 컴포넌트를 부착하는 방식이 안전합니다.

using UnityEngine;
using UnityEngine.AddressableAssets;

[RequireComponent(typeof(ParticleSystem))]
public class AutoReleaseVFX : MonoBehaviour
{
    private ParticleSystem _particleSystem;

    private void Awake()
    {
        _particleSystem = GetComponent<ParticleSystem>();
    }

    private void Update()
    {
        if (!_particleSystem.IsAlive(true))
        {
            Addressables.ReleaseInstance(gameObject);
        }
    }
}

Addressables Event Viewer를 통한 런타임 참조 카운트 디버깅

5. 메모리 누수를 방지하는 실전 체크리스트

Addressables를 연동할 때 흔히 발생하는 실수를 예방하기 위해 다음 지침을 준수해야 합니다.

  1. Destroy()ReleaseInstance() 구별: Addressables로 생성한 객체는 일반 Destroy() 대신 반드시 Addressables.ReleaseInstance()를 사용해야 참조 카운트가 정상 감소합니다.
  2. 에셋 번들 입도(Granularity) 조절: 몬스터 하나당 개별 에셋 번들로 쪼갤지, 지역 단위로 묶을지 정해야 합니다. 너무 크게 묶으면 작은 에셋 하나 때문에 전체 번들이 메모리에 남고 너무 작게 쪼개면 HTTP 요청 횟수가 늘어납니다.
  3. 프로파일러 모니터링: Addressables Event Viewer를 활성화하여 런타임 중 에셋의 Ref Count 변화와 레퍼런스 그래프를 주기적으로 확인합니다.
#Unity#Addressables#MemoryManagement#VFX#Optimization

계속 읽어보기

이런 글은 어떠세요?

< Back to Logs