Package Manager
에서 Addressables
을 임포트한다.
메뉴바의 Window - Asset Management - Addressables - Group
을 눌러 Addressables Groups
창을 연다.
Addressables Groups
창의 Profile - Manage Profiles
를 선택해 Addressables Profiles
창을 연다.
해당 창에서는 서버에서 에셋을 다운로드 할 때 사용할 서버 주소를 입력할 수 있다.
Addressables Groups
의 기본 로컬 그룹(Defalult Local Group(Default)
) 인스펙터 창에서 Cache Clear Behaviour
를 Clear When New Version Loaded
선택
이후 Inspect Top Level Settings
를 클릭해 다음과 같이 세팅
Unique Bundle IDs
체크(기존에 변경점이 없는 번들들은 무시하게 됨)Send Profiler Events
체크(이벤트 뷰어를 활성화해 상황 확인 가능)Build Remote Catalog
체크(카탈로그 사본을 생성해 서버에 업로드)Build & Load Paths
를 Remote
로 설정다시 기본 로컬 그룹에 들어와 다음과 같이 세팅
Build & Load Paths
를 Remote
로 설정프리팹과 해당 프리팹이 사용하는 머터리얼들의 Addressable
을 체크하여 어드레서블 애셋으로 등록
그룹 및 라벨로 에셋 분류 가능
원하는 오디오 파일 혹은 이미지 파일의 Addressable
을 체크하여 어드레서블 에셋으로 등록 가능
스크립트에 AssetReference
형식으로 프리팹들을 참조할 수 있다.
AssetRefernceGameObject
: 게임 오브젝트 형식AssetReferenceSprite
: 2D SpriteAssetReferenceT<>
: 제네릭 형식이외에 다양한 형식이 존재한다.
이후 인스펙터 창에서 원하는 프리팹을 목록 중 선택, 그룹창, 폴더에서 드래그 등으로 넣는다.
public class AddressableMananger : MonoBehaviour
{
[SerializeField] private AssetReferenceGameObject cubeObj;
[SerializeField] private AssetReferenceGameObject[] buildingObjs;
[SerializeField] private AssetReferenceT<AudioClip> soundBGM;
[SerializeField] private AssetReferenceSprite flagSprite;
// 추후 Release해줄 때 사용하기 위한 오브젝트 레퍼런스 리스트
private List<GameObject> _objects = new List<GameObject>();
[SerializeField] private AudioSource bgmObj;
[SerializeField] private Image flagImage;
// Start is called before the first frame update
void Start()
{
StartCoroutine(InitAddressable());
}
private IEnumerator InitAddressable()
{
var init = Addressables.InitializeAsync();
yield return init;
}
public void Button_SpawnObject()
{
// 단일 게임오브젝트 생성
cubeObj.InstantiateAsync().Completed += (obj) =>
{
_objects.Add(obj.Result);
};
// 다중 게임오브젝트 생성
for (int i = 0; i < buildingObjs.Length; i++)
{
buildingObjs[i].InstantiateAsync().Completed += (obj) =>
{
_objects.Add(obj.Result);
};
}
soundBGM.LoadAssetAsync().Completed += (clip) =>
{
bgmObj.clip = clip.Result;
bgmObj.loop = true;
bgmObj.Play();
};
flagSprite.LoadAssetAsync().Completed += (img) =>
{
flagImage.sprite = img.Result;
};
}
}
Addressables Groups
창에서 Build 0 New Build - Default Build Script
실행
빌드를 마친 후 Play Mode Script - Use Asset Database
선택
게임을 실행한 후 Button_SpawnObject()
함수 실행 시 미리 지정해둔 오브젝트를 실행할 수 있음
위의 클래스에 Release
함수를 추가해준다.
public void Button_Release()
{
// LoadAssetAsync로 로드한 애셋을 Release해준다
soundBGM.ReleaseAsset();
flagSprite.ReleaseAsset();
if (_objects.Count == 0)
return;
var index = _objects.Count - 1;
for (int i = _objects.Count - 1; i > -1; i--)
{
// InstantiateAsync로 생성한 오브젝트들을 Release해준다
Addressables.ReleaseInstance(_objects[i]);
_objects.RemoveAt(i);
}
}
미리 로컬에서 위와 같이 에셋을 추가 및 설정한 후 서버를 적용할 수 있다
1.AWS S3의 버킷을 만든다.
2.버킷 페이지에 들어가 다음과 같이 설정해준다.
*
를 입력, 'Actions'에서는 'GetObject'선택"Resource"
구문의 마지막에 /*
추가3.아무 파일을 하나 업로드한 후 해당 파일의 객체 URL 주소를 복사한다.
1.Remote Profile을 다음과 같이 설정한다.
Addressable Profile
의 Remote.LoadPath
에 붙여넣은 후 파일 이름을 [BuildTarget]
으로 변경해준다.2.Addressable Group
들의 Build & Load Paths
가 Remote
인지, Path Preview
로 보이는 경로가 올바른지 확인한다. 시스템 세팅에서도 변경되었는지 확인한다.
3.AWS S3에 올린 파일을 삭제한다.
1.Addressable Groups
의 Build
- Clear Build Cache
- All
을 선택해 이전 빌드 캐시 파일들을 삭제한다.
2.프로젝트의 파일 탐색기에서 Assets
- ServerData
내에 있는 Standalone...
파일을 삭제한다.
3.Addressable Groups
의 Build
- New Build
- Default Build Script
로 재빌드
파일 탐색기의 Assets
- ServerData
내에 있는 Standalone...
폴더를 AWS S3에 추가
Addressable Groups
의 Play Mode Script
를 Use Existing Build
로 선택해 서버에서 데이터를 받아오게 변경
C:\Users\유저_이름\AppData\LocalLow\Unity\프로젝트_이름
에 다운로드 된다.
테스트를 위해 다운로드 파일을 삭제하고 싶을 시 해당 폴더를 삭제하면 된다.
어드레서블 에셋의 다운로드 필요 여부를 확인하고 만약 필요하면 다운로드를 실행하는 씬의 매니저
public class DownloadManager : MonoBehaviour
{
[Header("UI")]
public GameObject waitMessage;
public GameObject downMessage;
public Slider downSlider;
public TMP_Text sizeText;
public TMP_Text downValText;
[Header("Label")] // 다운로드받을 애셋들의 라벨
public AssetLabelReference defaultLabel;
public AssetLabelReference matLabel;
// 다운받을 애셋들의 파일 크기
private long _patchSize;
// 각각의 애셋들의 파일 크기
private Dictionary<string, long> _patchMap = new Dictionary<string, long>();
// Start is called before the first frame update
void Start()
{
downMessage.SetActive(false);
waitMessage.SetActive(true);
StartCoroutine(InitAddressable());
StartCoroutine(CheckUpdateFiles());
}
// 어드레서블을 초기화해주는 코루틴
private IEnumerator InitAddressable()
{
var init = Addressables.InitializeAsync();
yield return init;
}
// 업데이트할 파일을 체크하는 코루틴
// 각 라벨별로 다운로드할 데이터를 파악한 후 추가 파일이 있으면 다운로드 버튼 활성화
// 아니면 로딩 씬을 거쳐 샘플신을 로드하게끔 실행
private IEnumerator CheckUpdateFiles()
{
var labels = new List<string>() { defaultLabel.labelString, matLabel.labelString };
_patchSize = default;
foreach (var label in labels)
{
var handle = Addressables.GetDownloadSizeAsync(label);
yield return handle;
_patchSize += handle.Result;
if (_patchSize > Decimal.Zero)
{
waitMessage.SetActive(false);
downMessage.SetActive(true);
sizeText.text = "" + GetFileSize(_patchSize);
}
else
{
downValText.text = "100%";
downSlider.value = 1f;
yield return new WaitForSeconds(2f);
LoadingManager.LoadScene("SampleScene");
}
}
}
public void Button_Download()
{
StartCoroutine(PatchFiles());
}
// 새 파일에 대한 패치를 실시하는 코루틴
// 각 라벨에 대해 다운로드 여부를 파악한 후 다운로드할 파일이 있으면 다운로드 코루틴 시작
// 직후 다운로드 상태 코루틴 시작
private IEnumerator PatchFiles()
{
var labels = new List<string>() { defaultLabel.labelString, matLabel.labelString };
_patchSize = default;
foreach (var label in labels)
{
var handle = Addressables.GetDownloadSizeAsync(label);
yield return handle;
if (handle.Result != Decimal.Zero)
{
StartCoroutine(DownLoadFromLabel(label));
}
}
yield return CheckDownloadStatus();
}
// 각각의 라벨에 대해 다운로드하게끔 하는 코루틴
// 해당 라벨에 대해 다운로드 데이터를 받아온 후 핸들을 Release
private IEnumerator DownLoadFromLabel(string label)
{
_patchMap.Add(label, 0);
var handle = Addressables.DownloadDependenciesAsync(label);
while (!handle.IsDone)
{
_patchMap[label] = handle.GetDownloadStatus().DownloadedBytes;
yield return new WaitForEndOfFrame();
}
_patchMap[label] = handle.GetDownloadStatus().TotalBytes;
Addressables.Release(handle);
}
// 다운로드 현황을 체크하는 코루틴
// 로딩바와 진행도를 갱신해주고 완료시 로딩신 후 샘플신으로 넘겨주기
private IEnumerator CheckDownloadStatus()
{
var total = 0f;
downValText.text = "0 %";
while (true)
{
total += _patchMap.Sum(tmp => tmp.Value);
var perValue = total / _patchSize;
downSlider.value = perValue;
downValText.text = string.Format("{0:##.##}", perValue * 100) + " %";
if (total.Equals(_patchSize))
{
LoadingManager.LoadScene("SampleScene");
break;
}
total = 0f;
yield return new WaitForEndOfFrame();
}
}
// 파일 사이즈에 따라 용량에 대한
public string GetFileSize(long byteCnt)
{
string size = "0 Bytes";
if (byteCnt >= 1073741824.0)
{
size = string.Format("{0:##.##}", byteCnt / 1073741824.0) + " GB";
}
else if (byteCnt >= 1048576.0)
{
size = string.Format("{0:##.##}", byteCnt / 1048576.0) + " MB";
}
else if (byteCnt >= 1024.0)
{
size = string.Format("{0:##.##}", byteCnt / 1024.0) + " KB";
}
else if (byteCnt > 0 && byteCnt < 1024.0)
{
size = string.Format("{0:##.##}", byteCnt) + " KB";
}
return size;
}
}
한 씬에서 다른 씬으로 로딩할 때 중간에 거쳐가는 신 매니저
public class LoadingManager : MonoBehaviour
{
// 로딩할 신의 이름
public static string nextScene;
public Slider loadingBar;
// Start is called before the first frame update
void Start()
{
StartCoroutine(StartLoading());
}
// 지정된 신으로 로딩을 시작하는 코루틴
private IEnumerator StartLoading()
{
yield return null;
AsyncOperation op = SceneManager.LoadSceneAsync(nextScene);
op.allowSceneActivation = false;
float timer = 0f;
while (!op.isDone)
{
yield return null;
timer += Time.deltaTime;
if (op.progress < 0.9f)
{
loadingBar.value = Mathf.Lerp(loadingBar.value, op.progress, timer);
if (loadingBar.value >= op.progress)
{
timer = 0f;
}
}
else
{
loadingBar.value = Mathf.Lerp(loadingBar.value, 1f, timer);
if (loadingBar.value.Equals(1f))
{
yield return new WaitForSeconds(2f);
op.allowSceneActivation = true;
yield break;
}
}
}
}
// 한 씬에서 다른 씬으로 넘길 때 사용하는 씬 로딩 함수
public static void LoadScene(string sceneName)
{
nextScene = sceneName;
SceneManager.LoadScene("LoadingScene");
}
}
에셋 파일 변경(파일의 이름은 그대로 하는 것을 권장) 후 Addressables Groups
에서Build
- Update a Previous Build
를 선택해 빌드
.bin
파일을 선택해 빌드프로젝트 파일의 ServerData
에 들어가서 파일의 수정 날짜 및 시간이 빌드한 날짜인 파일들을 선택.
이들이 새로 변경된 파일들이다.
AWS S3에 진입해 .json
, .hash
, 에셋을 변경한 로컬 그룹의 Bundle
파일을 삭제한 후 변경된 파일을 업로드한다.