Voltar para o Blog
Quest Log

Profiler da Unity: Achar o Que Está Travando o Jogo

Gráfico de barras coloridas de tempo de frame com um pico vermelho destacado por uma lupa sobre um painel de editor

Unity Profiler na prática: CPU ou GPU, Hierarchy e Timeline, GC Alloc, por que o EditorLoop engana no Play Mode e como medir numa Development Build no celular.

Ferramenta:Unity

Profiler da Unity: Achar o Que Está Travando o Jogo

O unity profiler mostra quanto tempo cada parte do jogo gasta em cada frame, e isso é o que separa otimizar com certeza de otimizar no chute. O uso que resolve a maioria dos casos: abrir em Window > Analysis > Profiler, achar o frame com pico, descobrir se o tempo está na CPU ou na GPU, e descer na view Hierarchy até a função culpada, olhando também a coluna GC Alloc.

O problema é que o Profiler no Play Mode mente um pouco: soma o custo do editor, que não existe no jogo final. Este post mostra a leitura correta, o caso clássico do EditorLoop enganando a medição, como medir numa build de desenvolvimento e no celular, e como marcar seu próprio código com Profile Markers no Unity 6.3 LTS.

Quando abrir o Profiler

Abra quando há um sintoma concreto:

  • FPS abaixo do alvo de forma constante.
  • Engasgos: o jogo roda bem e trava por um instante, às vezes em intervalos regulares.
  • Uma cena específica pesa e outras não.
  • Carregamento de fase demora.
  • O jogo esquenta o celular ou drena bateria.

Não abra para "otimizar por garantia" com o jogo rodando liso. Cada hora gasta nisso é uma hora a menos fazendo o jogo. A teoria geral (CPU bound, GPU bound, orçamento de frame) está no post sobre otimização de performance para 60 FPS; aqui é a ferramenta.

Unity Profiler: janela e módulos

Abra com Window > Analysis > Profiler (atalho Ctrl+7). Com o Profiler aberto, dê Play: ele grava os últimos frames num gráfico.

A janela tem duas metades:

  • Em cima, os módulos: gráficos por categoria. Os mais usados são CPU Usage (tempo de scripts, física, renderização), GPU Usage (precisa ser ativado e não funciona em todas as plataformas), Rendering (batches, triângulos), Memory e Audio. Clique em Profiler Modules para ligar ou desligar.
  • Embaixo, o detalhe do frame selecionado no módulo ativo.

Na barra superior, o seletor de alvo escolhe o que medir: Play Mode, Edit Mode ou um jogo rodando em outro lugar (build local, celular). Para picos raros, pause o jogo e arraste no gráfico até o frame alto; clicar no gráfico seleciona o frame.

CPU Usage: Timeline vs Hierarchy

No módulo CPU Usage, o painel de baixo tem um seletor de view.

Timeline mostra cada thread como uma faixa, com blocos na ordem em que aconteceram. Serve para ver quando as coisas rodam e se a thread principal está esperando outra. Se você vê blocos longos de Gfx.WaitForPresentOnGfxThread ou parecidos na thread principal, a CPU está esperando a GPU terminar: o gargalo é GPU, e mexer em script não ajuda.

Hierarchy agrupa as chamadas em árvore e soma os tempos. É onde você acha o quê custa. Colunas importantes:

  • Total e Self: porcentagem do frame incluindo ou não os filhos.
  • Time ms e Self ms: o mesmo em milissegundos.
  • Calls: quantas vezes a função rodou no frame. 400 chamadas de um Update que você esperava rodar 1 vez já é uma pista.
  • GC Alloc: memória alocada no heap gerenciado naquele frame.

O caminho típico: ordene por Time ms, expanda PlayerLoop, desça até Update.ScriptRunBehaviourUpdate e veja qual script aparece no topo.

Raw Hierarchy mostra cada chamada separada, sem agrupar, útil quando você precisa saber qual instância específica foi a lenta.

GC Alloc e picos de travada

A coleta de lixo do C# para o jogo por alguns milissegundos para liberar memória que não é mais usada. Se o seu código aloca um pouco todo frame, a coleta vem em intervalos e cada uma vira um engasgo visível.

Como achar:

  1. No gráfico do CPU Usage, procure picos regulares. Selecione um e procure GC.Collect na Hierarchy.
  2. Volte para um frame comum e ordene a Hierarchy por GC Alloc. Tudo que aloca toda vez aparece ali.
  3. Para saber a linha exata, ative Call Stacks na barra do Profiler e selecione as amostras GC.Alloc.

Um exemplo de alocação escondida. Arquivo EnemyScanner.cs, antes:

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class EnemyScanner : MonoBehaviour
{
    [SerializeField] private float radius = 10f;

    private void Update()
    {
        Collider[] hits = Physics.OverlapSphere(transform.position, radius);
        List<Collider> enemies = hits.Where(h => h.CompareTag("Enemy")).ToList();
        if (enemies.Count > 0)
        {
            Debug.Log("Inimigos: " + enemies.Count);
        }
    }
}

Três alocações por frame: o array do OverlapSphere, a lista do LINQ e a string do log. A versão sem alocação, mesmo arquivo:

using UnityEngine;

public class EnemyScanner : MonoBehaviour
{
    [SerializeField] private float radius = 10f;
    [SerializeField] private LayerMask enemyLayer;
    private readonly Collider[] hits = new Collider[32];

    public int EnemyCount { get; private set; }

    private void Update()
    {
        EnemyCount = Physics.OverlapSphereNonAlloc(transform.position, radius, hits, enemyLayer);
    }
}

O buffer é criado uma vez, a layer filtra os inimigos sem LINQ e o log sai do Update. A coluna GC Alloc desse script vai a zero.

EditorLoop: por que medir em build

Segundo o manual do Unity 6.3 (página "Play mode and Editor profile samples"), o Profiler separa as amostras em dois grupos: PlayerLoop, que é o seu jogo, e EditorLoop, que é o editor (Inspector, Scene View, janelas). No Play Mode, o tempo do EditorLoop vai para a categoria Others dos gráficos e não aparece detalhado.

O caso clássico: o gráfico mostra o frame a 25 ms, a maior faixa é Others, e a pessoa passa uma tarde caçando um problema que não existe. É o editor desenhando a Scene View aberta ao lado, ou um Inspector atualizando um objeto selecionado.

Para tirar a dúvida:

  • Feche a Scene View e desmarque objetos no Hierarchy antes de medir.
  • Olhe o tempo do PlayerLoop, não o total do frame.
  • Ou, melhor, meça uma build.

Saber ler o Profiler é uma habilidade isolada. O que trava de verdade é aplicar isso num projeto inteiro: decidir quais sistemas reescrever, como estruturar spawns, UI e física para não precisar remendar no fim, e fazer isso sem quebrar o que já funciona. Essa visão de arquitetura vem de construir jogos completos com alguém revisando o caminho.

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

Profiling em build de desenvolvimento e no celular

  1. File > Build Profiles, selecione a plataforma.
  2. Marque Development Build e Autoconnect Profiler. Deep Profiling fica desligado.
  3. Build And Run. O jogo abre e conecta sozinho ao Profiler.
  4. Se não conectar, use o seletor de alvo da janela do Profiler e escolha a build ou o aparelho na lista.

No Android, com o celular no USB e depuração ativa, o alvo aparece na lista. No iOS, pelo projeto Xcode rodando no aparelho. Em rede local, o Profiler encontra builds na mesma rede.

A build de desenvolvimento tem um pequeno custo extra, mas é próxima o bastante do jogo final. É a medição que vale para decidir.

Duas ferramentas oficiais complementam:

  • Memory Profiler (pacote com.unity.memoryprofiler, versão 1.1.12 no Unity 6.3): tira fotos da memória e mostra quais texturas, malhas e objetos ocupam mais. Indispensável para celular.
  • Profile Analyzer (com.unity.performance.profile-analyzer, versão 1.4.2 no Unity 6.3): compara capturas, por exemplo antes e depois de uma otimização, com média e mediana por marcador.

Também existe o Standalone Profiler, que roda o Profiler em processo separado do editor e evita que a própria janela pese na medição.

Profile Markers no seu código

Quando a Hierarchy mostra só "MeuScript.Update" e você quer saber qual parte do método custa, marque trechos com ProfilerMarker. Arquivo PathfindingSystem.cs:

using Unity.Profiling;
using UnityEngine;

public class PathfindingSystem : MonoBehaviour
{
    private static readonly ProfilerMarker RebuildGridMarker = new ProfilerMarker("Pathfinding.RebuildGrid");
    private static readonly ProfilerMarker FindPathsMarker = new ProfilerMarker("Pathfinding.FindPaths");

    private void Update()
    {
        using (RebuildGridMarker.Auto())
        {
            RebuildGrid();
        }

        using (FindPathsMarker.Auto())
        {
            FindPaths();
        }
    }

    private void RebuildGrid()
    {
        // atualiza a grade de navegação
    }

    private void FindPaths()
    {
        // calcula caminhos dos agentes
    }
}

Os marcadores aparecem com esse nome na Hierarchy e na Timeline, em builds de desenvolvimento também. O using com .Auto() garante que o marcador fecha mesmo se o método sair no meio. Criar o marcador como static readonly evita custo de criação a cada frame. Em build de release, o custo é desprezível.

Erros comuns: otimizar pelo editor

  • Confiar no FPS do Play Mode. O editor soma EditorLoop e roda numa GPU de PC. Meça build.
  • Deep Profiling ligado o tempo todo. Ele instrumenta todas as chamadas C# e distorce os tempos. Use só para investigar um trecho e prefira markers.
  • Olhar a média e ignorar picos. 16 ms de média com picos de 80 ms parece ok no número e trava na tela. Selecione os picos.
  • Otimizar CPU quando a espera é pela GPU. Se a thread principal espera o Present, reduza resolução, overdraw e efeitos.
  • Medir uma vez e mudar dez coisas. Mude uma, meça de novo. Sem isso você não sabe qual mudança ajudou.
  • Esquecer o Debug.Log. Log em Update aloca string e escreve em disco. Em build, isso aparece no Profiler como custo real.

Próximo passo

Pegue a cena mais pesada do seu jogo, gere uma Development Build com Autoconnect Profiler e anote: tempo médio de frame, o pior pico, os três scripts com mais Time ms e os três com mais GC Alloc. Corrija o primeiro da lista de GC Alloc, meça de novo e compare no Profile Analyzer. Se o problema for muitos projéteis criados e destruídos, o post sobre object pooling mostra o padrão; se o gráfico apontar renderização, leia sobre draw calls e batching na Unity.

Perguntas frequentes

O que é EditorLoop no Profiler da Unity?

É o grupo de amostras do trabalho do próprio editor: Inspector, Scene View, janelas abertas. No Play Mode, o Profiler joga esse tempo na categoria Others e não mostra detalhe. Ele não existe na build do jogo, então um EditorLoop alto não significa que o jogo está pesado.

Como abrir o Profiler da Unity?

Pelo menu Window > Analysis > Profiler, ou com o atalho Ctrl+7 no Windows (Cmd+7 no Mac). Para medir uma build, gere com Development Build e Autoconnect Profiler marcados e escolha o aparelho no seletor de alvo no topo da janela.

Por que o jogo trava de tempos em tempos sem motivo?

Engasgo periódico costuma ser o coletor de lixo. Procure picos com GC.Collect no Profiler e, na view Hierarchy, ordene pela coluna GC Alloc para achar quem aloca memória todo frame. Corrigir essas alocações elimina a travada regular.

Deep Profiling vale a pena?

Só para investigar um trecho específico. Ele instrumenta todas as chamadas de método C#, o que deixa o jogo bem mais lento e distorce o tempo absoluto. Use Profile Markers no seu código para medir com precisão sem esse custo.