Voltar para o Blog
Quest Log

Otimizar Jogo Unity para Celular Fraco

Celular simples rodando um jogo 3D com um velocímetro de FPS ao lado e engrenagens sendo ajustadas

Unity mobile optimization em ordem de ataque: Profiler no aparelho, targetFrameRate, Render Scale do URP, ASTC, luz, batching e GC para celular fraco.

Ferramenta:Unity

Otimizar Jogo Unity para Celular Fraco

Unity mobile optimization que funciona segue uma ordem: medir no aparelho, achar se o gargalo é CPU ou GPU, e atacar primeiro o que custa mais naquele celular. Mexer em tudo ao mesmo tempo, ou seguir lista de dicas sem medir, é o jeito mais comum de passar uma semana otimizando e ganhar 2 FPS.

Este post é um roteiro em ordem de ataque para celular fraco, com as configurações exatas do URP no Unity 6.3 LTS, o código para fixar o frame rate e ajustar a resolução de render, e o que olhar no Profiler antes de cada passo.

Medir no aparelho, não no editor

O editor roda no seu PC, com GPU dedicada e o EditorLoop somando custo que não existe no celular. O número do Play Mode não diz nada sobre um Android de entrada.

Faça assim:

  1. Em File > Build Profiles > Android, marque Development Build e Autoconnect Profiler.
  2. Plugue o celular por USB (com depuração USB ativa) e use Build And Run.
  3. Abra Window > Analysis > Profiler. O seletor no topo deve mostrar o aparelho.
  4. Jogue a parte mais pesada do jogo e pause num pico.

No módulo CPU Usage, olhe o tempo do frame. Se a maior parte está em scripts, física ou animação, o gargalo é CPU. Se a CPU fica esperando (marcadores como Gfx.WaitForPresent ou Gfx.PresentFrame tomando o frame), o gargalo é GPU. O post sobre o Profiler da Unity explica essa leitura em detalhe.

Escolha um aparelho de referência: o celular mais fraco que você quer suportar. Otimizar para o seu celular bom não salva quem tem um aparelho de entrada.

Unity mobile optimization: frame rate alvo e Application.targetFrameRate

Segundo a documentação do Application.targetFrameRate no Unity 6.3, com o valor padrão (-1) e QualitySettings.vSyncCount em 0, o Android e o iOS renderizam a 30 FPS fixos para economizar bateria. Muita gente "otimiza" um jogo que está travado em 30 por padrão.

Decida o alvo de propósito:

  • 60 FPS: jogos de ação, plataforma, ritmo. Exige que o frame caiba em 16,6 ms.
  • 30 FPS: puzzle, estratégia, jogos de turno. O frame tem 33,3 ms, e a bateria agradece.

Script de inicialização, arquivo PerformanceBootstrap.cs, num objeto da primeira cena:

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class PerformanceBootstrap : MonoBehaviour
{
    [SerializeField] private int targetFps = 60;
    [SerializeField, Range(0.5f, 1f)] private float mobileRenderScale = 0.8f;

    private void Awake()
    {
        QualitySettings.vSyncCount = 0;
        Application.targetFrameRate = targetFps;

        if (Application.isMobilePlatform && !Application.isEditor)
        {
            UniversalRenderPipelineAsset urp = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
            if (urp != null)
            {
                urp.renderScale = mobileRenderScale;
            }
        }

        DontDestroyOnLoad(gameObject);
    }
}

O !Application.isEditor está ali por um motivo: alterar o asset do URP em Play Mode muda o arquivo do projeto e a mudança fica salva. No aparelho, a alteração vale só durante a execução.

Render Scale e configurações do URP para mobile

Celular simples quase sempre trava na GPU por causa de preenchimento de pixels: tela de 1080 x 2400 tem mais de 2,5 milhões de pixels, e cada um passa pelo shader. O Render Scale do URP Asset reduz a resolução em que a cena é renderizada e depois amplia. Em 0,8, a GPU processa 64% dos pixels.

Crie um URP Asset separado para mobile (duplique o seu e ligue a ele um nível de qualidade em Project Settings > Quality) e ajuste, seguindo a página "Configure for better performance" do manual do URP no 6.3:

  • Quality > Render Scale: 0,75 a 0,85.
  • Quality > HDR: desligado, se o jogo não precisa de bloom forte.
  • Quality > Anti Aliasing (MSAA): desligado ou 2x.
  • Rendering > Depth Texture e Opaque Texture: desligadas, a menos que algum shader use.
  • Lighting > Additional Lights: Disabled ou Per Vertex.
  • Shadows > Max Distance: o menor que ainda fica bonito (20 a 30 em vez de 50). Cascade Count em 1.
  • Shadows > Soft Shadows: desligado.
  • Volume Update Mode: Via Scripting, para os volumes não atualizarem todo frame.
  • No Universal Renderer: Depth Priming Mode em Disabled no mobile.

Para shaders, o mesmo manual recomenda Simple Lit em objetos dinâmicos e Baked Lit em estáticos nos aparelhos mais fracos, e evitar o Complex Lit.

Texturas: ASTC, tamanho e mipmaps

Textura pesa em memória, em tamanho de download e em banda da GPU. No Import Settings de cada textura, aba Android:

  • Format: ASTC. É o padrão do Android no Unity 6. Use ASTC 6x6 em texturas de cenário e 4x4 só em arte que precisa de nitidez (UI, personagem principal). Quanto maior o bloco, menor o arquivo e mais perda.
  • Max Size: seja honesto com a tela. Uma pedra que ocupa 100 pixels na tela não precisa de 2048. Comece em 512 e só suba se ficar visivelmente pior no aparelho.
  • Generate Mipmaps: ligado em objetos 3D que se afastam da câmera (reduz custo e cintilação). Desligado em UI e sprites 2D com câmera fixa.
  • Read/Write: desligado, a menos que um script leia os pixels. Ligado, a textura existe duas vezes na memória.

Para achar as maiores, use o pacote Memory Profiler numa build no aparelho e ordene por tamanho.

Luz, sombra e post processing

Luz em tempo real é o item mais caro de um jogo 3D mobile. Ordem de corte:

  1. Faça bake da luz dos objetos estáticos (marque-os como Static e use Window > Rendering > Lighting > Generate Lighting). Uma luz direcional em tempo real só para personagens resolve a maioria dos casos.
  2. Uma sombra só: a da luz principal, com distância curta.
  3. Post processing: bloom, depth of field e motion blur custam passes inteiros de tela cheia. Num celular fraco, tire todos ou deixe só um tonemapping e color grading leves.

Draw calls e batching

Cada objeto com material diferente custa preparação de CPU antes da GPU desenhar. No URP o SRP Batcher vem ligado e reduz esse custo quando os objetos usam o mesmo shader. Static batching junta geometria parada. Use o Frame Debugger para contar quantos lotes cada frame tem.

Cada técnica tem regra própria e algumas se anulam entre si (GPU instancing e SRP Batcher, por exemplo), então confira no Frame Debugger o que realmente está agrupando. Para celular fraco, a regra prática: menos materiais diferentes (atlas de textura), menos objetos soltos, menos câmeras.

CPU e GC: alocações por frame

No Profiler, a coluna GC Alloc mostra quanta memória cada função aloca. Alocação todo frame enche o heap e dispara a coleta de lixo, que trava o jogo por alguns milissegundos em intervalos. No celular isso aparece como engasgo regular.

Os vilões mais comuns e a troca:

  • GetComponent em Update: guarde a referência em Awake.
  • Concatenar string para UI todo frame: atualize só quando o valor muda.
  • new List<T>() dentro de loop: crie uma vez e use Clear().
  • Instantiate/Destroy de projéteis: use um pool de objetos reaproveitados.
  • LINQ em código que roda todo frame.

Exemplo de HUD sem alocação, arquivo ScoreHud.cs:

using TMPro;
using UnityEngine;

public class ScoreHud : MonoBehaviour
{
    [SerializeField] private TMP_Text scoreText;
    private int lastShownScore = -1;

    public void Show(int score)
    {
        if (score == lastShownScore)
        {
            return;
        }

        lastShownScore = score;
        scoreText.SetText("Pontos: {0}", score);
    }
}

SetText com formato numérico do TextMeshPro escreve o número direto no buffer interno, sem criar string nova. E a checagem lastShownScore evita refazer o layout do texto quando nada mudou.

Até aqui, cada item é uma configuração isolada. O que trava de verdade é otimizar um jogo inteiro: cena, UI, áudio, física e scripts competindo pelo mesmo frame, e saber qual atacar primeiro sem desmontar o que já funciona. Essa visão de projeto completo é o que mais falta para quem aprende por tutorial avulso.

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

Ajuste automático com Adaptive Performance

No Unity 6.3, o Adaptive Performance é recurso embutido: você ativa em Project Settings > Adaptive Performance e a Unity adiciona o módulo interno ao projeto, sem instalar pacote. No Android, o provedor de dados vem do pacote com.unity.adaptiveperformance.google.android. Ele lê temperatura e carga do aparelho e permite baixar resolução ou frame rate quando o celular esquenta. É um complemento: primeiro faça o jogo caber no orçamento de frame, depois use o Adaptive Performance para segurar sessões longas.

Erros comuns: otimizar o que não é gargalo

  • Baixar textura quando o problema é script. Se o Profiler mostra 20 ms em Update de inimigos, nenhuma compressão resolve.
  • Medir no editor. O EditorLoop e a GPU do PC mascaram tudo.
  • Esquecer o limite de 30 FPS padrão e achar que o jogo está pesado.
  • Desligar o SRP Batcher por causa de um shader incompatível em vez de corrigir o shader.
  • Testar só no seu celular. Tenha pelo menos um aparelho de entrada para validar.
  • Ignorar o aquecimento. Rode 15 minutos seguidos: o celular reduz o clock quando esquenta, e o FPS do minuto 1 não é o do minuto 15.

Próximo passo

Gere uma Development Build hoje, conecte o Profiler no aparelho mais fraco que você tem e anote três números: tempo médio de frame, pior pico e GC Alloc por frame. Depois aplique só o primeiro item da ordem que corresponde ao seu gargalo e meça de novo. Para a teoria por trás de CPU bound e GPU bound em qualquer engine, leia otimização de performance para 60 FPS, e para o panorama do mobile (toque, bateria, lojas) veja desenvolvimento de jogos mobile.

Perguntas frequentes

Por que meu jogo Unity roda a 30 FPS no celular mesmo leve?

Porque é o padrão. Com Application.targetFrameRate em -1 e vSyncCount em 0, a Unity renderiza a 30 FPS fixos no Android e no iOS para poupar bateria, segundo a documentação do Unity 6.3. Para 60 FPS, defina Application.targetFrameRate = 60 no início do jogo.

Meu jogo Unity está com lag no Android. Por onde começo?

Conecte o Profiler numa Development Build rodando no próprio aparelho e veja se o frame está estourando na CPU ou na GPU. Só depois mexa em configuração. Ajustar textura quando o gargalo é script, ou vice-versa, gasta tempo sem ganhar FPS.

Render Scale baixo deixa o jogo feio?

Abaixo de 0,7 começa a borrar texto e bordas finas. Entre 0,75 e 0,85 a maioria dos jogadores não percebe, e o ganho na GPU de celular simples é grande. Se a UI é Screen Space Overlay, ela continua na resolução nativa, então o texto fica nítido.

Adaptive Performance ainda existe no Unity 6.3?

Existe, e no Unity 6.3 é recurso embutido: o núcleo é um módulo interno (com.unity.modules.adaptiveperformance) ativado em Project Settings, e o pacote com.unity.adaptiveperformance traz só exemplos e unidades de Visual Scripting. No Android, o provedor de dados fica no pacote com.unity.adaptiveperformance.google.android.