Voltar para o Blog
Quest Log

Addressables na Unity: Carregar Assets sob Demanda

Estantes de caixas etiquetadas com um braço mecânico pegando uma caixa e levando para uma cena de jogo iluminada

Unity Addressables na prática: marcar assets, carregar por AssetReference com await, liberar memória com Release, grupos locais e remotos, e quando não usar.

Ferramenta:UnityC#

Addressables na Unity: Carregar Assets sob Demanda

O unity addressables é o sistema da Unity para carregar assets pelo endereço, na hora em que você precisa, e tirar da memória quando não precisa mais. O uso básico: marcar o asset como Addressable, guardar um AssetReference no script, carregar com LoadAssetAsync ou InstantiateAsync esperando com await, e liberar com Release ou ReleaseInstance. É isso que impede o jogo de carregar todos os chefes, músicas e fases logo na abertura.

Antes de instalar, a pergunta honesta: o seu projeto precisa disso? Este post mostra quando não usar, e para quem precisa, o passo a passo no Unity 6.3 LTS com código C# que compila, a contagem de referências que evita vazamento, grupos locais e remotos, e os erros mais comuns.

O problema que Addressables resolve

Quando um script tem um campo [SerializeField] private GameObject bossPrefab; apontando para um prefab, a Unity carrega esse prefab, e tudo que ele usa (malhas, texturas, sons, animações), junto com a cena. Mesmo que o chefe só apareça daqui a 40 minutos de jogo.

Em projeto pequeno, isso não importa. Em projeto com muitas fases, personagens com texturas grandes ou trilha sonora pesada, a memória enche, o carregamento inicial demora e, no celular, o sistema fecha o app.

O Addressables troca a referência direta por uma referência por endereço. O asset fica fora da cena e só entra na memória quando o código pede. Também permite agrupar conteúdo em pacotes que podem ser baixados depois da instalação.

Quando NÃO usar Addressables

  • Jogo de jam ou protótipo: a complexidade não se paga.
  • Jogo pequeno que cabe inteiro na memória do aparelho mais fraco que você suporta: referência direta no Inspector é mais simples, mais rápida de depurar e não tem como esquecer Release.
  • Poucas fases leves: dividir em cenas e trocar de cena já descarrega o que a cena anterior usava.
  • Só para reduzir o tamanho do instalador: o Addressables ajuda, mas o primeiro passo é revisar compressão de textura e áudio, assunto do post sobre reduzir o tamanho da build na Unity.

A pasta Resources continua funcionando, mas tudo que está nela entra na build e fica num índice carregado no início, sem controle de dependência. Ela serve para poucos arquivos pequenos e sempre necessários. Para conteúdo grande e opcional, Addressables é o caminho.

Use quando: o jogo tem muito conteúdo que não precisa estar na memória ao mesmo tempo, você quer baixar conteúdo depois da instalação (fases extras, eventos), ou o celular alvo está ficando sem memória.

Instalar e marcar assets como Addressable

  1. Window > Package Management > Package Manager, Unity Registry, instale Addressables (com.unity.addressables). O manual do Unity 6.3 indica a versão 2.10.3 para o editor 6000.3 e lista a linha 4.x como disponível também.
  2. Abra Window > Asset Management > Addressables > Groups e clique em Create Addressables Settings. Isso cria a pasta AddressableAssetsData com as configurações e o grupo padrão.
  3. Selecione um prefab, áudio ou textura no Project e marque a caixa Addressable no topo do Inspector. Ele ganha um endereço (por padrão, o caminho do arquivo). Renomeie para algo curto, como Boss/Dragao.
  4. Na janela Groups, crie grupos com clique direito > Create New Group > Packed Assets e arraste assets para organizar (ex.: Fase1, Chefes, Musica).

Na barra da janela Groups, Play Mode Script define como o editor carrega no Play Mode:

  • Use Asset Database (fastest): carrega direto dos arquivos do projeto. Rápido para iterar, mas não testa a build de conteúdo.
  • Use Existing Build (requires built groups): usa o conteúdo gerado por Build > New Build > Default Build Script. É o teste fiel.

Unity Addressables: carregar com AssetReference

O AssetReference é um campo no Inspector onde você arrasta um asset Addressable, sem criar referência direta. As versões tipadas (AssetReferenceGameObject, AssetReferenceT<AudioClip>) limitam o que pode ser arrastado.

Arquivo BossSpawner.cs:

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class BossSpawner : MonoBehaviour
{
    [SerializeField] private AssetReferenceGameObject bossPrefab;
    [SerializeField] private Transform spawnPoint;

    private GameObject bossInstance;

    public async Task SpawnBossAsync()
    {
        if (bossInstance != null)
        {
            return;
        }

        AsyncOperationHandle<GameObject> handle = bossPrefab.InstantiateAsync(spawnPoint.position, spawnPoint.rotation);
        GameObject instance = await handle.Task;

        if (handle.Status != AsyncOperationStatus.Succeeded)
        {
            Debug.LogError("Falha ao carregar o chefe: " + handle.OperationException);
            Addressables.Release(handle);
            return;
        }

        bossInstance = instance;
    }

    public void DespawnBoss()
    {
        if (bossInstance == null)
        {
            return;
        }

        bossPrefab.ReleaseInstance(bossInstance);
        bossInstance = null;
    }

    private void OnDestroy()
    {
        DespawnBoss();
    }
}

O await handle.Task espera o carregamento sem travar o jogo: enquanto o chefe carrega, o resto continua rodando. Chame SpawnBossAsync() de um gatilho de área com _ = spawner.SpawnBossAsync(); ou de um método async. Na linha 4.x do pacote, o changelog informa que também dá para escrever await handle direto, com exceção em caso de falha.

Para assets que não são instanciados (áudio, sprite, ScriptableObject), carregue e guarde o handle. Arquivo MusicLoader.cs:

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class MusicLoader : MonoBehaviour
{
    [SerializeField] private AssetReferenceT<AudioClip> bossMusic;
    [SerializeField] private AudioSource musicSource;

    private AsyncOperationHandle<AudioClip> musicHandle;

    public async Task PlayBossMusicAsync()
    {
        StopAndRelease();
        musicHandle = Addressables.LoadAssetAsync<AudioClip>(bossMusic);
        AudioClip clip = await musicHandle.Task;

        if (musicHandle.Status == AsyncOperationStatus.Succeeded)
        {
            musicSource.clip = clip;
            musicSource.Play();
        }
    }

    public void StopAndRelease()
    {
        musicSource.Stop();
        musicSource.clip = null;

        if (musicHandle.IsValid())
        {
            Addressables.Release(musicHandle);
        }
    }

    private void OnDestroy()
    {
        StopAndRelease();
    }
}

Liberar memória e contagem de referências

O Addressables conta referências. Cada carregamento soma 1 ao asset e às dependências dele; cada Release tira 1. Quando chega a zero, o asset pode sair da memória (e o pacote que o contém, quando nada mais dele está em uso).

As regras que evitam vazamento:

  • Todo LoadAssetAsync tem um Release correspondente, com o mesmo handle.
  • Todo InstantiateAsync tem um ReleaseInstance. Chamar Destroy na instância apaga o GameObject, mas a contagem continua lá.
  • Guarde o handle num campo. Sem ele, você não tem o que liberar.
  • Não use o asset depois do Release: a referência pode virar nula ou apontar para algo descarregado.

Para ver se está vazando, abra Window > Analysis > Profiler e adicione o módulo Asset Loading, ou use o Memory Profiler e compare duas capturas antes e depois de entrar e sair de uma fase.

Carregar e liberar um chefe é o exemplo fácil. O difícil é desenhar o fluxo de conteúdo de um jogo inteiro: o que carrega no início, o que entra por fase, o que fica sempre, e como menus, save e transições conversam com isso sem travar a tela. Essa arquitetura de projeto é o que mais falta para quem aprende por tutorial isolado.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

Grupos, labels e build de conteúdo

Grupos decidem como os assets viram pacotes (AssetBundles). Assets do mesmo grupo podem cair no mesmo pacote, e um pacote só sai da memória quando nada dele está em uso. Por isso, agrupe por momento de uso: tudo da Fase 1 junto, cada chefe separado, músicas separadas. Um grupo gigante com tudo derrota o propósito.

Labels marcam vários assets para carregar juntos. Selecione assets na janela Groups, coluna Labels, e crie uma, como inimigos-fase1. Depois:

AsyncOperationHandle<IList<GameObject>> handle =
    Addressables.LoadAssetsAsync<GameObject>("inimigos-fase1", prefab => Debug.Log("Carregado: " + prefab.name));

(com using System.Collections.Generic;). O callback roda para cada asset, e um único Addressables.Release(handle) libera todos.

Build de conteúdo: na janela Groups, Build > New Build > Default Build Script gera os pacotes. Confira em AddressableAssetSettings a opção de construir o conteúdo junto com a build do jogo, para não publicar um jogo sem os pacotes atualizados.

Use Window > Asset Management > Addressables > Analyze e rode as regras de dependência duplicada. Elas mostram assets que entraram em mais de um pacote sem você perceber, o que gasta memória e espaço.

Conteúdo remoto: visão geral

Cada grupo tem Build Path e Load Path, configurados em Profiles (Window > Asset Management > Addressables > Profiles). Com o grupo apontando para o caminho Remote, os pacotes são gerados numa pasta que você sobe para um servidor ou CDN, e o jogo baixa quando precisa, guardando em cache.

Isso permite fases extras e eventos sem nova versão na loja, e reduz o download inicial. Exige cuidar de catálogo (o índice de endereços), versão de conteúdo e atualização. A Unity oferece o Cloud Content Delivery como hospedagem integrada, mas qualquer servidor HTTP serve. Para um primeiro projeto, deixe tudo local e domine carga e liberação antes.

Erros comuns: vazamento, InvalidKeyException

  • Memória que só cresce: LoadAssetAsync sem Release, ou Destroy em instância criada por InstantiateAsync.
  • InvalidKeyException: endereço digitado errado, asset desmarcado como Addressable, label vazia ou Play Mode Script em Use Existing Build com conteúdo velho. Refaça o Default Build Script.
  • "Attempting to load AssetReference that has already been loaded": chamou LoadAssetAsync duas vezes no mesmo AssetReference. Use o handle guardado ou Addressables.LoadAssetAsync com a referência como chave.
  • Funciona no editor, falha na build: Play Mode Script estava em Use Asset Database, e a build de conteúdo não foi gerada ou ficou desatualizada.
  • Asset duplicado na memória: o mesmo asset está numa cena (referência direta) e num grupo Addressable. Rode o Analyze.
  • Material rosa na build: shader removido no stripping porque só um asset Addressable o usava. Confira as variantes de shader na build.

Próximo passo

Pegue um único asset pesado do seu jogo, como a música de chefe ou um prefab grande, transforme em Addressable e carregue com o BossSpawner ou o MusicLoader acima. Meça a memória no Profiler antes e depois. Se o ganho for pequeno, você aprendeu algo barato e pode parar por ali. Se o problema for muitos objetos criados e destruídos durante o jogo, o padrão certo é outro: veja object pooling. E para o panorama de memória, CPU e GPU, leia otimização de performance para 60 FPS.

Perguntas frequentes

Qual versão do Addressables usar no Unity 6.3?

O manual do Unity 6.3 indica a versão 2.10.3 como a lançada para o editor 6000.3, e lista também a linha 4.x como disponível. O código deste post usa a API comum às duas (LoadAssetAsync, InstantiateAsync, Release e handle.Task). A 4.x acrescenta await direto no handle.

Addressables ou Resources: qual usar?

Para projeto pequeno, que cabe todo na memória do aparelho alvo, referência direta no Inspector resolve e é mais simples. Resources ainda funciona, mas tudo que está na pasta entra na build e não dá para controlar dependências. Addressables vale quando você precisa carregar e descarregar conteúdo sob demanda ou baixar conteúdo depois da instalação.

O que causa InvalidKeyException no Addressables?

A chave pedida não existe no catálogo carregado. Normalmente é endereço digitado diferente, asset que deixou de ser Addressable, label sem nenhum asset, ou Play Mode Script em Use Existing Build com o conteúdo desatualizado. Refaça a build de conteúdo ou volte para Use Asset Database para conferir.

Preciso chamar Release depois de carregar?

Sim. O Addressables conta referências: cada LoadAssetAsync soma uma, cada Release tira uma, e o asset só sai da memória quando a contagem chega a zero. Destruir o GameObject não libera o que foi carregado. Instância criada com InstantiateAsync se libera com ReleaseInstance.