ZoeAppClass/Assets/Scripts/Addressables/AddressableGameDownloader.cs

621 lines
14 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
/// <summary>
/// Baixa e carrega jogos Addressable somente quando forem necessários.
/// Compatível com Unity 2019.3 e Addressables 1.1.x.
/// </summary>
public static class AddressableGameDownloader
{
private static readonly Dictionary<string, AsyncOperationHandle> LoadedHandles =
new Dictionary<string, AsyncOperationHandle>(
StringComparer.OrdinalIgnoreCase
);
/// <summary>
/// Valida se o Address existe no catálogo carregado,
/// verifica o tamanho pendente,
/// baixa as dependências se necessário
/// e depois carrega o asset.
/// </summary>
public static IEnumerator LoadAsset<T>(
string address,
Action<T> onSuccess,
Action<float> onProgress = null,
Action<string> onStatus = null,
Action<string> onError = null)
where T : UnityEngine.Object
{
if (string.IsNullOrEmpty(address))
{
Fail(
"O endereço Addressable está vazio.",
onError
);
yield break;
}
Debug.Log(
"[Addressables] ==================================="
);
Debug.Log(
"[Addressables] Tentando carregar: [" +
address +
"]"
);
/*
* 1. VALIDA SE O ADDRESS EXISTE
*/
onStatus?.Invoke(
"Localizando conteúdo..."
);
AsyncOperationHandle<IList<IResourceLocation>> locationHandle =
Addressables.LoadResourceLocationsAsync(
address
);
yield return locationHandle;
bool locationFound =
locationHandle.Status ==
AsyncOperationStatus.Succeeded &&
locationHandle.Result != null &&
locationHandle.Result.Count > 0;
if (!locationFound)
{
if (locationHandle.IsValid())
{
Addressables.Release(
locationHandle
);
}
Fail(
"ADDRESS NÃO EXISTE NO CATÁLOGO: [" +
address +
"]",
onError
);
yield break;
}
Debug.Log(
"[Addressables] Address encontrado: [" +
address +
"]"
);
Debug.Log(
"[Addressables] Localizações encontradas: " +
locationHandle.Result.Count
);
for (int i = 0; i < locationHandle.Result.Count; i++)
{
IResourceLocation location =
locationHandle.Result[i];
Debug.Log(
"[Addressables] Localização [" +
i +
"] PrimaryKey: " +
location.PrimaryKey
);
Debug.Log(
"[Addressables] Localização [" +
i +
"] InternalId: " +
location.InternalId
);
}
Addressables.Release(
locationHandle
);
/*
* 2. VERIFICA TAMANHO DO DOWNLOAD
*/
onStatus?.Invoke(
"Verificando conteúdo..."
);
AsyncOperationHandle<long> sizeHandle =
Addressables.GetDownloadSizeAsync(
address
);
yield return sizeHandle;
if (
sizeHandle.Status !=
AsyncOperationStatus.Succeeded
)
{
if (sizeHandle.IsValid())
{
Addressables.Release(
sizeHandle
);
}
Fail(
"Não foi possível consultar o tamanho do Addressable: [" +
address +
"]",
onError
);
yield break;
}
long downloadSize =
sizeHandle.Result;
Debug.Log(
"[Addressables] Tamanho pendente: " +
FormatBytes(downloadSize)
);
Addressables.Release(
sizeHandle
);
/*
* 3. DOWNLOAD
*/
if (downloadSize > 0)
{
onStatus?.Invoke(
"Baixando " +
FormatBytes(downloadSize)
);
Debug.Log(
"[Addressables] Iniciando download: [" +
address +
"]"
);
AsyncOperationHandle downloadHandle =
Addressables.DownloadDependenciesAsync(
address
);
while (!downloadHandle.IsDone)
{
if (onProgress != null)
{
onProgress(
downloadHandle.PercentComplete
);
}
yield return null;
}
if (
downloadHandle.Status !=
AsyncOperationStatus.Succeeded
)
{
if (downloadHandle.IsValid())
{
Addressables.Release(
downloadHandle
);
}
Fail(
"Falha ao baixar o Addressable: [" +
address +
"]",
onError
);
yield break;
}
Debug.Log(
"[Addressables] Download concluído: [" +
address +
"]"
);
if (downloadHandle.IsValid())
{
Addressables.Release(
downloadHandle
);
}
}
else
{
Debug.Log(
"[Addressables] Conteúdo já está no cache: [" +
address +
"]"
);
if (onProgress != null)
{
onProgress(1f);
}
}
/*
* 4. CARREGA O ASSET
*/
onStatus?.Invoke(
"Carregando conteúdo..."
);
Debug.Log(
"[Addressables] LoadAssetAsync: [" +
address +
"]"
);
AsyncOperationHandle<T> loadHandle =
Addressables.LoadAssetAsync<T>(
address
);
while (!loadHandle.IsDone)
{
if (onProgress != null)
{
onProgress(
loadHandle.PercentComplete
);
}
yield return null;
}
if (
loadHandle.Status !=
AsyncOperationStatus.Succeeded ||
loadHandle.Result == null
)
{
if (loadHandle.IsValid())
{
Addressables.Release(
loadHandle
);
}
Fail(
"Falha ao carregar o Addressable: [" +
address +
"]",
onError
);
yield break;
}
/*
* 5. GUARDA HANDLE
*/
AsyncOperationHandle previous;
if (
LoadedHandles.TryGetValue(
address,
out previous
)
)
{
if (previous.IsValid())
{
Addressables.Release(
previous
);
}
LoadedHandles.Remove(
address
);
}
LoadedHandles[address] =
loadHandle;
if (onProgress != null)
{
onProgress(1f);
}
onStatus?.Invoke(
"Conteúdo carregado."
);
Debug.Log(
"[Addressables] SUCESSO: [" +
address +
"]"
);
Debug.Log(
"[Addressables] ==================================="
);
if (onSuccess != null)
{
onSuccess(
loadHandle.Result
);
}
}
/// <summary>
/// Verifica se um Address existe no catálogo atualmente carregado.
/// </summary>
public static IEnumerator Exists(
string address,
Action<bool> completed)
{
if (string.IsNullOrEmpty(address))
{
Debug.LogError(
"[Addressables] Exists recebeu address vazio."
);
if (completed != null)
{
completed(false);
}
yield break;
}
Debug.Log(
"[Addressables] Verificando Address: [" +
address +
"]"
);
AsyncOperationHandle<IList<IResourceLocation>> handle =
Addressables.LoadResourceLocationsAsync(
address
);
yield return handle;
bool exists =
handle.Status ==
AsyncOperationStatus.Succeeded &&
handle.Result != null &&
handle.Result.Count > 0;
if (exists)
{
Debug.Log(
"[Addressables] Address EXISTE: [" +
address +
"]"
);
for (int i = 0; i < handle.Result.Count; i++)
{
Debug.Log(
"[Addressables] -> " +
handle.Result[i].InternalId
);
}
}
else
{
Debug.LogError(
"[Addressables] Address NÃO EXISTE no catálogo: [" +
address +
"]"
);
}
if (handle.IsValid())
{
Addressables.Release(
handle
);
}
if (completed != null)
{
completed(
exists
);
}
}
/// <summary>
/// Consulta quanto ainda precisa ser baixado.
/// 0 bytes significa que já está no cache.
/// </summary>
public static IEnumerator GetDownloadSize(
string address,
Action<long> completed,
Action<string> onError = null)
{
if (string.IsNullOrEmpty(address))
{
Fail(
"O endereço Addressable está vazio.",
onError
);
yield break;
}
AsyncOperationHandle<long> handle =
Addressables.GetDownloadSizeAsync(
address
);
yield return handle;
if (
handle.Status ==
AsyncOperationStatus.Succeeded
)
{
Debug.Log(
"[Addressables] Download pendente para [" +
address +
"]: " +
FormatBytes(handle.Result)
);
if (completed != null)
{
completed(
handle.Result
);
}
}
else
{
Fail(
"Falha ao consultar tamanho: [" +
address +
"]",
onError
);
}
if (handle.IsValid())
{
Addressables.Release(
handle
);
}
}
/// <summary>
/// Libera um Addressable carregado anteriormente.
/// </summary>
public static void Release(
string address)
{
if (string.IsNullOrEmpty(address))
{
return;
}
AsyncOperationHandle handle;
if (
!LoadedHandles.TryGetValue(
address,
out handle
)
)
{
return;
}
if (handle.IsValid())
{
Addressables.Release(
handle
);
}
LoadedHandles.Remove(
address
);
Debug.Log(
"[Addressables] Liberado: [" +
address +
"]"
);
}
/// <summary>
/// Libera todos os Addressables carregados.
/// </summary>
public static void ReleaseAll()
{
foreach (
KeyValuePair<string, AsyncOperationHandle> item
in LoadedHandles
)
{
if (item.Value.IsValid())
{
Addressables.Release(
item.Value
);
}
}
LoadedHandles.Clear();
Debug.Log(
"[Addressables] Todos os handles foram liberados."
);
}
private static void Fail(
string message,
Action<string> onError)
{
Debug.LogError(
"[Addressables] " +
message
);
if (onError != null)
{
onError(
message
);
}
}
private static string FormatBytes(
long bytes)
{
const long KB = 1024;
const long MB = KB * 1024;
const long GB = MB * 1024;
if (bytes >= GB)
{
return (
bytes / (float)GB
).ToString("0.00") + " GB";
}
if (bytes >= MB)
{
return (
bytes / (float)MB
).ToString("0.00") + " MB";
}
if (bytes >= KB)
{
return (
bytes / (float)KB
).ToString("0.00") + " KB";
}
return bytes + " bytes";
}
}