2022. 7. 26. 14:03ㆍ유니티 unity
유니티 Addressable에 대해서 알아보겠습니다.
콘텐츠를 빌드하고 로드하는 시스템입니다.
기존에는 리소스 폴더를 가 있는데
빌드 사이즈가 커지는 문제와 앱 로딩 시간이 길어지는 문제 때문에
에셋번들이 생겼는데
에셋번들 하드코딩을 해야 하는 단점과 어렵다는 단점이 있어서 그것을 보완하기 위해서
어드레서블이 생겼습니다.
에셋번들을 좀 더 쉽게 사용하기 위해서 만들어진 유틸이라고 생각하시면 됩니다.
유니티 버전 2018.2 이상부터 사용이 가능
모든 동작이 비동기로 구현하기 때문에 잘 생각하시면서 사용해야 합니다.
설치
Package manager에서 설치
실행
이렇게 사용하면 끝이 납니다.
플레이 모드 설정하는 칸인데
Use Asset Database 랑 Simulate Groups는 에셋 번들을 만들지 않았어도 에셋번들을 만든 것처럼 작동하는 거고
여기서 문제가 없어도 실제 런타임에서 문제가 생길 수도 있습니다
Use Existing Build는 실제로 빌드하고 작동하는 것처럼 나오기 때문에
실제 빌드와 동일한 환경에서 테스트하고 싶을 때는 이것을 체크합니다.
어드레서블을 확인하기 위해서 게임 오브젝트를 만들겠습니다
5개 오브젝트를 만들어서
폴더로 넣어서 프리팹화 시킵니다.
addressable에 체크해서 어드레서블에 올립니다. 옆에 주소같이 써있는거는 말 그대로 어드레서블의 주소 이름이라고 생각하시면 편합니다.
5개 전부 이렇게 수정했습니다.
어드레서블 그룹을 보시면 체크한 오브젝트들이 포함되어있습니다.
Labels을 만들어서 같은 그룹에 포함시킬 수 있습니다.
이제 게임에서 불러오는 방법에 대해서 알아보겠습니다.
2개의 버튼을 만들었습니다.
스크립트 하나를 만듭니다.
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class Addressable_Test1 : MonoBehaviour
{
public AssetReference aref;
public void Btn1()
//변수로 가져와서 불러오기
{
Addressables.LoadAssetAsync<GameObject>(aref).Completed += (op) =>
{
if (op.Status != AsyncOperationStatus.Succeeded)
{
return;
}
Instantiate(op.Result, new Vector3(0, 1, 0), Quaternion.identity);
};
}
public void Btn2()
//직접 주소를 넣기
{
Addressables.LoadAssetAsync<GameObject>("Test/Black").Completed += (op) =>
{
if (op.Status != AsyncOperationStatus.Succeeded)
{
return;
}
Instantiate(op.Result, new Vector3(0, 1, 0), Quaternion.identity);
};
}
}
변수로 쓰게 되면 따로 넣어줘야 합니다
LoadAssetAsync로 어드레서블을 비동기로 불러와서
op.Result로 결괏값을 가져옵니다
메모리상으로 올라왔으니 이걸 내리고 싶다면
Addressables.Release(op);
로 내릴 수 있습니다.
InstantiateAsync이라는 함수를 이용하면 게임 오브젝트를 바로 생성시킬 수 있습니다.
public void Btn1()
//변수로 가져와서 불러오기
{
// Addressables.LoadAssetAsync<GameObject>(aref).Completed += (op) =>
// {
// if (op.Status != AsyncOperationStatus.Succeeded)
// {
// return;
// }
//
// Instantiate(op.Result, new Vector3(0, 1, 0), Quaternion.identity);
// };
aref.InstantiateAsync(new Vector3(0, 1, 0), Quaternion.identity);
}
어드레서블을 이용하면 원하는 거만 메모리에 올리고
이제 사용이 필요가 없다면 메모리를 해제해서 매우 유용하게 사용할 수 있습니다.
그리고 번들을 로컬이 아닌 서버에서 다운로드하여서 사용이 가능합니다.
이것을 활용한다면 서버에 있는 번들만 바꿔주면 되니까 빌드 없이 번들 안에 있는 내용들을 바꿀 수 있습니다.
서버에 리소스들을 넣어서 용량을 매우 크게 감소를 할 수 도 있고
빌드 없이도 패치를 할 수 있다는 매우 큰 장점이 있습니다.
이런 식으로 빌드 없이도 추가 패치를 할 수 있습니다.
이번에는 예시로
게임 시작 버튼을 눌러서 다운로드해야 할 용량을 보여주고 다운로드를 하게 되면
로딩바 % 를 보여줘서 현재 상황을 알려주는 간단한 구현과
빌드 없이 번들만 바꿔서 패치를 하는 방법을 보여드리겠습니다.
음악 5개를 추가해서 넣었습니다 라벨을 Test로 하나로 통일했습니다.
무료 사이트 https://www.bensound.com/free-music-for-videos
해당 번들을 가지고 있는지 없는지 확인을 하려면
GetDownloadSizeAsync로 확인 가능하고
파일 하나씩도 가능하고 Lable로 묶어서 여러 개로 한 번에 확인이 가능합니다.
파일이 없다면 DownloadDependenciesAsync를 이용해서 다운로드합니다
만약 파일이 있는 상태에서 DownloadDependenciesAsync를 사용하면 추가로 다운로드하지 않고 넘어갑니다.
번들 하나가 수정이 된다면 전체 다시 다운로드가 아니라 수정된 번들만 따로 받습니다.
라벨 또한 변수로 가져올 수 있습니다.
public AssetLabelReference 변수명;
이것을 활용해서 스크립트를 하나 만들겠습니다.
using System.Collections;
using TMPro;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Random = UnityEngine.Random;
public class Addressable_Test1 : MonoBehaviour
{
public AssetReference[] obs; // 게임오브젝트들
public AssetReference[] musics; // 음악
public AudioSource audioSource; // 오디오소스
public GameObject StartPanel; //UI
public GameObject DownPanel; //UI
public GameObject LoadingPanel; // UI
private AsyncOperationHandle handle;
public TextMeshProUGUI byteText; //다운로드 사이즈
public Slider slider; // 로딩%
public TextMeshProUGUI perText; //퍼센트 Text
public void StartBtn()
//시작버튼 누름
{
StartCoroutine(StartFunc());
StartPanel.SetActive(false);
}
IEnumerator StartFunc()
{
AsyncOperationHandle<long> getDownloadSize = Addressables.GetDownloadSizeAsync("Test");
//Test 이라는 라벨 다운로드사이즈 확인
yield return getDownloadSize;
if (getDownloadSize.Result > 0)
//다운로드 사이즈가 0 이상이라면 다운받아야하기 때문에 다운로드UI 표시
{
DownPanel.SetActive(true);
byteText.text=$"{getDownloadSize.Result} Byte";
}
else
{
//아니라면 다음으로 넘어간다.
NextShow();
}
//메모리 내림
Addressables.Release(getDownloadSize);
}
public void DownBtn()
//다운로드 시작 버튼 누름
{
StartCoroutine(DownFunc());
DownPanel.SetActive(false);
}
IEnumerator DownFunc()
{
handle = Addressables.DownloadDependenciesAsync("Test");
//Test 라벨을 다운로드 받습니다.
StartCoroutine(Show());
//로딩바 보여줌
yield return handle;
yield return new WaitForSeconds(1);
//1초대기
LoadingPanel.SetActive(false);
NextShow();
//다음으로 넘어감
//메모리 해제
Addressables.Release(handle);
}
public void NextShow()
{
Addressables.InstantiateAsync(obs[0], new Vector3(-5, 0, 0), quaternion.identity);
Addressables.InstantiateAsync(obs[1], new Vector3(-2.5f, 0, 0), quaternion.identity);
Addressables.InstantiateAsync(obs[2], new Vector3(0, 0, 0), quaternion.identity);
Addressables.InstantiateAsync(obs[3], new Vector3(2.5f, 0, 0), quaternion.identity);
Addressables.InstantiateAsync(obs[4], new Vector3(5, 0, 0), quaternion.identity);
//오브젝트 소환
int ran = Random.Range(0,musics.Length);
//랜덤으로 뮤직 하나 선택
Addressables.LoadAssetAsync<AudioClip>(musics[ran]).Completed += (op) =>
{
if (op.Status != AsyncOperationStatus.Succeeded)
{
return;
}
audioSource.clip = op.Result;
audioSource.Play();
//뮤직 스타트
};
}
IEnumerator Show()
//다운로드 % 보여주는 함수
{
LoadingPanel.SetActive(true);
yield return new WaitUntil(() => handle.IsValid());
while (handle.PercentComplete<1)
{
slider.value = handle.PercentComplete;
perText.text=$"{handle.PercentComplete*100:F2}%";
yield return null;
}
slider.value = 1;
perText.text = "100%";
}
}
이제 번들을 다운로드할 주소만 따로 넣는다면 끝입니다.
서버를 선택해야 하는데
로컬 서버를 하시 거나
아마존
파이어 베이스
구글 클라우드
등등
여러 가지 있습니다
해당 다운로드할 링크만 있다면 어떤 것도 다 가능합니다.
여기서 파이어 베이스는
주소가 https://가 아닌 gs:// 로 시작해서 따로 세팅을 해줘야 합니다
저는 무료인 구름 ide를 사용하겠습니다.
예전에 어드레서블을 사용할 때는 파이어 베이스를 사용을 했었는데
이번에는 구름 ide가 최근에 무료 플랜도 항상 켜 두기를 사용이 가능해서 사용하기 편하더라고요 가입절차도 복잡하지 않습니다
다른 클라우드 사용하시는 분들은 번들을 넣고 링크만 있으면 됩니다.
구름ide
항상 켜 두기를 사용하지 않으면 일정 시간이 지나면 자동으로 꺼집니다.
구름 ide를 켜놓고 다시 유니티로 돌아갑니다 서버에서 다운받을려면 몇 가지 설정을 해야 합니다.
전 에셋 번들을 테스트하기 위해서 설정했습니다.
이름을 Remote로 설정합니다 원하는 이름으로
Custom으로 설정해야 하고
Remote.LoadPath를 서버링크를 넣으시면 됩니다.
[BuildTarget]라는 게 나오는데
PC는 StandalonWindows64 폴더
안드로이드는 Android라는 폴더로 설정됩니다.
임시로 http://서버주소/[BuildTarget] 로 설정합니다
Path Preview를 보시면
/StandaloneWindows64 가 따로 넣어졌습니다.
이제 설정이 끝났으면 다시 서버로 돌아가서 링크를 복사합니다.
구름ide로 가서 폴더를 만듭니다.
전 CDN 폴더 안에 넣었습니다.
주소를 복사하고 뒤에 컨테이너 이름과 폴더를 넣습니다
전 컨테이너 이름은 addressable 이니까
https://addressable-ofpne.run.goorm.io/addressable/CDN 입니다.
마지막으로 확인해봅니다
이제 빌드를 해봅시다
기존 빌드 설치 폴더를 안 건드셨다면 본인 프로젝트 폴더에 가셔서 ServerData라는 폴더를 찾습니다
이 파일들을 이제 서버에 올리면 됩니다.
구름 ide는 따로 서버를 켜줘야 하는데
service apache2 start를 쳐줍시다
한번 주소창에 실행해서 서버가 켜졌나 확인해봅니다
이제 빌드를 하고 실행을 해보면 잘 나옵니다.
C:\Users\사용자이름\AppData\LocalLow\Unity
잘 들어와 있습니다.
파일을 지우고 싶으면 폴더를 지우거나
에디터상에서
Caching.ClearCache();
를 실행하면 됩니다.
이번엔 번들의 파일을 수정하고 빌드 없이 실행해보겠습니다.
폴더를 삭제하고
테스트를 위해서 에셋스토어에서 아무거나 다운받으세요
https://assetstore.unity.com/packages/3d/characters/humanoids/zombie-30232
빌드 없이 패치가 되었습니다.
Player Version Override 에다가 이름을 적으면 빌드시
이런 식으로 나옵니다.
어드레서블을 활용해서 앱 용량을 크게 줄일 수 있습니다.
씬도 어드레서블로 저장이 가능하니까 활용을 잘하면 정말 괜찮은 기능인거 같습니다.
그리고 이번에 서버를 구름 ide를 써봤는데
구름ide 항상 켜 두기가 무료 플랜에서도 사용이 가능해져서 좋네요 개인프로젝트나 테스트용도로는 정말 좋은 선택이라고 생각이 듭니다.
깃허브주소
https://github.com/wolstar415/unity_Addressables
스크립트보기
using System;
using System.Collections;
using TMPro;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Random = UnityEngine.Random;
public class Addressable_Test1 : MonoBehaviour
{
public AssetReference[] obs; // 게임오브젝트들
public AssetReference[] musics; // 음악
public AudioSource audioSource; // 오디오소스
public GameObject StartPanel; //UI
public GameObject DownPanel; //UI
public GameObject LoadingPanel; // UI
private AsyncOperationHandle handle;
public TextMeshProUGUI byteText; //다운로드 사이즈
public Slider slider; // 로딩%
public TextMeshProUGUI perText; //퍼센트 Text
// private void Start()
// {
// Caching.ClearCache();
// }
public void StartBtn()
//시작버튼 누름
{
StartCoroutine(StartFunc());
StartPanel.SetActive(false);
}
IEnumerator StartFunc()
{
AsyncOperationHandle<long> getDownloadSize = Addressables.GetDownloadSizeAsync("Test");
//Test 이라는 라벨 다운로드사이즈 확인
yield return getDownloadSize;
if (getDownloadSize.Result > 0)
//다운로드 사이즈가 0 이상이라면 다운받아야하기 때문에 다운로드UI 표시
{
DownPanel.SetActive(true);
byteText.text = $"{getDownloadSize.Result} Byte";
}
else
{
//아니라면 다음으로 넘어간다.
NextShow();
}
//메모리 내림
Addressables.Release(getDownloadSize);
}
public void DownBtn()
//다운로드 시작 버튼 누름
{
StartCoroutine(DownFunc());
DownPanel.SetActive(false);
}
IEnumerator DownFunc()
{
handle = Addressables.DownloadDependenciesAsync("Test");
//Test 라벨을 다운로드 받습니다.
StartCoroutine(Show());
//로딩바 보여줌
yield return handle;
yield return new WaitForSeconds(1);
//1초대기
LoadingPanel.SetActive(false);
NextShow();
//다음으로 넘어감
//메모리 해제
Addressables.Release(handle);
}
public void NextShow()
{
Addressables.InstantiateAsync(obs[0], new Vector3(-5, 0, 0), quaternion.identity);
Addressables.InstantiateAsync(obs[1], new Vector3(-2.5f, 0, 0), quaternion.identity);
Addressables.InstantiateAsync(obs[2], new Vector3(0, 0, 0), quaternion.identity);
Addressables.InstantiateAsync(obs[3], new Vector3(2.5f, 0, 0), quaternion.identity);
Addressables.InstantiateAsync(obs[4], new Vector3(5, 0, 0), quaternion.identity);
//오브젝트 소환
int ran = Random.Range(0, musics.Length);
//랜덤으로 뮤직 하나 선택
Addressables.LoadAssetAsync<AudioClip>(musics[ran]).Completed += (op) =>
{
if (op.Status != AsyncOperationStatus.Succeeded)
{
return;
}
audioSource.clip = op.Result;
audioSource.Play();
//뮤직 스타트
};
}
IEnumerator Show()
//다운로드 % 보여주는 함수
{
LoadingPanel.SetActive(true);
yield return new WaitUntil(() => handle.IsValid());
while (handle.PercentComplete < 1)
{
slider.value = handle.PercentComplete;
perText.text = $"{handle.PercentComplete * 100:F2}%";
yield return null;
}
slider.value = 1;
perText.text = "100%";
}
}
'유니티 unity' 카테고리의 다른 글
unity(유니티) vector 함수 전부 알기 (0) | 2022.08.03 |
---|---|
Unity로 node.js WebSocket 통신하기 (socket.io,Mysql 이용해서 붐버맨 만들기)-4 (0) | 2022.07.30 |
Unity로 node.js WebSocket 통신하기 (socket.io,Mysql 이용해서 오목게임 만들기)-3 (0) | 2022.07.25 |
Unity로 node.js WebSocket 통신하기 (socket.io를 이용해서 룸형식 채팅방 구현)-2 (4) | 2022.07.23 |
unity ToolKit 와 UI Builder 이용해서 커스텀에디터만들기(응용편 rpg 아이템에디터)-3 (0) | 2022.07.21 |