Unity
[Unity] AssetBundle을 활용해 Scene 로드하기
JiHxxn
2024. 7. 9. 22:38
🤔 사용 목적
프로젝트에서 다른 프로젝트의 Scene을 사용해야 할 때, 해당 Scene을 통째로 번들화하여 로드해 사용할 수 있다.
아래 가이드대로 따라하게 되면 게임 시작 버튼 클릭 시, 해당 게임의 에셋 번들을 로드하여 게임을 실행시킬 수 있게 된다.
1. AssetBundle 추출 및 셋팅
1. 번들 화 될 오브젝트가 있는 프로젝트에서 Asset Bundle Browser 패키지
- 설치 Git URL : com.unity.assetbundlebrowser
2. Asset Bundle Browser → Configure 탭에서 번들화 할 오브젝트 추가
- Build 되는 Scene들을 드래그해주면 의존되는 파일들도 자동으로 추가가 된다.
3. Asset Bundle Browser → Build 탭에서 번들화
- B ulid Target, Clear Folders, Compression(LZ4) 확인
4. 번들화 한 AssetBundle을 사용할 프로젝트에 옮기기
Android.manifest 파일의 경우, 프로젝트 내에 한 개만 있으면 충분(플랫폼당 하나)
5. 게임의 스크립트 파일들을 사용할 프로젝트에 옮기기
2. AssetBundle 로드
플랫폼에 따라 StreamingAssets → AssetBundles → ANDROID/IOS 폴더에 에셋번들 넣기
💱 Coroutine 버전
- 버튼 클릭 시 작동되는 함수
public void OnClickGameButton()
{
// LoadGame(게임이름, 게임의 메인 씬 이름)
LoadBundleSceneManager.Instance.LoadGame("multiplicationtable", "MainScene");
}
- 게임 로드
private string gameAssetBundleName;
private string startSceneName;
private bool isCoroutineRunning;
public void LoadGame(string _assetBundelName, string startSceneName)
{
isCoroutineRunning = false;
// 전역변수에 번들이름 담아놓기
gameAssetBundleName = _assetBundelName;
this.startSceneName = startSceneName;
// Empty Scene Load
StopAllCoroutines();
StartCoroutine(LoadEmptyScene(StartLoadingGame));
}
// Scene이동 시 EmptyScene을 거쳐간다.
// 중복 실행을 방지하고, UI를 활성화하고 로딩 패널을 연다.
// EmptyScene 로드가 완료되면, _onEmptySceneLoaded 콜백을 호출한다.
IEnumerator LoadEmptyScene(Action<AsyncOperation> _onEmptySceneLoaded)
{
// 중복 실행 방지
if (isCoroutineRunning)
{
yield break;
}
isCoroutineRunning = true;
//로딩 Panel
UIManager.Instance.OpenGameUIPanel(EUIPanelName.Loding);
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("00_EmptyScene");
asyncOperation.allowSceneActivation = false;
asyncOperation.completed += _onEmptySceneLoaded;
while (asyncOperation.allowSceneActivation == false)
{
if (asyncOperation.progress >= 0.9f && asyncOperation.allowSceneActivation == false)
{
asyncOperation.allowSceneActivation = true;
}
yield return null;
}
}
// EmptyScene 로드가 완료된 후 호출되는 함수.
// 에셋 번들을 로드하고, 해당 번들에 포함된 씬을 찾아서 로드한다.
void StartLoadingGame(AsyncOperation asyncOperation)
{
isCoroutineRunning = false;
// AssetBundle 경로 확인
string assetBundleName = gameAssetBundleName.ToLower();
string path = Path.Combine(Application.streamingAssetsPath, "AssetBundles/ANDROID", assetBundleName);
// AssetBundle 로드
currentAssetBundle = AssetBundle.LoadFromFile(path);
//방어코드
if (currentAssetBundle == null)
{
Debug.Log($"{this.name}::: {currentAssetBundle} = SceneBundle 없음");
}
else
{
string[] sceneNameArray = currentAssetBundle.GetAllScenePaths();
foreach (string sceneName in sceneNameArray)
{
if (sceneName.Contains(startSceneName))
{
// 에셋 번들을 로드하는 코루틴 실행
StartCoroutine(StartLoadingGameRoutine(sceneName));
break;
}
}
}
}
// 실제 씬을 로드하는 코루틴, 로딩 진행도를 UI에 업데이트하고, 로딩이 완료되면 씬을 활성화
IEnumerator StartLoadingGameRoutine(string sceneName)
{
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneName);
asyncOperation.allowSceneActivation = false;
while (!asyncOperation.isDone)
{
//로딩바
UIManager.Instance.SetSliderValue(EUIPanelName.Loding, asyncOperation.progress);
if (asyncOperation.progress >= 0.9f && asyncOperation.allowSceneActivation == false)
{
asyncOperation.allowSceneActivation = true;
}
yield return null;
}
}
🔁 Unitask 버전
[SerializeField] private Image loadingBar;
public async UniTask LoadAppGameAsync()
{
string assetBundleName = "multiplicationtable";
AssetBundle assetBundle;
string platform = "Android";
string type = "Games";
//에셋번들 경로 지정
string path = Path.Combine(Application.streamingAssetsPath, platform, type, assetBundleName.ToLower());
assetBundle = AssetBundle.LoadFromFile(path);
if (assetBundle == null)
{
await UniTask.Yield();
return;
}
// 에셋번들 내의 메인 씬을 불러오는 부분
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("MainScene");
while (!asyncOperation.isDone)
{
await UniTask.Yield();
// 로딩바
loadingBar.fillAmount = asyncOperation.progress;
}
asyncOperation.allowSceneActivation = true;
await UniTask.Delay(100);
asyncOperation.allowSceneActivation = true;
}
public async void OnClickLoadAssetBundle()
{
await LoadAppGameAsync();
}
⤵️ 게임 종료 시 메모리 해제
- 언로딩 (LoadBundleSceneManager.cs)
- 게임 안에서 UnloadGame()을 통하여 다시 MainScene으로 돌아가게 할 수 있다.
- 해당 미니게임 종료 시 반드시 번들을 메모리 해주고, 해당 미니게임에 사용되었던 싱글톤 매니저도 삭제해 준다.
public void UnloadGame()
{
UnloadUnusedAssetBundle();
LoadMainScene();
DestroyManager();
}
public void UnloadUnusedAssetBundle()
{
if (currentAssetBundle != null)
{
currentAssetBundle.Unload(true);
currentAssetBundle = null;
}
}
// 메인 씬을 로드하는 메소드. 빈 씬을 로드하고, 로드가 완료되면 메인 씬 로드
public void LoadMainScene()
{
// Empty Scene Load
StopAllCoroutines();
StartCoroutine(LoadEmptyScene(StartLoadingMain));
}
// 빈 씬 로드 완료 후 메인 씬 로드 시작
void StartLoadingMain(AsyncOperation asyncOperation)
{
StopAllCoroutines();
StartCoroutine(LoadMainRoutine("01_MainScene"));
}
// 메인 씬 로드하는 코루틴, 로딩 진행도 UI에 업데이트하고, 로딩 완료되면 씬 활성화
IEnumerator LoadMainRoutine(string _mainScene)
{
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(_mainScene);
asyncOperation.allowSceneActivation = false;
while (!asyncOperation.isDone)
{
//로딩바
//UIManager.Instance.SetSliderValue(EUIPanelName.Loding, asyncOperation.progress);
if (asyncOperation.allowSceneActivation == false && asyncOperation.progress >= 0.9f)
{
asyncOperation.allowSceneActivation = true;
//UIManager.Instance.OpenGameUIPanel(EUIPanelName.MainLoding);
}
yield return null;
}
//UIManager.Instance.gameObject.SetActive(true);
}
//싱글톤 매니저 삭제
public void DestroyManager()
{
if (destroyQueue != null && destroyQueue.Count > 0)
{
int count = destroyQueue.Count;
for (int i = 0; i < count; i++)
{
GameObject go = destroyQueue.Dequeue();
Destroy(go);
}
}
}