본문 바로가기
Achaive

Unity, AAB 설치 완료 체크

by Client. DJ 2023. 6. 29.
반응형

AAB 설치 완료 체크

AAB는 총 3가지의 설치 구간(PAD)이 있습니다. (*PAD: Play Asset Delivery)

  1. install-time: '선불'이라고 표현하기도 하며, 최초 설치 시 포함 (1GB)
  2. fast-follow: 설치 완료 시, 백그라운드에서 추가 다운로드 및 설치 진행 (512MB)
  3. on-demand: 유저가 애플리케이션 진입을 하고 나서, 이후에 추가 다운로드 및 설치 진행 (512MB)

위 설치 형태 모두를 합하면 총 2GB(사전 설치 1GB + 추가 설치 1GB)를 지원합니다. 현재 구글 플레이 스토어에 등록하기 위해서는 위해서는 위와 같은 규격을 준수해야하며, 필요에 따라 각 구간 별로 용량 조절이 필요합니다.

 

'install-time'의 형태는 이미 설치하면서 포함되었기에 괜찮지만, 나머지 'fast-follow', 'on-demand'의 경우는 추가 설치의 형태이며, 'fast-follow' 단계에서는 백그라운드 설치 진행 중에도 애플리케이션 진입이 가능하기 때문에, 모두 설치가 되었는지 확인이 필요합니다.

 

대부분의 최초 설치 경우, 에셋 번들 확인으로 구분할 수 있습니다.

스크립트

PlayAssetDeliveryManager.cs

//#define USE_AAB_BUILD
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
#if UNITY_ANDROID && USE_AAB_BUILD
using Google.Play.AssetDelivery;
#endif

public class PlayAssetDeliveryManager : MonoBehaviour
{
    #region static

    private static PlayAssetDeliveryManager _instance;
    public static PlayAssetDeliveryManager instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<PlayAssetDeliveryManager>();
                DontDestroyOnLoad(_instance);
            }
            return _instance;
        }
    }

    #endregion

    #region Inspector
    public string[] assetBundleNames;
    #endregion

    /// <summary>
    /// 추가 설치가 완료된 에셋 번들 리스트
    /// </summary>
    public Dictionary<string, AssetBundle> AssetBundles { get; private set; } = new();

    private void Start()
    {
#if UNITY_ANDROID && USE_AAB_BUILD && !UNITY_EDITOR
        // 에셋팩 설치 여부를 확인한다.
        PatchAsync();
#endif
        // 에셋 번들 설치가 완료되었다면, 다음 씬으로 이동한다. (현재 씬은 최초 씬으로 인덱스가 0번이다. 0 => 1번씬으로 이동)
        SceneManager.LoadScene(1, LoadSceneMode.Single);
    }

#if UNITY_ANDROID && USE_AAB_BUILD
    /// <summary>
    /// 에셋 번들 모두 요청
    /// </summary>
    public void PatchAsync() => StartCoroutine(Patch());

    private IEnumerator Patch()
    {
        Debug.Log("Before calling Patch");
        foreach (string bundleName in assetBundleNames)
        {
            yield return RetrieveAssetBundle(bundleName);
        }
        Debug.Log("After calling Patch");
    }

    /// <summary>
    /// 에셋 번들 요청
    /// 참고 : https://developer.android.com/guide/playcore/asset-delivery/integrate-unity?hl=ko
    /// </summary>
    /// <param name="assetBundleName">에셋 번들 이름</param>
    /// <returns></returns>
    private IEnumerator RetrieveAssetBundle(string assetBundleName)
    {   
        // 에셋 번들 요청
        var handle = PlayAssetDelivery.RetrieveAssetBundleAsync(assetBundleName);
        
        // 다운로드 진행 상황 확인
        Debug.Log($"(before) AsseteBundleRequest '{assetBundleName}' Status => {handle.Status}.");
        while (!handle.IsDone)
        {
            if (handle.Status == AssetDeliveryStatus.WaitingForWifi)
            {
                // 대용량 다운로드의 경우 (150mb 이상), 유저의 동의를 묻는 팝업을 띄운다.
                var userConfirmationOperation = PlayAssetDelivery.ShowCellularDataConfirmation();
                yield return userConfirmationOperation;

                if ((userConfirmationOperation.Error != AssetDeliveryErrorCode.NoError) ||
                    (userConfirmationOperation.GetResult() != ConfirmationDialogResult.Accepted))
                {
                    // 유저가 다운로드 동의하지 않았을 경우 처리 구간
                }

                // 계속 진행하기 전에 Wi-Fi 연결 또는 확인 대화 상자 수락을 기다린다.
                yield return new WaitUntil(() => handle.Status != AssetDeliveryStatus.WaitingForWifi);
            }
            yield return null;
        }
        Debug.Log($"(after) AsseteBundleRequest '{assetBundleName}' Status => {handle.Status}.");

        if (handle.Error != AssetDeliveryErrorCode.NoError)
        {
            Debug.LogError($"AsseteBundleRequest '{assetBundleName}' Error => {handle.Error}");
            yield return null;
        }
        else
        {
            AssetBundle assetBundle = handle.AssetBundle;
            if (assetBundle != null)
            {
            	AssetBundles.Add(assetBundleName, assetBundle);
            }
        }
    }
#endif
}

위와 같은 코드를 유니티 최초 실행 때, 가장 먼저 호출되는 부분에 넣어주어야합니다. 해당 스크립트는 동기적으로 구현했기 때문에 다음 줄의 코드가 호출되지 않습니다.

예제

가장 먼저 실행되는 씬에 하나의 스크립트가 있다.

위와 같이 최초 씬에서 컴포넌트 하나가 사용됩니다. assetPackNames를 확인하고 다음 씬으로 넘어가게 됩니다.

로그의 순서는 아래와 같습니다.

Before calling WaitPatch
'base' AssetBundle is available.
'resources' AssetBundle is available.
'additivescenes' AssetBundle is available.
'videos' AssetBundle is available.
After calling WaitPatch

에셋번들이 세팅되었다면 아래와 같이 사용 가능합니다.

private void Start()
{
    AsseteBundle resources = PlayAssetDeliveryManager.instance.AssetBudles["resources"];
    var obj = resources.LoadAsset("fileName");
}

마무리

에셋이 모두 설치가 되었는지는 확인이 필요하게 되어 작성했습니다. 에셋 번들 설치가 완료되었는지 체크는 비동기적으로 사용할 수 있으므로 'PlayAssetDelivery.RetrieveAssetBundleAsync(assetBundleName)' 호출 방법을 좀 더 고민해보세요. 코루틴을 이용하여 영상이 재생되는 동안 설치가 되었는지 체크 필요할 때 사용하면 자연스러운 흐름 안에서 설치 완료까지 대기가 가능합니다. :)

 

APKs로 뽑아서 테스트하는 경우에 조금의 예외가 생길 수 있는데, CDN처리를 해주는 마켓이 없어서 디바이스 로컬 저장소에 임시로 다운로드 사이트 역활을 해주는 공간을 생성합니다. 해당 공간이 모두 만들어지고 나면, 추가 다운로드가 시작되기에, APKs로 설치하자마자 진입하는 것은 의미가 없으며 애러가 생깁니다. (완성되지 않은 에셋팩을 다운로드함.)

또, 데이터 삭제 등으로 통해, CDN 역할을 해주는 공간이 없어지면 에셋팩을 다운로드할 수 없어서 애러가 생깁니다. 이 점 테스트에 참고 바랍니다. :) 수십번의 테스트를 통해 알게됬네요.


참고 1: https://developer.android.com/guide/app-bundle/asset-delivery?hl=ko

참고 2: https://developer.android.com/guide/playcore/asset-delivery/integrate-unity?hl=ko&language=%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8#asset-type

반응형

댓글