Unity 자습서/Addressable

유니티 Addressable #4 - 씬 로드와 게임오브젝트 Instantiate

개양반 2020. 9. 10.
728x90

오늘은 번들에 올라간 Scene을 로드하는 방법과 게임오브젝트를 로드하고 언로드하는 방법에 배웁니다.

※ Scene을 번들에 올려서 사용하면 Scene에서 게임오브젝트의 위치가 바뀌는 등의 변경이 있을 경우 유저는 APK 를 다시 설치할 필요없이 서버에서 변경된 Scene을 다운받아 변경된 부분이 자연스럽게 패치되도록 할 수 있습니다.


가. 에셋 준비

1. Scene 준비하기

씬을 두개 만듭니다. 이름을 StartScene, CubeScene 으로 변경합니다.  

 

1-1 StartScene

아래와 같이 버튼을 추가합니다. 

 

1-2 CubeScene 

CubeScene을 열고 Cube를 대충 설치합니다. MainCamera에 있는 Audio Listener 를 삭제합니다. 

CubeScene의 Addressable을 활성화하고 주소를 CubeScene으로 변경합니다.

 

1-3 SampleScene

이전 강좌에서 사용했던 SampleScene을 엽니다. 버튼 두개를 추가합니다.

SampleScene의 Addressable을 활성화하고 주소를 변경합니다.

 

나. Sphere 오브젝트 만들기

아무 씬에서 Sphere 게임 오브젝트를 만들고 프리팹으로 만듭니다. Addressable을 활성화하고 주소를 변경합니다. Hirarchy창에 있는 Sphere는 삭제합니다.


나. 스크립트 작성

1. NextScene.cs 생성 후 작성

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;

public class NextScene : MonoBehaviour
{
    public string NextSceneAddress;
  

    public void OnNextScene()
    {

        // 매개변수에는 String 뿐만 아니라
        // public AssetReference reference; 등 키를 전달할 수 있으면 된다.
        Addressables.LoadSceneAsync(NextSceneAddress);

    }
}

#주요 코드

Addressables.LoadSceneAsync(Key); 에셋번들에 올라간 씬을 로드하는 코드입니다.

 

2.  SceneLoadAndObjectInstantiate.cs 생성 후 작성

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;

public class SceneLoadAndObjectInstantiate : MonoBehaviour
{
    public AssetReference addSceneReference;
    public AssetReferenceGameObject sphereReference;


    bool isSphereLoad = false;
    SceneInstance m_LoadedScene;
    List<GameObject> sphereGameObjects = new List<GameObject>();


    // Scene 관련 코드--------------
    // 버튼 클릭 이벤트
    public void OnSceneAction()
    {
        if (m_LoadedScene.Scene.name == null)
        {
            Addressables.LoadSceneAsync(addSceneReference, LoadSceneMode.Additive).Completed += OnSceneLoaded;
        }
        else
        {
            Addressables.UnloadSceneAsync(m_LoadedScene).Completed += OnSceneUnloaded;
        }
    }

    private void OnSceneUnloaded(AsyncOperationHandle<SceneInstance> obj)
    {
        switch (obj.Status)
        {
            case AsyncOperationStatus.Succeeded:
                m_LoadedScene = new SceneInstance();
                break;
            case AsyncOperationStatus.Failed:
                Debug.LogError("씬 언로드 실패: " + addSceneReference.AssetGUID);
                break;
            default:
                break;
        }
    }

    private void OnSceneLoaded(AsyncOperationHandle<SceneInstance> obj)
    {
        switch (obj.Status)
        {
            case AsyncOperationStatus.Succeeded:
                m_LoadedScene = obj.Result;
                break;
            case AsyncOperationStatus.Failed:
                Debug.LogError("씬 로드 실패: " + addSceneReference.AssetGUID);
                break;
            default:
                break;
        }
    }



    // Sphere 관련 코드--------------
    // 버튼 클릭 이벤트
    public void OnSphereAction()
    {
        if (isSphereLoad == false)
        {
            // sphereReference.LoadAssetAsync().Completed += op => {} 람다식으로 처리할 수도 있다.
            sphereReference.LoadAssetAsync().Completed += OnSphereLoaded;
            isSphereLoad = true;
        }
        else
        {
            // sphereReference.InstantiateAsync 형태로 인스턴트화 했다면
            // sphereReference.ReleaseInstantiate(해당 게임오브젝트) 로 릴리즈한다.
            // sphereReference.InstantiateAsync 로 했는데 sphereReference.ReleaseAsset();를 호출하면
            // 해당 개체는 메모리에 올라간 것이 없다고 경고가 출력된다.
            sphereReference.ReleaseAsset();
            DestroySphere();
            isSphereLoad = false;
        }
    }

    private void OnSphereLoaded(AsyncOperationHandle<GameObject> obj)
    {
        switch (obj.Status)
        {
            case AsyncOperationStatus.Succeeded:
                InstantiateObject(obj.Result);
                break;
            case AsyncOperationStatus.Failed:
                Debug.LogError("OnSphereLoaded Failed");
                break;
            default:
                break;
        }
    }

    void InstantiateObject(GameObject obj)
    {
        for (int i = 0; i < 5; i++)
        {
            Vector3 rndVector = new Vector3(UnityEngine.Random.Range(-3, 3), UnityEngine.Random.Range(-3, 3), 0);
            sphereGameObjects.Add(Instantiate(obj, rndVector, Quaternion.identity));
        }
    }

    void DestroySphere()
    {
        for (int i = sphereGameObjects.Count -1; i >= 0; i--)
        {
            Destroy(sphereGameObjects[i]);
        }
    }
}

#주요 코드

public AssetReferenceGameObject sphereReference; 게임오브젝트로 된 에셋만 참조할 수 있는 변수입니다.

SceneInstance m_LoadedScene; 로드된 씬의 인스턴트를 참조시킬 변수입니다.

Addressables.LoadSceneAsync(addSceneReference, LoadSceneMode.Additive) 에서 LoadSceneMode.Additive는 씬을 새로 열지 않고 현재 열려있는 씬에다가 추가로 연다는 의미입니다. addSceneReference는 로드할 씬의 Key를 입력합니다.

Addressables.UnloadSceneAsync(m_LoadedScene).Completed += OnSceneUnloaded; 해당 씬을 언로드할 때 사용하는 코드입니다. Unload를 통해 메모리에 올라간 해당 에셋을 내립니다.


다. 컴포넌트 설정

1. StartScene 

StartScene 을 열고 빈 게임오브젝트를 만듭니다. 해당 오브젝트에 NextScene.cs를 컴포넌트로 추가하고 변수에 Main을 입력합니다. 그리고 앞에서 만든 버튼에 NextScene.cs의 OnNextScene()가 호출되도록 연결합니다.

2. SampleScene

SampleScene을 열고 Loader 오브젝트에 SceneLoadAndObjectInstantiate.cs 를 연결합니다. 다른 스크립트는 비활성화시킵니다. Cube Action, Sphere Action 버튼에 클릭 이벤트를 연결합니다.


라. 테스트하기

StartScene을 열고 재생버튼을 눌러 테스트합니다. 각 버튼을 누를때마다 Addressable Event Viewer의 정보가 어떻게 바뀌는지 확인합니다.

 

https://paypal.me/Mrbinggrae?locale.x=ko_KR

블로그를 후원해주시면 더욱 열심히 좋은 자료를 만들겠습니다.

댓글

💲 추천 글