Diseñaremos un Behavior Tree para nuestra IA.

Es recomendable ver el tutorial básico si no estás familiarizado con este tema.

Objetivos

  • Aprender el funcionamiento del Behavior Tree y el Blackboard.
  • Aprender qué son los Composites, Tasks, Decorators y Services en el Behavior Tree.
  • Diseñaremos el comportamiento simple de un guardia.
    • Cuando está calmado se dedica a patrullar.
    • Cuando ve a un sospechoso lo perseguirá.

Introducción

Necesitaremos un nivel con el NavMesh funcionando, un NPC y un AIController antes de empezar (si vienes del anterior tutorial, puedes aprovechar el nivel y el NPC, pero recomiendo crear otro AIController para evitar errores).

Behavior Tree y el Blackboard

Para crear un nuevo Behavior Tree y un Blackboard pulsa click derecho donde quieras crearlo dentro del Content Browser y selecciona Artificial Inteligence y ahí los tienes a ambos.
El Blackboard es una herramienta que sirve para guardar variables que el Behavior Tree va a usar, es decir, las decisiones que tome el Behavior Tree dependen de las variables del Blackboard.
El Behavior Tree es un árbol de decisiones que se va recorriendo de arriba a abajo y de izquierda a derecha, por lo cual, el orden es muy importante aquí (el numero que aparece en cada nodo significa el orden en el que se van a ejecutar).

Blackboard

Lo único que vamos a hacer aquí es declarar las variables. Para ello, pulsa New Key, ponle un buen nombre (este paso es importante para no confundirte más adelante) y dale el tipo deseado a la variable en Key Type.
NOTA: Automáticamente se crea una variable llamada SelfActor que contiene al pawn controlado por la IA.
Las variables que vamos a declarar son las siguientes:

  • EnemigoAvistado de tipo Bool.
  • Pos_A de tipo vector.
  • Pos_B de tipo vector.
  • Persiguiendo? de tipo Bool

Behavior tree captura1

Behavior Tree

Antes de poder usar en Behavior Tree, debemos asociarlo con el Blackboard que hemos creado (si no se ha hecho ya automáticamente). Una vez hecho esto, podremos hacer un switch rápido entre el árbol y la pizarra.
IA captura1
Para diseñar el árbol, es necesario saber de qué está compuesto:

Tasks

Son nodos terminales (las hojas del árbol), identificadas por el color morado. Son las que dan las ordenes a la IA. Al finalizar la task, esta envía a su nodo padre un Success o Fail.

Composite

IA captura1
Son nodos intermediarios, identificados por el color gris. Hay 3 tipos de Composites:

  • Selector: Si recibe un Success de uno de sus nodos hijos, el Composite parará la ejecución de los demás hijos y finalizará el Composite enviando un Success a su nodo padre.
    • En caso de no recibir ningún Success, finalizará el Composite enviando un Fail a su nodo padre.
  • Sequence: Si recibe un Fail de uno de sus nodos hijos, el Composite parará la ejecución de los demás hijos y finalizará el Composite enviando un Fail a su nodo padre.
    • En caso de no recibir ningún Fail, finalizará el Composite enviando un Success a su nodo padre.
  • Simple Parallel: Este nodo te permite ejecutar una Task mientras se ejecuta un Composite u otra Task diferente en paralelo.

Decorator

Es un elemento que puedes adjuntar a un nodo, identificado por el color azul. El Decorator es igual a un Branch o un If, si la condición del Decorator se cumple, entonces el nodo al que está adjuntado se ejecuta (y también sus Services si tuviera). Puedes adjuntar más de un Decorator a un mismo nodo y se ejecutarían en orden decreciente. Para las condiciones, el Decorator usa las variables del Blackboard.

Service

Es un elemento que puedes adjuntar a un nodo, identificado por un color verdoso. Un Service se ejecuta de forma periódica mientras el nodo al que está adjuntado esté en funcionamiento. Por lo general, los Services se usan para revisar y actualizar variables del Blackboard, aunque nada te impide crear un Service que haga cualquier otra cosa.
IA captura1

Comportamiento de un guardia

Ahora que ya sabemos de qué está formado un Behavior Tree, procedemos a diseñarlo. Al abrir el editor de Behavior Tree nos encontramos que ya hay un nodo colocado, ese nodo es la raiz del árbol y solo puede tener un nodo hijo al que llamaremos nodo Main, ya que este nodo puede tener más de un nodo hijo. Usaremos un nodo Composite Selector como Main. El nodo Main tendrá dos hijos, que equivalen a los dos comportamientos que le vamos a dar a nuestro guardia: patrulla y persecución. Empecemos por el comportamiento de patrulla:

Patrulla

Usaremos un nodo Composite Sequence cuyo padre es el Main, tendrá 4 hijos que serán Tasks y las pondremos en el siguiente orden:

  1. Move To
  2. Wait
  3. Move To
  4. Wait

IA captura1
Aquí es cuando entra en acción el Blackboard. Para decirle la posición a la que moverse necesita una Blackboard Key, es decir, una variable almacenada en la Blackboard. Si pulsáis sobre el Move To y luego sobre Blackboard Key, podréis ver que están las variables compatibles con esa Task, entre las cuales están las que creamos anteriormente. Usaremos Pos_A en el primer Move To y Pos_B para el segundo. En cuanto a los Wait puedes modificar los segundos sin necesidad de ninguna Blackboard Key.
IA captura1
Usamos un nodo Composite Sequence por las siguientes razones:

  • Las Tasks hijas por lo general van a devolver siempre Success.
    • Move To devuelve Success si llega al punto indicado.
    • Wait devuelve Success al terminar el tiempo de espera.
  • Si mientras esté patrullando ve a un enemigo:
    • Detendremos su movimiento, dando paso a un Fail en la Task.
    • El nodo Sequence parará la ejecución de sus hijos, devolviendo un Fail a su padre.
  • El nodo padre (Main) al ser un Selector, ignorará el Fail y seguirá la ejecución con su siguiente hijo.

Por último le añadiremos un Decorator a nuestro nodo Composite Sequence. Para añadirlo pulsamos click derecho sobre el nodo y seleccionamos Add Decorator y seleccionamos Blackboard.
El Decorator que hemos seleccionado se sirve de una Blackboard Key para comprobar si la variable seleccionada está a Set o a Not Set de la siguiente manera:

  • Objeto/Actor sin inicializar (a NULL) -> Not Set.
  • Objeto/Actor inicializada -> Set.
  • Boolean a True -> Set.
  • Boolean a False -> Not Set.
  • Etc…

Al Decorator recién creado le pondremos como Blackboard Key la variable EnemigoAvistado y como Key Query le pondremos Is Not Set, esto significa que para que se cumpla la condición del Decorator y se ejecute el nodo, la variable EnemigoAvistado debe de estar a False. Este es el comportamiento que buscábamos, si no ve a nadie, que patrulle.
IA captura1

Persecución

Para el modo persecución usaremos solo un nodo Task, pero esta vez nos fabricaremos una Task nosotros mismos. Para ello, haz click en New Task y luego a BTTask_BlueprintBase, esto te creará un UObject de tipo BTTask_BlueprintBase en la misma carpeta en la que guardas el árbol. Llamaremos a esta Task “SeguirAJugador”.

SeguirAJugador Task

Para empezar necesitaremos un evento que capture las llamadas a la Task del árbol, dicho evento se llama Receive Tick AI. Necesitamos además la función AI Move To explicada en el anterior tutorial (lo usamos de manera similar). Creamos también una variable de tipo Blackboard Key que sea editable llamada BBKey Persiguiendo?. Esta variable contendrá la Blackboard Key que nos suministre el árbol, pudiendo usarla y modificarla como deseemos.
IA captura1
Antes de seguir, pondremos nuestra Task como hija de Main en el Behavior Tree, añadiendo a dicha Task 2 Decorators:

  • De tipo Blackboard -> EnemigoAvistado Is Set
    • Si el guardia te ve, empieza la persecución.
  • De tipo Blackboard -> Persiguiendo? Is Not Set
    • Si el guardia ya está persiguiendo al jugador, esto evitará que se llame de nuevo a la persecución.

Si clickamos en la Task, veremos un campo llamado BBKey Persiguiendo?, es aquí donde el árbol suministra la variable a la Task, que será Persiguiendo? de tipo Bool.
IA captura1
Si volvemos dentro de la Task, vemos que la variable BBKey Persiguiendo? es de tipo Blackboard Key pero el árbol nos ha dado una variable Booleana. Para hacer la traducción de Blackboard Key a Bool usamos el nodo Get Blackboard Value as Bool si queremos obtener el valor y Set Blackboard Value as Bool si queremos modificar el valor. Dicha variable la pondremos a True antes de empezar el movimiento, y a False al terminarlo.
IA captura1
Toda Task debe devolver a su padre un Success o un Fail, pues con el nodo Finish Execute lo podemos hacer.
IA captura1
El montaje de la Task sería así:

  • El tick se conecta a Set Blackboard Value as Bool que estará a True.
  • El nodo anterior se unirá al AI Move To.
  • On Success estará conectado a Finish Execute que estará a True.
    • Este Finish Execute estará conectado a otro Set Blackboard Value as Bool que pondrá la variable a False.
  • On Fail estará conectado a Finish Execute que estará a False.

Con esto tendríamos terminada tanto la Task como el Behavior Tree.
IA captura1
IA captura1

Configurar AIController

Tenemos un Blackboard y un Behavior Tree, pero no están asociados a ninguna IA. Para asociar asociar un Blackboard a una AIController, ve al Event Graph y con un evento Begin Play ejecutas el nodo Use Blackboard.
IA captura1
Una vez que ya tenemos Blackboard, podemos empezar a inicializar algunas variables, para ello, es necesario crear una variable de tipo Name por cada variable a modificar. El nombre de la variable puede ser el que tu quieras, pero el valor de dicha variable debe ser el nombre exacto de la variable que quieres modificar. Crearemos 3 variables tipo name:

  • Nombre variable: BBName Pos_A, Valor: Pos_A
  • Nombre variable: BBName Pos_B, Valor: Pos_B
  • Nombre variable: BBName EnemigoAvistado, Valor: EnemigoAvistado

IA captura1
Usaremos 2 nodos para modificar las variables:

  • Set Value as Vector: Modifica variables de tipo Vector. Como parámetros de entrada requiere un Blackboard, el nombre de la variable que quieres modificar y el valor nuevo.
  • Set Value as Bool: Es igual pero con Bool.

Después del Use Blackboard inicializamos Pos_A y Pos_B, que será los dos puntos por donde va a estar vigilando (procura que las posiciones estén en el NavMesh). Para que el árbol empiece a funcionar con nuestra AIController, usaremos el nodo Run Behavior Tree.
IA captura1
Por último, crearemos un Event Dispatcher llamado Jugador Avistado y hacemos un Bind a dicho Event Dispatcher, después crearemos un nuevo evento llamado Activar Persecución en el que haremos que deje de patrullar y persiga al jugador, para ello, usaremos el nodo Stop Movement para parar cualquier movimiento y Set Value as Bool para poner a EnemigoAvistado a True. De esta manera, al hacer un CallJugador Avistado, se ejecutará Activar Persecución.
IA captura1

Visión del guarda

Nuestro guarda está ahora mismo ciego, vamos a darle algo de visión. Nos vamos al blueprint del personaje que estamos usando, en nuestro caso a ThirdPersonCharacter, de aquí nos vamos a Viewport, le añadimos un componente llamado PawnSensing y compilamos. Al pulsar sobre este componente veremos varias líneas, las verdes corresponde a la visión. En Peripheral Vision Angle podremos cambiar el ángulo de visión del personaje (para el test lo puse a 30º) y en Sight Radius podremos cambiar la distancia de visión (para el test lo puse a 2000).
IA captura1

Una vez hecho esto, usaremos el evento que tiene el componente recién añadido, llamado OnSeePawn. El evento antes mencionado se ejecutará si un Pawn se cruza en su línea de visión, para asegurarnos de que sea a nuestro personaje al que persiga, preguntaremos antes si el Pawn al que ha visto es nuestro personaje. Después de ese filtro, daremos el Call a Jugador Avistado del AIController, de manera que tendremos que obtener el AIController del personaje, luego hacer un cast a nuestro AIController y realizar el Call. Antes del Call pondremos el nodo DoOnce para evitar múltiples llamadas (solo necesitamos que se llame 1 vez).
IA captura1

Y ya está, si todo ha salido bien, deberíais tener a un guardia dando vueltas y persiguiéndote. Si os ayudó este post o tenéis alguna duda, dejad un comentario.

Deja un comentario

Tu email no será publicado. Campos obligatorios marcados con *