Comportamiento de NPC · de las FSM a los LLM

Comportamiento de NPC: máquinas de estados, árboles de comportamiento, GOAP y NPC impulsados por AI

Crea tu propio comportamiento de NPC

¿Cómo deciden los NPC qué hacer? Máquinas de estados, árboles de comportamiento, GOAP, AI utilitaria y NPC impulsados por LLM — explicados con ejemplos 3D en vivo en el navegador.

✦Juego
Crea un comportamiento de NPC

El comportamiento de NPC es la lógica que convierte un modelo, una barra de salud y una cápsula de colisión en algo que parece querer cosas. Responde a una sola pregunta en bucle, normalmente cada fotograma o en un tick fijo: dado lo que sé del mundo, ¿qué debería hacer después? Todo lo demás en esta página — máquinas de estados finitos, árboles de comportamiento, GOAP, AI utilitaria, intención impulsada por LLM — es una respuesta distinta a esa pregunta, con costes y modos de fallo distintos.

Construimos NPC que se ejecutan en una pestaña del navegador dentro del constructor de juegos 3D de Neta Studio. Esa restricción condiciona los consejos que siguen. No hay servidor dedicado, ni hilo de física aparte, ni presupuesto para un planificador que busque durante 40 milisegundos en cada tick. Si una técnica no puede abaratarse lo suficiente para ejecutarse junto al renderizado en un portátil de gama media, o la simplificamos o la sacamos de la ruta crítica. Las secciones siguientes describen lo que publicamos de verdad y dónde se rompe cada enfoque.

Respuesta rápida: cómo decide un NPC

Un NPC decide ejecutando una función de decisión según un calendario, leyendo una pequeña porción del estado del mundo (distancia al objetivo, línea de visión, salud actual, última posición conocida) y devolviendo una acción que los sistemas de movimiento y animación pueden ejecutar. El calendario importa tanto como la lógica. Las decisiones en un tick de 100 a 250 ms, con movimiento y colisión cada fotograma, cuestan una fracción de lo que cuesta la lógica de decisión por fotograma y, en la mayoría de los casos, son indistinguibles para el jugador.

Los cuatro modelos clásicos se diferencian en dónde vive la complejidad. Una máquina de estados codifica las decisiones como transiciones entre estados con nombre. Un árbol de comportamiento las codifica como una jerarquía de nodos selectores y nodos secuencia. GOAP las codifica como un objetivo más un conjunto de acciones con precondiciones y efectos, y busca un plan. La AI utilitaria las codifica como una función de puntuación sobre cada acción candidata. Los NPC impulsados por LLM codifican la intención de alto nivel en lenguaje natural y devuelven el resultado a uno de los modelos más baratos.

En la práctica, se combinan. Un puntuador de utilidad elige el objetivo, un árbol de comportamiento o una máquina de estados lo ejecuta, un navmesh mueve el cuerpo, y el modelo de lenguaje solo toca el diálogo y la intención. El resto de esta página repasa cada capa por turno y nombra el modo de fallo concreto con el que te encontrarás.

Máquinas de estados (FSM)

Una máquina de estados finitos es el modelo útil más barato y la primera elección correcta para la mayoría de los NPC. Defines un conjunto de estados — Inactivo, Patrulla, Persecución, Ataque, Huida, Aturdido — y un conjunto de condiciones de transición entre ellos. En cada tick evalúas las transiciones que salen del estado actual y, si una se cumple, cambias. El coste es proporcional al número de transiciones que compruebas, que es pequeño. El estado es una sola variable, lo que hace trivial serializarlo, depurarlo y mostrarlo en una superposición del editor.

El modo de fallo es la oscilación. Un NPC a 5,1 metros del jugador pasa de Patrulla a Persecución, se acerca a 4,9 metros y una transición simétrica lo devuelve a Patrulla. Vibra en el límite para siempre. La solución es la histéresis: entrar en Persecución a 5,0 metros y salir a 6,5 metros. El mismo problema aparece con las comprobaciones de línea de visión que parpadean cuando el jugador se mueve tras una columna, y se aplica la misma solución: exigir que la condición se mantenga durante N ticks consecutivos, o añadir un temporizador de memoria que mantenga al NPC en Persecución durante dos segundos tras el último avistamiento.

El segundo modo de fallo es el enredo de transiciones. Cinco estados necesitan diez transiciones; diez estados se acercan a cuarenta y cinco. Cada estado nuevo debe conectarse con todos los estados desde los que se puede llegar a él, y las condiciones se solapan de formas difíciles de razonar. Cuando tu tabla de transiciones deja de caber en una pantalla, esa es la señal para pasar a un árbol de comportamiento.

Una FSM concreta para un guardia de patrulla: Inactivo (esperar 2 s) → Patrulla (caminar hasta el siguiente punto de ruta) → Alerta (girar hacia el ruido, 1,5 s) → Persecución (moverse a la última posición conocida a velocidad de carrera) → Ataque (si está a menos de 2 m y el enfriamiento ha terminado) → Búsqueda (vagar cerca de la última posición conocida durante 8 s) → Patrulla. Siete estados, aproximadamente una docena de transiciones, y es el temporizador de memoria en Búsqueda lo que hace que se sienta deliberado en lugar de nervioso.

  • Estado en una sola variableEl estado actual es una única enumeración. Depurar es leer un valor, no reconstruir la intención a partir de una pila.
  • Histéresis en cada umbralCualquier distancia o temporizador que pueda cambiar en ambos sentidos necesita valores de entrada y de salida distintos, o el NPC oscila en el límite.
  • Presupuesto de transicionesMantén los estados por debajo de unos diez antes de que la tabla se vuelva inmantenible. Más allá de eso, pasa a un árbol de comportamiento o divídela en submáquinas.

Árboles de comportamiento

Un árbol de comportamiento reemplaza la tabla de transiciones por una jerarquía. Los nodos hoja son acciones (MoveTo, PlayAnimation, Wait) y condiciones (IsPlayerVisible, HealthBelow). Dos tipos de nodos compuestos hacen casi todo el trabajo. Un selector prueba a sus hijos en orden y devuelve éxito en cuanto uno tiene éxito: esta es tu lista de prioridades. Una secuencia ejecuta a sus hijos en orden y falla en cuanto uno falla: este es tu procedimiento paso a paso.

La ventaja de escalado es la composabilidad. Añadir un comportamiento nuevo significa añadir un subárbol bajo un selector, no conectar un estado nuevo con todos los estados desde los que se puede llegar a él. Las prioridades son explícitas en el orden de los hijos, que es mucho más fácil de leer que un conjunto de condiciones booleanas solapadas. Los subárboles se pueden reutilizar: la misma rama «investigar ruido» sirve para un guardia, un perro y un civil.

Los árboles no son automáticamente más rápidos. Una implementación ingenua reevalúa todo el árbol cada vez que recibe un tick, por eso conviene seguir haciéndolo en el calendario de decisiones de 100–250 ms y dejar que la acción que se está ejecutando gobierne el movimiento cada fotograma. Las acciones de larga duración necesitan un estado de ejecución que el árbol recuerde, porque de lo contrario una acción se reinicia desde cero en cada tick y el NPC se desliza de vuelta al inicio de su ruta. La mayoría de los motores que incluyen un árbol de comportamiento — el paquete Behavior de Unity, el árbol integrado de Unreal o uno pequeño propio en TypeScript — resuelven esto con un valor de retorno Running, pero es lo primero que hay que verificar cuando un NPC se entrecorta.

Una estructura de selector para un NPC de combate, en orden de prioridad: (1) si la salud está por debajo del 25 por ciento, secuencia [Huir a cubierto, Curarse]; (2) si el jugador es visible y está a menos de 2 m, secuencia [Girar hacia el jugador, Atacar]; (3) si el jugador es visible, secuencia [Moverse a una posición de strafe, Atacar]; (4) si hay una última posición conocida, Buscar; (5) Patrullar. El selector elige la primera rama cuya condición se cumple. Reordenar esos hijos es la forma de ajustar la agresividad sin tocar el código.

  • Nodo selectorDevuelve éxito en el primer hijo que tiene éxito. Úsalo para prioridades de reserva: prueba primero el comportamiento más específico, luego el siguiente y por último el predeterminado.
  • Nodo secuenciaFalla en el primer hijo que falla. Úsalo para procedimientos ordenados en los que el segundo paso carece de sentido si no ocurrió el primero.
  • Estado de ejecuciónLas acciones largas deben devolver Running y reanudarse donde se detuvieron. Si devuelven Success de inmediato, el árbol las reinicia en cada tick y el movimiento se rompe.
  • BlackboardUn almacén compartido de clave-valor para los hechos que el árbol lee y escribe: posición del objetivo, nivel de alerta, fin del enfriamiento. Mantenlo pequeño y tipado.

GOAP (planificación de acciones orientada a objetivos)

GOAP le da la vuelta al modelo. En lugar de describir cuándo transicionar, describes qué hacen las acciones y qué requieren, y un planificador busca una secuencia que alcance un objetivo. Cada acción tiene precondiciones, efectos y un coste. «Attack» requiere HasWeapon e InRange, y establece TargetDead. «GetWeapon» requiere WeaponAvailable y establece HasWeapon. «MoveToTarget» requiere TargetKnown y establece InRange. El planificador parte del objetivo, trabaja hacia atrás y encuentra la cadena de acciones más barata cuyos efectos lo satisfacen.

Esto compensa cuando el estado del mundo es rico y la secuencia correcta no es obvia. Un NPC que necesita romper la línea de visión, recargar, reposicionarse y luego entrar en combate produce un plan que el diseñador nunca escribió de forma explícita, porque el planificador descubrió el orden a partir del grafo de precondiciones. También compensa en producción, porque añadir un objeto o una habilidad nueva significa añadir una definición de acción, no editar cada máquina de estados que debería conocerla.

El coste es la búsqueda. Un planificador ingenuo explora combinaciones de acciones hasta encontrar un plan, lo que puede dispararse a medida que añades acciones y variables de estado. La mitigación estándar es una heurística — A* sobre el grafo de estados con una estimación de distancia al objetivo — más un límite estricto de expansiones. En nuestros propios contextos de navegador, los planes se calculan en el tick de decisión con un tope de unos pocos cientos de expansiones de nodos, y todo lo que lo supere recurre a un plan en caché o a una rama sencilla de árbol de comportamiento. El límite honesto es este: GOAP vale el coste de búsqueda cuando el número de secuencias de acciones significativas es grande, y es sobrecarga desperdiciada cuando el NPC solo hace tres cosas.

  • Precondiciones y efectosCada acción declara qué debe ser cierto para ejecutarse y qué cambia. El planificador razona sobre esas declaraciones, no sobre el código.
  • Búsqueda hacia atrás desde el objetivoEmpieza en el estado deseado y encuentra las acciones cuyos efectos lo producen, luego recurre sobre sus precondiciones. Suele ser más barato que la búsqueda hacia adelante.
  • Límite de expansiónLimita las expansiones de nodos por llamada de planificación. Si superas el límite, recurre a otra cosa: nunca dejes que un planificador bloquee un fotograma.
  • Caché de planesReplanifica solo cuando el estado del mundo cambie de forma significativa. Las situaciones estáticas reutilizan el plan anterior a coste cero.

AI utilitaria

La AI utilitaria puntúa cada opción y elige la más alta. Cada acción candidata tiene una función que asigna el estado del mundo a un número, normalmente normalizado de cero a uno. «Huir» puntúa alto cuando la salud es baja y hay enemigos cerca. «Atacar» puntúa alto cuando el objetivo está al alcance y la munición es mayor que cero. «Vagar» puntúa de forma moderada cuando nada más apremia. El NPC toma el argmax, opcionalmente con un pequeño ruido aleatorio para que los empates se resuelvan de forma impredecible.

Este es el modelo que produce los personajes de aspecto más sorprendente, porque el comportamiento emerge de la interacción de curvas en lugar de reglas explícitas. Dos NPC con pesos de puntuación ligeramente distintos se comportan de forma notablemente diferente sin ningún código con ramificaciones, lo que lo convierte en el encaje natural para la variación de personalidad. También es el modelo más fácil de ajustar en tiempo de ejecución: expón los pesos en un panel de depuración y arrástralos mientras el juego se ejecuta.

El modo de fallo es una utilidad plana o ruidosa. Si cada acción puntúa 0,4, el NPC parece indeciso y cambia constantemente. Si la puntuación más alta oscila en cada tick entre dos valores cercanos, obtienes la misma oscilación que con una mala máquina de estados. La solución estándar es una ventana de compromiso: una vez elegida una acción, comprométete con ella durante una duración mínima fija — 500 ms es un valor predeterminado razonable — a menos que la puntuación más alta supere la de la acción comprometida por un margen significativo. Las curvas también importan. Las curvas de respuesta lineales sobre la salud producen un NPC que huye exactamente en el mismo momento en cada combate. Las curvas exponenciales o logísticas producen umbrales que se mueven con la situación.

  • Funciones de puntuación por acciónCada acción asigna el estado del mundo a una utilidad de cero a uno. Mantenlas independientes para poder reajustar una sin tocar las demás.
  • Ventana de compromisoUna vez elegida, mantén una acción durante una duración mínima. Evita el vaivén por tick entre puntuaciones casi iguales.
  • Pesos ajustablesExpón los pesos en vivo. Dos NPC que solo difieren en los pesos se perciben como personajes distintos, que es el sistema de personalidad más barato disponible.
  • Curvas de respuestaLas curvas lineales crean umbrales fijos. Las curvas logísticas hacen que el NPC responda a la situación y no a un número mágico.

Navegación y colisión

La lógica de decisión elige un destino. La navegación lleva el cuerpo hasta allí. Un navmesh es una superficie transitable triangulada; la búsqueda de rutas ejecuta A* sobre su grafo de polígonos, y el resultado es una lista de aristas de polígonos que el NPC sigue. El nuestro lo construimos en tiempo de carga a partir de la geometría del nivel y lo guardamos en caché, porque reconstruirlo en cada fotograma es la forma más fácil de tumbar la tasa de fotogramas en un juego de navegador.

El steering se ocupa de los últimos metros y del movimiento momento a momento. Seek y arrive producen el vector de avance, la separación aleja al NPC de los agentes cercanos, y la evitación de obstáculos curva la ruta alrededor de bloqueadores dinámicos como una puerta u otro personaje. Ejecuta el steering cada fotograma; ejecuta la búsqueda de rutas solo cuando cambie el destino o se invalide la ruta. Una ruta recalculada en cada fotograma para cada NPC es un coste habitual y evitable.

La colisión es la pasada que impide que un NPC atraviese una pared. El navmesh mantiene la ruta sobre terreno transitable, pero el steering y la física aún pueden empujar a un personaje contra la geometría, así que el controlador de personaje resuelve la penetración contra los colisionadores después del movimiento. En nuestros mundos isométricos — Mini World y The Cyclops' Island — de aquí vienen la mayoría de los bugs visibles de los NPC: una ruta que se cuela por un hueco más estrecho que el radio del personaje, o un vector de steering que empuja a un agente contra una pared en una esquina. Ajustar bien el radio del agente en el navmesh y mantener la fuerza de separación por debajo de la fuerza de empuje del controlador resuelve la mayor parte.

1

01

Genera el navmesh en tiempo de carga a partir de la geometría estática y guárdalo en caché. No lo reconstruyas en cada fotograma.

2

02

Ajusta el radio y la altura del agente a la cápsula de colisión real del personaje, no a la caja envolvente del modelo.

3

03

Ejecuta la búsqueda de rutas solo cuando cambie el destino, la ruta esté bloqueada o el NPC se haya desviado de la ruta más allá de un umbral.

4

04

Ejecuta el steering, la separación y la evitación de obstáculos cada fotograma con una fuerza de separación limitada.

5

05

Resuelve la colisión contra los colisionadores después del movimiento y luego ajusta el personaje de vuelta al navmesh.

6

06

Registra y visualiza la ruta y el navmesh en una superposición de depuración: los bugs suelen ser espaciales, no lógicos.

NPC impulsados por LLM

Un modelo de lenguaje cambia el panorama en la capa de intención y diálogo, no en la de navegación. La búsqueda de rutas, el steering y la colisión siguen siendo exactamente lo que eran, porque un modelo que tarda cientos de milisegundos en responder no puede gobernar un cuerpo que debe moverse cada fotograma. Lo que el modelo sí puede hacer es decidir qué objetivo tiene sentido dadas las palabras del jugador, generar texto de respuesta en el personaje y elegir entre opciones de alto nivel cuando la situación es ambigua y las reglas escritas a mano producirían algo plano.

Una arquitectura viable separa las dos velocidades. Las reacciones inmediatas se ejecutan en la máquina de estados o en el puntuador de utilidad — atacar, huir, ponerse a cubierto — porque deben ocurrir en uno o dos fotogramas. La intención deliberada pasa por el modelo a una cadencia mucho más lenta, quizá una vez por interacción o cada pocos segundos, y su salida es un comando estructurado que ejecuta la capa rápida: fijar el objetivo en Seguir, fijar la actitud hacia el jugador en Hostil, fijar la línea de diálogo. El modelo nunca toca la posición directamente.

Los límites honestos son la latencia, el coste y el determinismo. Los tiempos de respuesta son lo bastante largos como para que el NPC deba acusar recibo de la entrada de inmediato con un gesto o una breve línea pregrabada, o se percibirá como roto. El coste escala con la frecuencia con la que llamas, así que agrupas y guardas en caché. Y como la salida no es determinista, debes validarla contra un esquema antes de que llegue al juego: una respuesta en texto libre que no se pueda analizar debe recurrir a una línea predeterminada, no tumbar el sistema de diálogo. Trata el modelo como un servicio poco fiable tras una interfaz tipada, y la integración aguanta.

  • Salida de comandos tipadaEl modelo devuelve una intención estructurada, no prosa que el código del juego analiza. Valídala contra un esquema y rechaza todo lo mal formado.
  • Bucle de dos velocidadesLógica local rápida para las reacciones, llamadas lentas al modelo para la deliberación. Nunca pongas un ida y vuelta de red en la ruta de movimiento.
  • Confirmación inmediataReproduce un gesto o una breve línea pregrabada en cuanto llegue la entrada, y reemplázala cuando responda el modelo. Oculta la latencia.
  • Reserva eleganteSi la llamada falla o se agota el tiempo, recurre a la respuesta predeterminada de la máquina de estados. El NPC nunca debe quedarse bloqueado esperando a un modelo.
Ashenmere — lee las decisiones del rival mientras disputas un duelo.
The Cyclops' Island — navegación y colisión en una cuadrícula isométrica.

Camina hacia el mundo al que pertenece

Un sprite es un personaje sin un lugar donde pararse. El último paso es el que nadie más ofrece: toma la hoja que acabas de crear y colócala en un mundo por el que se pueda caminar — una pequeña isla, un planeta, una esquina lluviosa. Cada mundo de este sitio se creó a partir de una sola descripción escrita y se publicó como una página que puedes jugar.

Volver al constructor de juegos 3D

Los 31 mundos, juegos y escenas 3D terminados viven en una sola página — y todos son jugables.

Plataforma: Neta StudioPágina: Comportamiento de NPC: máquinas de estados, árboles de comportamiento, GOAP y NPC impulsados por AILee a continuación: /explore/guide/what-is-a-non-player-character · /explore/guide/what-are-npcs · /explore/guide/ai-npc
×