NavMesh 2D na Unity: Pathfinding em Jogo Top-Down
Unity NavMesh 2D não existe como recurso nativo pronto: o NavMesh da Unity é calculado num plano 3D a partir de malhas e colliders 3D, e não enxerga Tilemap nem Collider2D. Para um jogo top-down, o caminho mais usado é o NavMeshPlus, um projeto aberto que gera a malha a partir do Tilemap e deixa o NavMeshAgent normal andar sobre ela, com duas linhas de configuração para o agente não girar o sprite. Aqui você monta isso numa masmorra de Tilemap, vê as alternativas (A* Pathfinding Project e um grid próprio) e sai sabendo quando cada uma vale.
A parte honesta: qualquer solução de NavMesh em 2D é uma adaptação. Saber os limites dela antes de basear a IA do jogo inteiro nisso evita retrabalho no meio do projeto.
Por que o NavMesh da Unity não funciona direto no 2D
O bake do NavMesh (no pacote AI Navigation, pelo componente NavMesh Surface) coleta render meshes ou colliders 3D e gera polígonos andáveis num plano horizontal, o plano XZ. Um jogo 2D vive no plano XY, com sprites e Tilemaps que não têm malha 3D com volume e colliders que são 2D. Resultado: o bake não encontra nada, ou encontra uma malha deitada no plano errado.
Mesmo que você gire a superfície, o NavMeshAgent foi feito para 3D. Ele rotaciona o objeto para olhar na direção do movimento e alinha o "para cima" do objeto à malha. Num sprite, isso vira o personagem deitado de lado ou girando no eixo Z.
No Unity 6.3 LTS, o pacote AI Navigation não traz coleta de Tilemap nem de Collider2D. A busca por essa solução é tão comum que existe ecossistema em volta dela.
Opções: NavMeshPlus, A* Pathfinding Project, A* próprio
| Opção | Custo | Bom para | Limite |
|---|---|---|---|
| NavMeshPlus | grátis, código aberto (MIT) | top-down com movimento livre, usando o NavMeshAgent da Unity | projeto da comunidade, sem garantia de compatibilidade oficial |
| A* Pathfinding Project | versão gratuita e versão Pro | projetos maiores, grid ou navmesh, 2D nativo | a versão gratuita não tem recast, avoidance local nem múltiplas threads |
| A* ou BFS próprio | grátis | movimento de tile em tile, roguelike, tático | você mantém o código |
Sobre o A* Pathfinding Project: a tabela oficial de comparação, consultada em outubro de 2026, mostra a versão gratuita com grid graphs, suporte a pathfinding 2D (com física 2D para grid e point graphs), navmesh graphs, grafos hexagonais e isométricos e suavização de caminho. Ficam só na Pro, que custava US$ 100 na mesma página, a geração automática de navmesh por recast, avoidance local entre agentes e pathfinding em várias threads. Para um top-down comum, a versão gratuita com grid graph já resolve.
O resto do post usa o NavMeshPlus, porque ele mantém o fluxo do NavMeshAgent que você já usa em 3D. Se você vem do Godot, lá o 2D tem navegação nativa (NavigationRegion2D e NavigationAgent2D), como mostra o post de pathfinding 2D no Godot.
NavMesh 2D na Unity com NavMeshPlus e Tilemap
Instalar. Em Window > Package Management > Package Manager, clique em + > Install package from git URL e cole https://github.com/h8man/NavMeshPlus.git. O pacote se chama NavMeshPlus (com.h8man.2d.navmeshplus). O repositório estava ativo em outubro de 2026, mas não publica tabela de compatibilidade com o Unity 6.3: instale numa cópia do projeto (ou numa branch do Git) antes de adotar.
O NavMeshPlus traz o próprio NavMeshSurface, no namespace NavMeshPlus.Components, diferente do Unity.AI.Navigation do pacote oficial. No Add Component, os dele aparecem como Navigation Surface, Navigation Modifier e Navigation CollectSources2d. Não misture a superfície dele com a do AI Navigation na mesma cena.
Montar a cena. Uma masmorra típica tem um Grid com dois Tilemaps: Chao e Paredes (este com TilemapCollider2D).
- Crie um GameObject vazio
NavMesh2Dna raiz da cena. - Adicione Navigation Surface e, em seguida, Navigation CollectSources2d.
- No CollectSources2d, clique em Rotate Surface to XY. A superfície gira para -90 em X e passa a olhar para a câmera 2D.
- No Tilemap
Chao, adicione Navigation Modifier, ligue Override Area e escolha Walkable. - No Tilemap
Paredes, adicione Navigation Modifier, ligue Override Area e escolha Not Walkable. - Na Navigation Surface, deixe Use Geometry em Physics Colliders se as paredes têm collider, ou Render Meshes para usar os sprites dos tiles.
- Clique em Bake. O chão fica azul e as paredes ficam de fora.
Obstáculos soltos (baús, colunas feitas de sprite) também levam Navigation Modifier com Not Walkable. O pacote tem ainda o Navigation Modifier Tilemap, que marca tiles específicos de um Tilemap com áreas diferentes, útil para lama e água rasa com custo maior.
Fazer o bake da masmorra é um bom marco. Transformar isso num jogo, com vários tipos de inimigo, salas geradas, portas que abrem, combate e uma IA que não trava nos cantos, é onde muita gente para, porque juntar sistemas é diferente de aprender cada um. Ver um jogo 2D completo sendo construído é o atalho para essa parte.
Configurar NavMeshAgent para 2D: updateRotation e updateUpAxis
O inimigo usa o NavMeshAgent padrão da Unity (UnityEngine.AI). Adicione o componente e ajuste Radius para a metade da largura do sprite em unidades. Depois, desligue as duas coisas que o agente faz pensando em 3D. Crie TopDownNavAgent.cs:
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class TopDownNavAgent : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private SpriteRenderer spriteRenderer;
[SerializeField] private float repathInterval = 0.25f;
private NavMeshAgent agent;
private float repathTimer;
private void Awake()
{
agent = GetComponent<NavMeshAgent>();
agent.updateRotation = false;
agent.updateUpAxis = false;
}
private void Update()
{
if (target == null || !agent.isOnNavMesh)
{
return;
}
repathTimer -= Time.deltaTime;
if (repathTimer <= 0f)
{
repathTimer = repathInterval;
Vector3 destination = target.position;
destination.z = transform.position.z;
agent.SetDestination(destination);
}
Vector3 velocity = agent.velocity;
if (spriteRenderer != null && Mathf.Abs(velocity.x) > 0.05f)
{
spriteRenderer.flipX = velocity.x < 0f;
}
}
}
O que cada linha resolve:
updateRotation = false: o agente para de girar o Transform na direção do movimento. Sem isso, o sprite gira em Z como um ponteiro de relógio.updateUpAxis = false: segundo a documentação, essa propriedade define se o agente se alinha ao eixo "para cima" da malha ou do link onde está. Na superfície rotacionada para XY, alinhar faria o objeto deitar. Desligado, o sprite fica de pé.destination.z = transform.position.z: mantém o destino no mesmo plano do agente. Um Z diferente entre jogador e inimigo pode fazer oSetDestinationfalhar silenciosamente.agent.velocitypara virar o sprite: o agente já calcula a velocidade, e o sinal de X diz para que lado olhar.
Se o seu inimigo tem Animator, passe agent.velocity.magnitude para um parâmetro de velocidade e deixe um Blend Tree escolher entre parado e andando.
Obstáculos e paredes do Tilemap
Três situações aparecem em toda masmorra:
Paredes finas somem do bake. Se o Radius do agente é grande e o tile de parede é estreito, a malha pode atravessar. Use paredes de um tile inteiro de espessura, ou diminua o raio.
Corredor de um tile não aparece. O raio do agente encolhe a malha nas bordas. Corredor de 1 unidade com agente de raio 0,5 vira zero. Para personagem de um tile, raio entre 0,3 e 0,4 mantém o corredor.
Porta que abre. Para obstáculos que mudam durante o jogo, use NavMesh Obstacle com Carve ligado no objeto da porta. Ele recorta a malha sem precisar de novo bake e funciona com o NavMeshAgent da Unity normalmente. Ao abrir, desligue o componente.
Para mudanças grandes (parede destruída, sala gerada na hora), chame UpdateNavMesh(surface.navMeshData) na Navigation Surface do NavMeshPlus, que reconstrói de forma assíncrona.
Quando um grid A* simples resolve melhor
Se o seu jogo é de grade (roguelike por turnos, tático, puzzle, inimigo que anda de tile em tile), NavMesh é ferramenta demais. O Tilemap já é um grafo: cada célula é um nó e os vizinhos são as quatro células ao lado. Uma busca em largura (BFS) acha o menor caminho em número de passos. Crie TilemapPathfinder.cs:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class TilemapPathfinder : MonoBehaviour
{
[SerializeField] private Tilemap floor;
[SerializeField] private Tilemap walls;
[SerializeField] private int maxSearchNodes = 4000;
private static readonly Vector3Int[] Directions =
{
Vector3Int.up, Vector3Int.down, Vector3Int.left, Vector3Int.right
};
public bool IsWalkable(Vector3Int cell)
{
return floor.HasTile(cell) && !walls.HasTile(cell);
}
public List<Vector3> FindPath(Vector3 fromWorld, Vector3 toWorld)
{
Vector3Int start = floor.WorldToCell(fromWorld);
Vector3Int goal = floor.WorldToCell(toWorld);
List<Vector3> path = new List<Vector3>();
if (!IsWalkable(start) || !IsWalkable(goal))
{
return path;
}
Queue<Vector3Int> frontier = new Queue<Vector3Int>();
Dictionary<Vector3Int, Vector3Int> cameFrom = new Dictionary<Vector3Int, Vector3Int>();
frontier.Enqueue(start);
cameFrom[start] = start;
while (frontier.Count > 0 && cameFrom.Count < maxSearchNodes)
{
Vector3Int current = frontier.Dequeue();
if (current == goal)
{
break;
}
foreach (Vector3Int direction in Directions)
{
Vector3Int next = current + direction;
if (!cameFrom.ContainsKey(next) && IsWalkable(next))
{
cameFrom[next] = current;
frontier.Enqueue(next);
}
}
}
if (!cameFrom.ContainsKey(goal))
{
return path;
}
Vector3Int step = goal;
while (step != start)
{
path.Add(floor.GetCellCenterWorld(step));
step = cameFrom[step];
}
path.Reverse();
return path;
}
}
A função devolve a lista de centros de tile do início ao destino (sem o tile de partida). O inimigo anda até o primeiro, remove da lista e segue para o próximo, com Vector2.MoveTowards. Recalcule só quando o jogador muda de célula, não a cada frame. O maxSearchNodes impede uma busca infinita num mapa enorme.
Para custos diferentes por terreno (lama mais lenta), troque a fila por uma fila de prioridade e você tem o A*. A lógica de grade com heurística está explicada no post de AStarGrid2D no Godot, e o raciocínio é o mesmo em C#.
Erros comuns: agente girando para o eixo Z, bake vazio
Sprite gira ou deita. Faltou updateRotation = false e updateUpAxis = false. Configure no Awake, antes do primeiro frame.
Bake vazio, nada azul. A superfície não foi rotacionada para XY, o Tilemap de chão não tem Navigation Modifier com Walkable, ou faltou o componente Navigation CollectSources2d.
Erro dizendo que o agente não está no NavMesh. O agente está num Z diferente da malha ou começa em cima de uma parede. Coloque o inimigo no centro de um tile de chão e mantenha o Z igual.
Agente atravessa paredes. As paredes entraram como Walkable ou o raio do agente é grande para a espessura delas.
Conflito depois de instalar. Dois pacotes de componentes de navegação no mesmo projeto podem disputar o editor. Use a superfície de um só por cena e confira o namespace dos scripts que referenciam a superfície.
Próximo passo
Monte a masmorra com dois Tilemaps, faça o bake com o NavMeshPlus e ponha três inimigos com o TopDownNavAgent perseguindo o jogador. Depois, num jogo de grade, troque pelo TilemapPathfinder e compare: qual dos dois combina com o movimento do seu jogo? Para o lado 3D do mesmo sistema, com links, obstáculos e rebake, o post de NavMesh na Unity 6 cobre o pacote oficial.
Perguntas frequentes
A Unity tem NavMesh 2D oficial?
Não como componente pronto para Tilemap e sprites. O NavMesh da Unity trabalha num plano horizontal 3D e coleta malhas e colliders 3D. Para jogo 2D, a solução mais usada é o NavMeshPlus, um projeto aberto que estende os componentes de NavMesh para ler Tilemap, sprites e Collider2D.
O que é o NavMeshPlus?
Um fork de código aberto (licença MIT) dos componentes de NavMesh da Unity com extensões para 2D. Ele gera a malha de navegação a partir de Tilemaps, sprites e colliders 2D, e o NavMeshAgent padrão da Unity anda sobre ela. Instala pelo Package Manager com a URL do repositório.
Por que o NavMeshAgent gira o sprite no eixo errado no 2D?
Porque o agente assume que o "para cima" é o eixo Y de um mundo 3D e tenta alinhar o objeto à malha. Em 2D, desligue agent.updateRotation e agent.updateUpAxis logo no Awake; a orientação do sprite fica por conta do seu script.
Preciso de NavMesh para pathfinding em jogo 2D de grid?
Nem sempre. Se o personagem anda de tile em tile, uma busca em largura ou um A* sobre o próprio Tilemap resolve com poucas linhas e sem pacote externo. NavMesh compensa quando o movimento é livre, sem grade, e o mapa tem formas irregulares.



