Otimizar Jogo Unity para Celular Fraco

Unity mobile optimization em ordem de ataque: Profiler no aparelho, targetFrameRate, Render Scale do URP, ASTC, luz, batching e GC para celular fraco.
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:
- Em File > Build Profiles > Android, marque Development Build e Autoconnect Profiler.
- Plugue o celular por USB (com depuração USB ativa) e use Build And Run.
- Abra Window > Analysis > Profiler. O seletor no topo deve mostrar o aparelho.
- 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:
- 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.
- Uma sombra só: a da luz principal, com distância curta.
- 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:
GetComponentemUpdate: guarde a referência emAwake.- Concatenar string para UI todo frame: atualize só quando o valor muda.
new List<T>()dentro de loop: crie uma vez e useClear().Instantiate/Destroyde 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.
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
Updatede 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.


