Voltar para o Blog
Quest Log

Unity Behavior: Árvore de Comportamento Visual para IA

Grafo de nós conectados ao lado de um inimigo 3D patrulhando entre pontos marcados no chão

Behavior tree na Unity com o pacote Unity Behavior: Blackboard, Behavior Graph, patrulha e perseguição visual e um nó de ação próprio em C#, no Unity 6.3.

Ferramenta:Unity

Unity Behavior: Árvore de Comportamento Visual para IA

Uma behavior tree na Unity (a unity behavior tree que aparece em todo tutorial de IA de inimigo) é uma árvore de decisões que o agente percorre várias vezes por segundo: "consigo ver o jogador? Então persigo. Não consigo? Então patrulho". O pacote oficial Unity Behavior (com.unity.behavior) deixa você montar essa árvore num editor visual de grafo, com Blackboard para os dados e nós prontos de navegação, e criar seus próprios nós em C# quando os prontos não bastam.

Aqui você monta um inimigo 3D que patrulha entre pontos e persegue o jogador quando o vê, cria um nó de ação próprio em C#, depura a árvore em Play Mode e, no fim, entende quando não usar nada disso.

Behavior tree vs máquina de estados

Uma máquina de estados (FSM) responde "em que estado estou e para onde posso ir". Patrulha vai para Perseguição quando vê o jogador, Perseguição volta para Patrulha quando perde. Com 3 estados, você tem poucas transições. Com 8 estados, cada um podendo ir para vários outros, as transições explodem e mudar uma regra quebra outras.

Uma behavior tree responde "o que é mais importante fazer agora". A árvore é avaliada de cima para baixo, por prioridade: fugir vem antes de atacar, que vem antes de perseguir, que vem antes de patrulhar. Adicionar um comportamento novo é encaixar um galho na posição de prioridade certa, sem mexer nos outros.

Os dois blocos que você precisa entender:

  • Sequence (sequência): executa os filhos em ordem e falha assim que um falha. É o "E": vire para o alvo E ande até ele.
  • Try In Order (seletor): tenta os filhos em ordem e para no primeiro que der certo. É o "OU": persiga OU patrulhe. No Unity Behavior o seletor clássico tem esse nome.

Cada nó devolve um status: Running (ainda executando), Success ou Failure. Toda a árvore é feita desses três resultados subindo pelos galhos.

Instalar o Unity Behavior

  1. Abra Window > Package Management > Package Manager.
  2. No painel da esquerda, escolha Unity Registry.
  3. Busque por Behavior e clique em Install.

Conferido em outubro de 2026: o manual do Unity 6.3 lista o pacote com.unity.behavior na versão 1.0.16, com estado released para o editor 6000.3. Se você está em outra versão do editor, o Package Manager mostra a versão compatível.

Para os nós de navegação usarem NavMesh, instale também o pacote AI Navigation, adicione um NavMesh Surface ao chão da cena e clique em Bake. No inimigo, coloque um NavMeshAgent. É ele que faz o inimigo contornar paredes em vez de andar em linha reta contra elas.

Behavior Graph: Blackboard, Agent e nós

São três conceitos.

Behavior Graph é o asset que guarda a árvore. Crie com botão direito no Project, Create > Behavior > Behavior Graph. Dê o nome EnemyAI e dê dois cliques para abrir o editor.

Blackboard é a memória do grafo: variáveis com nome e tipo que os nós leem e escrevem. Ela fica no painel lateral do editor. Crie estas variáveis clicando no +:

  • Target do tipo GameObject: o jogador.
  • Waypoints do tipo List de GameObject: os pontos de patrulha.
  • CanSeeTarget do tipo Boolean: se o inimigo está vendo o jogador agora.

O Blackboard já vem com uma variável Self, que representa o próprio GameObject que roda o grafo.

Behavior Agent é o componente que roda o grafo num GameObject. No inimigo, clique em Add Component, busque Behavior Agent e arraste o asset EnemyAI para o campo Graph. No Inspector do agente aparecem as variáveis do Blackboard, e é lá que você arrasta os waypoints da cena e o jogador.

Montar patrulha e perseguição visual

A árvore que vamos montar, de cima para baixo:

  1. On Start, com a opção Repeat ligada no Inspector do nó, para a árvore recomeçar sempre que terminar.
  2. Restart com a condição Variable Value Changed apontando para CanSeeTarget. Ele reinicia o galho de baixo quando a visão muda, então o inimigo larga a patrulha no meio no instante em que vê o jogador.
  3. Conditional Branch com a condição Variable Comparison: CanSeeTarget igual a true.
  4. Na saída True, um Sequence com dois filhos: o nosso nó Look At Target (criado na próxima seção) e Navigate To Target, com Self como agente e Target como destino.
  5. Na saída False, o nó Patrol, com Self como agente e Waypoints como a lista de pontos.

Para adicionar cada nó, clique com o botão direito no fundo do editor e use Add: os nós de navegação ficam em Action > Navigation, e Sequence, Conditional Branch e Restart ficam em Flow. Ligue as portas arrastando da saída de um nó para a entrada do outro.

Falta preencher o CanSeeTarget. Ver o jogador é física e geometria, e isso é mais claro em C# do que em nós. Crie EnemySight.cs no inimigo:

using Unity.Behavior;
using UnityEngine;

[RequireComponent(typeof(BehaviorGraphAgent))]
public class EnemySight : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField] private float viewDistance = 12f;
    [SerializeField] private float viewAngle = 90f;
    [SerializeField] private float eyeHeight = 1.6f;
    [SerializeField] private LayerMask obstacleMask;
    [SerializeField] private float checkInterval = 0.2f;

    private BehaviorGraphAgent agent;
    private bool lastCanSee;
    private float nextCheckTime;

    private void Awake()
    {
        agent = GetComponent<BehaviorGraphAgent>();
    }

    private void Start()
    {
        agent.SetVariableValue("Target", target.gameObject);
        agent.SetVariableValue("CanSeeTarget", false);
    }

    private void Update()
    {
        if (Time.time < nextCheckTime)
        {
            return;
        }
        nextCheckTime = Time.time + checkInterval;

        bool canSee = CanSeeTarget();
        if (canSee != lastCanSee)
        {
            lastCanSee = canSee;
            agent.SetVariableValue("CanSeeTarget", canSee);
        }
    }

    private bool CanSeeTarget()
    {
        Vector3 eye = transform.position + Vector3.up * eyeHeight;
        Vector3 targetPoint = target.position + Vector3.up * (eyeHeight * 0.5f);
        Vector3 toTarget = targetPoint - eye;
        float distance = toTarget.magnitude;

        if (distance > viewDistance)
        {
            return false;
        }

        if (distance < 0.01f)
        {
            return true;
        }

        if (Vector3.Angle(transform.forward, toTarget) > viewAngle * 0.5f)
        {
            return false;
        }

        return !Physics.Raycast(eye, toTarget / distance, distance, obstacleMask, QueryTriggerInteraction.Ignore);
    }
}

SetVariableValue é o método do BehaviorGraphAgent que escreve no Blackboard daquele agente. O script só escreve quando o valor muda, o que importa porque o nó Restart reage a mudança: escrever true a cada frame não reiniciaria nada, mas deixaria o fluxo mais difícil de seguir. A checagem roda a cada 0,2 s, não todo frame: visão de inimigo não precisa de precisão de 60 Hz, e dez inimigos fazendo raycast todo frame é custo à toa.

Repare quanto da IA já depende de coisas fora do grafo: física para a visão, NavMesh para andar, animação para parecer viva, vida e dano para o combate ter consequência. É nessa integração que projeto solo costuma parar, porque cada peça funciona sozinha e o conjunto não. Montar um jogo inteiro, com IA conversando com o resto dos sistemas, é exatamente o que um curso de Unity com projeto completo faz você praticar.

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

Criar um nó de ação em C#

Os nós prontos cobrem o básico. Quando falta algo, você cria um nó. No editor do grafo, clique com o botão direito e escolha Create new > Action. Dê o nome Look At Target, mantenha a categoria e escreva a frase do nó (a story): [Agent] turns to face [Target]. Cada palavra entre colchetes vira um campo ligado ao Blackboard. Ao clicar em Create, o pacote gera um script. Edite-o para ficar assim, em LookAtTargetAction.cs:

using System;
using Unity.Behavior;
using Unity.Properties;
using UnityEngine;
using Action = Unity.Behavior.Action;

[Serializable, GeneratePropertyBag]
[NodeDescription(name: "Look At Target", story: "[Agent] turns to face [Target]", category: "Action", id: "c41f0a2e9b7d4f3a8e15d6b2a0c9e7f1")]
public partial class LookAtTargetAction : Action
{
    [SerializeReference] public BlackboardVariable<GameObject> Agent;
    [SerializeReference] public BlackboardVariable<GameObject> Target;
    [SerializeReference] public BlackboardVariable<float> TurnSpeed = new BlackboardVariable<float>(360f);
    [SerializeReference] public BlackboardVariable<float> Tolerance = new BlackboardVariable<float>(5f);

    protected override Status OnStart()
    {
        if (Agent.Value == null || Target.Value == null)
        {
            return Status.Failure;
        }
        return Status.Running;
    }

    protected override Status OnUpdate()
    {
        Transform agentTransform = Agent.Value.transform;
        Vector3 toTarget = Target.Value.transform.position - agentTransform.position;
        toTarget.y = 0f;

        if (toTarget.sqrMagnitude < 0.0001f)
        {
            return Status.Success;
        }

        Quaternion desired = Quaternion.LookRotation(toTarget);
        agentTransform.rotation = Quaternion.RotateTowards(agentTransform.rotation, desired, TurnSpeed.Value * Time.deltaTime);

        bool aligned = Quaternion.Angle(agentTransform.rotation, desired) <= Tolerance.Value;
        return aligned ? Status.Success : Status.Running;
    }

    protected override void OnEnd()
    {
    }
}

O que cada parte faz:

  • using Action = Unity.Behavior.Action; evita a ambiguidade com System.Action. O gerador já coloca essa linha.
  • [GeneratePropertyBag] e partial existem porque o pacote gera código para ler os campos do nó. Mantenha os dois.
  • [NodeDescription] define nome, frase e categoria no menu. O id é gerado na criação e identifica o nó: não copie o id de outro nó.
  • Campos public com [SerializeReference] são a exceção à regra de usar [SerializeField] private: o pacote exige esse formato para ligar o campo ao Blackboard.
  • OnStart roda uma vez quando o nó começa. OnUpdate roda a cada tick enquanto devolver Running. OnEnd roda quando o nó termina ou é interrompido, bom para limpar estado.

O nó devolve Running enquanto gira e Success quando está alinhado, então a Sequence só passa para o Navigate To Target depois que o inimigo encarou o jogador. É um detalhe pequeno que faz a IA parecer que reagiu, em vez de sair andando de lado.

Depurar a árvore em Play Mode

O editor mostra a árvore rodando ao vivo:

  1. Com o grafo aberto, clique em Debug na barra superior do editor. O menu lista os agentes da cena que usam esse grafo.
  2. Escolha o inimigo que você quer acompanhar.
  3. Aperte Play. Os nós ganham ícones de status enquanto executam.

Deixe a janela do grafo e a Game view lado a lado. Ande na frente do inimigo e veja a execução pular do Patrol para a Sequence de perseguição. Se nada muda, o problema está antes do grafo: abra o Inspector do Behavior Agent em Play Mode e veja se CanSeeTarget está mudando. Se não muda, o defeito está no EnemySight (layer de obstáculo cobrindo o próprio jogador é o clássico).

Outros problemas comuns: Patrol parado porque a lista Waypoints está vazia no Inspector do agente, e Navigate To Target sem efeito porque o NavMesh não foi bakeado ou o inimigo nasceu fora dele.

Quando NÃO usar: inimigo simples

Behavior tree é ferramenta para IA com muitas decisões. Para um slime que anda até a borda e vira, ou um morcego que voa em seno e mergulha quando o jogador passa embaixo, ela é peso morto: mais um pacote, mais um editor, mais um lugar para o bug se esconder.

O critério que eu uso:

  • Até 3 ou 4 estados com transições óbvias: FSM em C#, com um enum e um switch. Lê-se em 30 segundos.
  • Muitas prioridades, galhos reaproveitados entre inimigos, designer ajustando sem programar: Unity Behavior.
  • Inimigo só com padrão de movimento: nem FSM. Um script com o padrão basta.

Se a sua IA cabe numa máquina de estados, o post de máquina de estados na Unity mostra o caminho mais curto. E para entender por que State, Observer e companhia aparecem tanto em código de jogo, vale ler padrões de projeto em jogos.

Próximo passo

Com patrulha e perseguição funcionando, o próximo degrau é dar prioridade a mais um comportamento: troque o Conditional Branch por um Try In Order com três galhos (atacar se perto, perseguir se vê, patrulhar), cada um protegido por uma condição. Depois, crie um segundo inimigo reaproveitando o mesmo grafo com outros valores no Blackboard. E se você também usa Godot, compare com a behavior tree no Godot: o conceito é idêntico, e perceber isso é o que faz o conhecimento valer em qualquer engine.

Perguntas frequentes

O que é o pacote Unity Behavior?

É o pacote oficial da Unity (com.unity.behavior) para criar IA com behavior trees num editor visual de grafo. Você monta a lógica com nós prontos (sequência, condição, navegação, espera) e pode criar nós próprios em C#. Ele é instalado pelo Package Manager.

Behavior tree ou máquina de estados: qual usar na Unity?

Máquina de estados é mais simples e basta para inimigos com 2 a 4 estados bem definidos. Behavior tree compensa quando a IA tem muitas decisões com prioridade (fugir se vida baixa, senão atacar se perto, senão perseguir, senão patrulhar) e você quer reaproveitar pedaços entre inimigos.

Como passar informação do C# para o Behavior Graph?

Pelo Blackboard. No componente BehaviorGraphAgent, chame SetVariableValue com o nome da variável e o valor, por exemplo agent.SetVariableValue("CanSeeTarget", true). O grafo lê essa variável nas condições e nos nós.

Unity Behavior funciona em jogo 2D?

A lógica do grafo (sequência, condição, Blackboard, nós próprios) funciona em qualquer jogo. Os nós de navegação prontos foram pensados para movimento em 3D, então em 2D o comum é escrever nós de ação próprios para andar e pular com Rigidbody2D.