Comportamento de NPC · de FSMs a LLMs

Comportamento de NPC: máquinas de estado, árvores de comportamento, GOAP e NPCs guiados por IA

Crie seu próprio comportamento de NPC

Como os NPCs decidem o que fazer? Máquinas de estado, árvores de comportamento, GOAP, IA utilitária e NPCs guiados por LLM — explicados com exemplos 3D ao vivo no navegador.

✦Jogo
Crie um comportamento de NPC

O comportamento de NPC é a lógica que transforma um modelo, uma barra de vida e uma cápsula de colisão em algo que parece desejar coisas. Ele responde a uma pergunta em loop, geralmente a cada quadro ou em um tick fixo: dado o que sei sobre o mundo, o que devo fazer em seguida? Todo o resto desta página — máquinas de estado finitas, árvores de comportamento, GOAP, IA utilitária, intenção guiada por LLM — é uma resposta diferente a essa pergunta, com custos diferentes e modos de falha diferentes.

Nós criamos NPCs que rodam em uma aba do navegador dentro do construtor de jogos 3D do Neta Studio. Essa restrição molda os conselhos abaixo. Não há servidor dedicado, nem thread de física separada, nem orçamento para um planejador que busca por 40 milissegundos a cada tick. Se uma técnica não puder ficar barata o suficiente para rodar ao lado da renderização em um notebook de médio porte, nós a rebaixamos ou a tiramos do caminho crítico. As seções a seguir descrevem o que realmente entregamos e onde cada abordagem quebra.

Resposta rápida: como um NPC decide

Um NPC decide executando uma função de decisão em uma cadência, lendo uma pequena fatia do estado do mundo (distância até o alvo, linha de visão, vida atual, última posição conhecida) e retornando uma ação que os sistemas de movimento e animação podem executar. A cadência importa tanto quanto a lógica. Decisões em um tick de 100 ms a 250 ms, com movimento e colisão a cada quadro, custam uma fração do que custa a lógica de decisão por quadro e são indistinguíveis para o jogador na maioria dos casos.

Os quatro modelos clássicos diferem em onde mora a complexidade. Uma máquina de estado codifica as decisões como transições entre estados nomeados. Uma árvore de comportamento as codifica como uma hierarquia de nós seletor e sequência. GOAP as codifica como um objetivo mais um conjunto de ações com pré-condições e efeitos, e busca por um plano. A IA utilitária as codifica como uma função de pontuação sobre cada ação candidata. NPCs guiados por LLM codificam a intenção de alto nível em linguagem natural e devolvem o resultado a um dos modelos mais baratos.

Na prática, você os combina. Um pontuador de utilidade escolhe o objetivo, uma árvore de comportamento ou máquina de estado o executa, um navmesh move o corpo, e o modelo de linguagem só toca no diálogo e na intenção. O resto desta página aborda cada camada por vez e nomeia o modo de falha específico que você vai encontrar.

Máquinas de estado (FSM)

Uma máquina de estado finita é o modelo útil mais barato e a escolha inicial certa para a maioria dos NPCs. Você define um conjunto de estados — Ocioso, Patrulha, Perseguição, Ataque, Fuga, Atordoado — e um conjunto de condições de transição entre eles. A cada tick, você avalia as transições a partir do estado atual e, se uma disparar, troca. O custo é proporcional ao número de transições verificadas, que é pequeno. O estado é uma única variável, o que torna trivial serializá-lo, depurá-lo e exibi-lo em uma sobreposição do editor.

O modo de falha é a oscilação. Um NPC a 5,1 metros do jogador transita de Patrulha para Perseguição, move-se para 4,9 metros, e uma transição simétrica o devolve para Patrulha. Ele vibra na fronteira para sempre. A correção é a histerese: entre em Perseguição a 5,0 metros e saia a 6,5 metros. O mesmo problema aparece em verificações de linha de visão que oscilam quando o jogador se move lateralmente atrás de um pilar, e a mesma correção se aplica — exija que a condição se mantenha por N ticks consecutivos, ou adicione um cronômetro de memória que mantenha o NPC em Perseguição por dois segundos após o último avistamento.

O segundo modo de falha é o emaranhado de transições. Cinco estados precisam de dez transições; dez estados precisam de quase quarenta e cinco. Cada novo estado deve ser conectado a todos os estados dos quais ele pode ser alcançado, e as condições se sobrepõem de maneiras difíceis de raciocinar. Quando sua tabela de transições deixa de caber em uma tela, esse é o sinal para migrar para uma árvore de comportamento.

Um FSM concreto para um guarda de patrulha: Ocioso (espera 2 s) → Patrulha (caminha até o próximo ponto de referência) → Alerta (vira-se na direção do ruído, 1,5 s) → Perseguição (move-se até a última posição conhecida na velocidade de corrida) → Ataque (se estiver a menos de 2 m e o tempo de recarga tiver expirado) → Busca (perambula perto da última posição conhecida por 8 s) → Patrulha. Sete estados, cerca de uma dúzia de transições, e o cronômetro de memória em Busca é o que faz tudo parecer deliberado em vez de nervoso.

  • Estado de variável únicaO estado atual é um único enum. Depurar é ler um valor, não reconstruir a intenção a partir de uma pilha.
  • Histerese em cada limiarQualquer distância ou cronômetro que possa transitar nos dois sentidos precisa de valores diferentes de entrada e saída, ou o NPC oscila na fronteira.
  • Orçamento de transiçõesMantenha os estados abaixo de cerca de dez antes que a tabela se torne impossível de manter. Depois disso, mude para uma árvore de comportamento ou divida em sub-máquinas.

Árvores de comportamento

Uma árvore de comportamento substitui a tabela de transições por uma hierarquia. Os nós folha são ações (MoveTo, PlayAnimation, Wait) e condições (IsPlayerVisible, HealthBelow). Dois tipos de nós compostos fazem a maior parte do trabalho. Um seletor tenta seus filhos em ordem e retorna sucesso assim que um tiver sucesso — esta é a sua lista de prioridades. Uma sequência executa seus filhos em ordem e falha assim que um falhar — este é o seu procedimento passo a passo.

O ganho de escala é a composabilidade. Adicionar um novo comportamento significa adicionar uma subárvore sob um seletor, não conectar um novo estado a todos os estados dos quais ele pode ser alcançado. As prioridades são explícitas na ordem dos filhos, o que é muito mais fácil de ler do que um conjunto de condições booleanas sobrepostas. As subárvores podem ser reutilizadas: o mesmo ramo 'investigar ruído' funciona para um guarda, um cão e um civil.

As árvores não são automaticamente mais rápidas. Uma implementação ingênua reavalia a árvore inteira a cada tick, e é por isso que você continua seguindo a cadência de decisão de 100–250 ms e deixa a ação em execução comandar o movimento a cada quadro. Ações de longa duração precisam de um estado em execução que a árvore lembre; caso contrário, uma ação reinicia do zero a cada tick e o NPC desliza de volta ao início do seu caminho. A maioria dos motores que trazem uma árvore de comportamento — o pacote Behavior da Unity, a árvore integrada da Unreal, ou uma pequena implementação personalizada em TypeScript — lida com isso com um valor de retorno em execução, mas é a primeira coisa a verificar quando um NPC apresenta engasgos.

Uma estrutura de seletor para um NPC de combate, em ordem de prioridade: (1) se a vida estiver abaixo de 25 por cento, sequência [Fugir para cobertura, Curar]; (2) se o jogador estiver visível e a menos de 2 m, sequência [Encarar o jogador, Atacar]; (3) se o jogador estiver visível, sequência [MoveTo posição de flanqueamento, Atacar]; (4) se a última posição conhecida estiver definida, Buscar; (5) Patrulhar. O seletor escolhe o primeiro ramo cuja condição passa. Reordenar esses filhos é como você ajusta a agressividade sem tocar no código.

  • Nó seletorRetorna sucesso no primeiro filho que tiver sucesso. Use-o para prioridades de fallback: tente o comportamento mais específico, depois o próximo, depois o padrão.
  • Nó sequênciaFalha no primeiro filho que falhar. Use-o para procedimentos ordenados em que o passo dois não faz sentido se o passo um não aconteceu.
  • Estado em execuçãoAções longas devem retornar Running e retomar de onde pararam. Se retornarem Success imediatamente, a árvore as reinicia a cada tick e o movimento quebra.
  • BlackboardUm armazenamento compartilhado de chave-valor para fatos que a árvore lê e grava — posição do alvo, nível de alerta, expiração do tempo de recarga. Mantenha-o pequeno e tipado.

GOAP (planejamento de ações orientado a objetivos)

O GOAP inverte o modelo. Em vez de descrever quando transitar, você descreve o que as ações fazem e o que elas exigem, e um planejador busca por uma sequência que alcance um objetivo. Cada ação tem pré-condições, efeitos e um custo. 'Atacar' exige HasWeapon e InRange, e define TargetDead. 'GetWeapon' exige WeaponAvailable e define HasWeapon. 'MoveToTarget' exige TargetKnown e define InRange. O planejador começa pelo objetivo, trabalha de trás para frente e encontra a cadeia de ações mais barata cujos efeitos o satisfazem.

Isso compensa quando o estado do mundo é rico e a sequência correta não é óbvia. Um NPC que precisa quebrar a linha de visão, recarregar, reposicionar-se e então engajar produz um plano que o designer nunca escreveu explicitamente, porque o planejador descobriu a ordem a partir do grafo de pré-condições. Também compensa em produção, porque adicionar um novo item ou habilidade significa adicionar uma definição de ação, não editar cada máquina de estado que deveria conhecê-lo.

O custo é a busca. Um planejador ingênuo explora combinações de ações até encontrar um plano, o que pode explodir à medida que você adiciona ações e variáveis de estado. A mitigação padrão é uma heurística — A* sobre o grafo de estados com uma estimativa de distância até o objetivo — mais um limite estrito de expansão. Em nossos próprios contextos de navegador, os planos são calculados no tick de decisão com um limite de algumas centenas de expansões de nós, e tudo o que ultrapassa isso recorre a um plano em cache ou a um ramo simples de árvore de comportamento. O limite honesto é este: o GOAP vale o custo de busca quando o número de sequências de ações significativas é grande, e é sobrecarga desperdiçada quando o NPC só faz três coisas.

  • Pré-condições e efeitosCada ação declara o que deve ser verdadeiro para ser executada e o que ela altera. O planejador raciocina sobre essas declarações, não sobre o código.
  • Busca retroativa a partir do objetivoComece no estado desejado e encontre ações cujos efeitos o produzam, depois recorra às suas pré-condições. Geralmente mais barato que a busca progressiva.
  • Limite de expansãoLimite as expansões de nós por chamada de planejamento. Ultrapasse o limite e recorra — nunca deixe um planejador travar um quadro.
  • Cache de planosReplaneje apenas quando o estado do mundo mudar de forma significativa. Situações estáticas reutilizam o plano anterior a custo zero.

IA utilitária

A IA utilitária pontua cada opção e escolhe a mais alta. Cada ação candidata tem uma função que mapeia o estado do mundo para um número, normalmente normalizado de zero a um. 'Fugir' pontua alto quando a vida está baixa e os inimigos estão perto. 'Atacar' pontua alto quando o alvo está ao alcance e a munição está acima de zero. 'Perambular' pontua moderadamente quando nada mais é urgente. O NPC pega o argmax, opcionalmente com uma pequena variação aleatória para que os empates se resolvam de forma imprevisível.

Este é o modelo que produz os personagens de aparência mais surpreendente, porque o comportamento emerge da interação das curvas em vez de regras explícitas. Dois NPCs com pesos de pontuação ligeiramente diferentes se comportam de forma perceptivelmente diferente sem qualquer código de ramificação, o que o torna a opção natural para variação de personalidade. Também é o modelo mais fácil de ajustar em tempo de execução: exponha os pesos em um painel de depuração e arraste-os enquanto o jogo roda.

O modo de falha é uma utilidade plana ou ruidosa. Se toda ação pontuar 0,4, o NPC parece indeciso e troca constantemente. Se a pontuação mais alta oscilar a cada tick entre dois valores próximos, você tem a mesma oscilação de uma máquina de estado ruim. A correção padrão é uma janela de compromisso: uma vez escolhida uma ação, comprometa-se no mínimo por uma duração fixa — 500 ms é um padrão razoável — a menos que a pontuação mais alta supere a da ação comprometida por uma margem significativa. As curvas também importam. Curvas de resposta lineares para a vida produzem um NPC que foge exatamente no mesmo momento em toda luta. Curvas exponenciais ou logísticas produzem limiares que se movem com a situação.

  • Funções de pontuação por açãoCada ação mapeia o estado do mundo para uma utilidade de zero a um. Mantenha-as independentes para que uma possa ser reajustada sem tocar nas outras.
  • Janela de compromissoUma vez escolhida, mantenha uma ação por uma duração mínima. Evita a troca constante a cada tick entre pontuações quase iguais.
  • Pesos ajustáveisExponha os pesos ao vivo. Dois NPCs que diferem apenas nos pesos são percebidos como personagens diferentes, o que é o sistema de personalidade mais barato disponível.
  • Curvas de respostaCurvas lineares criam limiares fixos. Curvas logísticas fazem o NPC responder à situação em vez de a um número mágico.

Navegação e colisão

A lógica de decisão escolhe um destino. A navegação leva o corpo até lá. Um navmesh é uma superfície caminhável triangulada; o pathfinding executa A* sobre seu grafo de polígonos, e o resultado é uma lista de arestas de polígonos que o NPC segue. Nós construímos o nosso no momento do carregamento a partir da geometria do nível e o colocamos em cache, porque reconstruí-lo a cada quadro é a maneira mais fácil de destruir a taxa de quadros em um jogo de navegador.

A direção (steering) cuida dos últimos metros e do movimento momento a momento. Seek e arrive produzem o vetor para frente, a separação empurra o NPC para longe de agentes próximos, e o desvio de obstáculos curva o caminho ao redor de bloqueios dinâmicos, como uma porta ou outro personagem. Execute a direção a cada quadro; execute o pathfinding apenas quando o destino mudar ou o caminho for invalidado. Um caminho recalculado a cada quadro para cada NPC é um custo comum e evitável.

A colisão é a passagem que impede um NPC de atravessar uma parede. O navmesh mantém o caminho em terreno caminhável, mas a direção e a física ainda podem empurrar um personagem para dentro da geometria, então o controlador do personagem resolve a penetração contra os colisores depois do movimento. Em nossos mundos isométricos — Mini World e The Cyclops' Island — é daí que vêm a maioria dos bugs visíveis de NPC: um caminho que passa por uma fenda mais estreita que o raio do personagem, ou um vetor de direção que empurra um agente para dentro de uma parede em um canto. Definir o raio do agente corretamente no navmesh, e manter a força de separação abaixo da força de empurrão do controlador, resolve a maior parte disso.

1

01

Gere o navmesh no momento do carregamento a partir da geometria estática e coloque-o em cache. Não o reconstrua a cada quadro.

2

02

Defina o raio e a altura do agente para corresponder à cápsula de colisão real do personagem, não à caixa delimitadora do modelo.

3

03

Execute o pathfinding apenas quando o destino mudar, o caminho estiver bloqueado, ou o NPC tiver se desviado do caminho além de um limiar.

4

04

Execute direção, separação e desvio de obstáculos a cada quadro com uma força de separação limitada.

5

05

Resolva a colisão contra os colisores depois do movimento e, em seguida, encaixe o personagem de volta no navmesh.

6

06

Registre e visualize o caminho e o navmesh em uma sobreposição de depuração — os bugs geralmente são espaciais, não lógicos.

NPCs guiados por LLM

Um modelo de linguagem muda o panorama na camada de intenção e diálogo, não na camada de navegação. Pathfinding, direção e colisão permanecem exatamente como eram, porque um modelo que leva centenas de milissegundos para responder não pode comandar um corpo que precisa se mover a cada quadro. O que o modelo pode fazer é decidir qual objetivo faz sentido dadas as palavras do jogador, gerar texto de resposta no personagem e escolher entre opções de alto nível quando a situação é ambígua e as regras escritas à mão produziriam algo sem graça.

Uma arquitetura viável separa as duas velocidades. Reações imediatas rodam na máquina de estado ou no pontuador de utilidade — atacar, fugir, buscar cobertura — porque precisam acontecer dentro de um ou dois quadros. A intenção deliberada passa pelo modelo em uma cadência muito mais lenta, talvez uma vez por interação ou a cada poucos segundos, e sua saída é um comando estruturado que a camada rápida executa: definir objetivo como Seguir, definir atitude em relação ao jogador como Hostil, definir fala do diálogo. O modelo nunca toca na posição diretamente.

Os limites honestos são latência, custo e determinismo. Os tempos de resposta são longos o suficiente para que o NPC precise reconhecer a entrada imediatamente com um gesto ou uma fala curta pré-gravada, ou parecerá defeituoso. O custo escala com a frequência das chamadas, então você agrupa e coloca em cache. E, como a saída é não determinística, você deve validá-la contra um esquema antes que ela chegue ao jogo — uma resposta em texto livre que falha ao ser interpretada deve recorrer a uma fala padrão, não quebrar o sistema de diálogo. Trate o modelo como um serviço não confiável por trás de uma interface tipada, e a integração se sustenta.

  • Saída de comando tipadaO modelo retorna intenção estruturada, não texto em prosa que o código do jogo interpreta. Valide contra um esquema e rejeite qualquer coisa malformada.
  • Loop de duas velocidadesLógica local rápida para reações, chamadas lentas ao modelo para deliberação. Nunca coloque uma ida e volta pela rede no caminho de movimento.
  • Reconhecimento imediatoExecute um gesto ou uma fala curta pré-gravada assim que a entrada chegar e substitua-a quando o modelo responder. Oculta a latência.
  • Fallback eleganteSe a chamada falhar ou expirar, recorra à resposta padrão da máquina de estado. O NPC nunca deve travar esperando um modelo.
Ashenmere — leia as decisões do oponente enquanto você duela.
The Cyclops' Island — navegação e colisão em uma grade isométrica.

Entre no mundo ao qual ele pertence

Um sprite é um personagem sem um lugar onde ficar. O último passo é o que ninguém mais oferece: pegue a folha de sprites que você acabou de criar e coloque-a em um mundo pelo qual alguém possa caminhar — uma pequena ilha, um planeta, uma esquina chuvosa. Todo mundo neste site foi feito a partir de uma descrição escrita e publicado como uma página que você pode jogar.

Voltar ao construtor de jogos 3D

Todos os 31 mundos, jogos e cenas 3D finalizados ficam em uma única página — todos eles jogáveis.

Plataforma: Neta StudioPágina: Comportamento de NPC: máquinas de estado, árvores de comportamento, GOAP e NPCs guiados por IALeia a seguir: /explore/guide/what-is-a-non-player-character · /explore/guide/what-are-npcs · /explore/guide/ai-npc
×