13
Introducción a la Inteligencia Artificial en Unreal Engine 4 En este tutorial vamos a agregar un enemigo que estará patrullando una zona del nivel. Cuando nos acerquemos a esa zona y el enemigo se de cuenta que estamos cerca, nos perseguirá, si pierde nuestro rastro volverá a su tarea de vigilante. Con este simple ejemplo veremos varios conceptos relacionados con la inteligencia artificial en Unreal Engine 4 como el Behavior Tree, Decorators, Task, Services, BlackBoard, AIController etc. Modifica un poco el nivel, copiando el objeto Floor y usando las herramientas de Transformación para crear una plataforma que será la que estará patrullando el enemigo. A pesar que en el estilo final de nuestro juego no es necesario que la plataforma tenga mucho espacio hacia atrás, para este ejemplo crésela también en ese eje, con el objetivo de crear un buen espacio para que el enemigo se desplace libremente y poder probar y jugar mejor con la AI. Por último, tenemos que encerrar la zona que va a patrullar el enemigo con un objeto de tipo NevMeshBoundsVolume. Arrastra al nivel, desde la sección Volumes, un Nav Mesh Bounds Volume y modifícalo para que encierre todo el nivel. Creado el enemigo a partir de un AIController En el primer tutorial explicamos la filosofía que seguía el framework de UE4 en su jerarquía de clases en lo referente a los personajes de nuestro juego, hablamos que el personaje principal era un Pawn especial, un Character, y este era controlado por el PlayerController. Los personajes que usan inteligencia artificia, generalmente conocimos como non-player characters (NPC), como los enemigos por ejemplo, funcionan muy parecidos, su representación en el juego es mediante un Pawn y también son controlados por un Controller, pero en este caso por un AIController. Por lo demás, básicamente contienen lo mismo que nuestro personaje principal. Un skeletal mesh, el sistema de animaciones, el Movement Component etc. Para nuestro ejemplo, haremos al enemigo a partir de las mismas animaciones y modelo de nuestro personaje protagónico, aunque si quieres variar te recomiendo de nuevo que te des una vuelta por el Marketplace, ahí podrás encontrar varios modelos de personajes con sus animaciones listos para usar. Crea una carpeta en el Content Browser, ponle el nombre que quieras, en mi caso le puse AIEnemy.

Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Embed Size (px)

Citation preview

Page 1: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Introducción a la Inteligencia Artificial en Unreal Engine 4

En este tutorial vamos a agregar un enemigo que estará patrullando una zona del nivel. Cuando nos

acerquemos a esa zona y el enemigo se de cuenta que estamos cerca, nos perseguirá, si pierde nuestro

rastro volverá a su tarea de vigilante. Con este simple ejemplo veremos varios conceptos relacionados

con la inteligencia artificial en Unreal Engine 4 como el Behavior Tree, Decorators, Task, Services,

BlackBoard, AIController etc.

Modifica un poco el nivel, copiando el objeto Floor y usando las herramientas de Transformación para

crear una plataforma que será la que estará patrullando el enemigo. A pesar que en el estilo final de

nuestro juego no es necesario que la plataforma tenga mucho espacio hacia atrás, para este ejemplo

crésela también en ese eje, con el objetivo de crear un buen espacio para que el enemigo se desplace

libremente y poder probar y jugar mejor con la AI.

Por último, tenemos que encerrar la zona que va a patrullar el enemigo con un objeto de tipo

NevMeshBoundsVolume. Arrastra al nivel, desde la sección Volumes, un Nav Mesh Bounds Volume y

modifícalo para que encierre todo el nivel.

Creado el enemigo a partir de un AIController

En el primer tutorial explicamos la filosofía que seguía el framework de UE4 en su jerarquía de clases en

lo referente a los personajes de nuestro juego, hablamos que el personaje principal era un Pawn especial,

un Character, y este era controlado por el PlayerController. Los personajes que usan inteligencia artificia,

generalmente conocimos como non-player characters (NPC), como los enemigos por ejemplo, funcionan

muy parecidos, su representación en el juego es mediante un Pawn y también son controlados por un

Controller, pero en este caso por un AIController.

Por lo demás, básicamente contienen lo mismo que nuestro personaje principal. Un skeletal mesh, el

sistema de animaciones, el Movement Component etc. Para nuestro ejemplo, haremos al enemigo a partir

de las mismas animaciones y modelo de nuestro personaje protagónico, aunque si quieres variar te

recomiendo de nuevo que te des una vuelta por el Marketplace, ahí podrás encontrar varios modelos de

personajes con sus animaciones listos para usar. Crea una carpeta en el Content Browser, ponle el

nombre que quieras, en mi caso le puse AIEnemy.

Page 2: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Crea dentro de esa carpeta un nuevo Blueprint que herede de AIController y ponle de nombre

AIEnemyController. Crea otro Blueprint que herede de Character y ponle de nombre AIEnemyCharacter.

Ahora vamos a configurar el Skeletal Mesh y las animaciones para el enemigo, que como dijimos,

usaremos las mismas animaciones y el mismo mesh que el personaje principal. Da doble clic en

AIEnemyCharacter, sección Componentes, selecciona el componente Mesh y en la sección Mesh para el

atributo Skeletal Mesh selecciona el mismo Mesh que estamos usando para nuestro personaje

protagónico y ajústalo dentro de la capsula como ya hemos hecho antes. En la sección Animation

selecciona para el atributo Anim Blueprint Generated Class la misma que estamos usando para nuestro

personaje. Selecciona el Capsule Component y cambia el atributo Capsule Half Height a 96 y Capsule

Radius a 42. Esto es para ajustar un poco la capsula al modelo.

Selecciona el componente CharacterMovement y en la sección Movement Component despliega Nav

Agent Props en el atributo Agent Radius ponle 42 (este atributo tiene que ser al menos del tamaño del

radio de la capsula) y el atributo Agent Height ponlo en 192 (este tiene que ser al menos el doble del

atributo Capsule Half Height). La propiedad NavAgentProps define como el sistema de navegación del

NPC interactúa con este componente.

Ahora pasa al Max Walk Speed y ponle 400.

Por último, pasa al modo Defaults y en la sección AI, para el atributo AIControllerClass, selecciona el

AIController que acabamos de crear, AIEnemyController. Con esto le decimos al AICharacter que será

controlado por el AIEnemyController.

A diferencia de como hicimos con nuestro personaje, vamos a configurar a nuestro enemigo

COMPLETAMENTE desde el Editor mediante el Blueprint. Aunque en un próximo tutorial veremos la

variante de hacerlo desde C++ para que tu solo tomes la decisión de la variante que prefieres.

Ahora agrega el AIEnemyCharacter al nivel en la zona que creamos con bastante espacio. Guarda y corre

el juego. Muévete hasta la zona donde pusimos al enemigo, como verás, ya lo tenemos ahí, pero está

parado sin hacer nada, y aunque nos acerquemos a él, ni se inmuta :(. Vamos a arreglar esto, vamos a

darle vida.

Un poco de teoría sobre la inteligencia artificial en Unreal Engine 4 y el Behavior Tree

En UE4 hay muchas formas de implementar la lógica de un personaje AI, una muy simple es en el mismo

Blueprint del Character, haciendo uso del evento Tick, implementar algún algoritmo para que el personaje

se mueva de un lado a otro, que compruebe que está cerca de alguien con los métodos que brinda el

Engine (y que veremos algunos hoy) y nos ataque o haga cualquier otra cosa. Esto se puede implementar

Page 3: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

directamente ahí, pero a medida que necesitemos complejizar las acciones que tendrá ese Actor, los

estados en los que podrá estar y las cosas que podrá hacer en cada situación, se hará mucho más

complejo el control y el mantenimiento de toda esa lógica, ya sea en VisualScript o desde C++.

Este es uno de los motivos por los que el UE4 nos facilita la creación de un Behavior Tree (árbol de

comportamiento) para poder definir el comportamiento que tendrá el NPC según las condiciones en las

que se encuentre. Behavior Tree es un concepto de mucho tiempo de la IA que no vamos a detenernos

en él aquí, si quieres profundizar en la teoría detrás de los arboles de comportamientos para mecanismos

IA puedes poner en Google “behavior tree ai“ y date una vuelta por el resultado que más te llame la

atención ;)

Creando el Behavior Tree y el BlackBoard que definirá el comportamiento del enemigo

Después de está teoría vamos a la práctica. Entra en la carpeta AIEnemy en el Content Browser y

selecciona New/Miscellaneous/Behavior Tree ponle de nombre AIEnemyBehaviorTree y

New/Miscellaneous/Blackboard y ponle de nombre AIEnemyBlackboard

En el objeto Behavior Tree mediante el Blueprint vamos a diseñar de forma visual todo el árbol de

comportamiento del personaje y el Blackboard es la “fuente de conocimiento“ de nuestro personaje AI, o

sea, todas las variables globales que necesite el personaje para poder definir su comportamiento las

definiremos aquí y los nodos que tenga nuestro árbol y necesiten esta información, harán referencia a ella

mediante los métodos Get Blackboard Key As o Set Black Board Key As. Por ejemplo, una variable que

define una parte del comportamiento del personaje es la referencia al actor que tiene que perseguir si lo

ve. Pues en este caso, esa variable o ese ”conocimiento” que necesita el personaje la tenemos que definir

aquí.

Un detalle a aclarar es que en el Blackboard lo que definimos en realidad son parejas de clave-valor

donde el valor será del tipo de dato del que definimos para la clave.

Enemigo patrullando una zona del nivel

Vamos con la primera tarea que tiene el enemigo, patrullar una zona del nivel. Primero, pensemos…

¿cual es la lógica detrás de alguien que está vigilando un lugar ?… Simple, estará caminando de un lado

hacia el otro, en cada punto crítico se detendrá unos segundos y pasará al siguiente, y así en ciclo infinito

(a menos que se canse y se quede dormido :) …. )

En el modelo side-scroller que estamos usando en nuestro juego esto es muy simple porque como todo el

desplazamiento se hace en un solo eje, básicamente el enemigo se estaría moviendo de un lado al otro y

nada más. Por este motivo es que cambiamos el estilo de nuestro juego temporalmente, para complicar

un poco el movimiento que tiene que tener el enemigo y hacer más interesante nuestro estudio ;). Vamos

a hacer que este enemigo esté caminando por 4 puntos clave del nivel. Se moverá al primero, cuando

llegue se detendrá unos segundos, pasará para el segundo, se volverá a detener y así…

Vamos primero a crear los objetos que agregaremos al nivel y usaremos como puntos clave en el camino.

En estos puntos es donde se detendrá el personaje antes de pasar al siguiente y definirán la trayectoria

que patrullará. En el Content Browser crea un nuevo Blueprint que herede de TargetPoint dale de nombre

AIEnemyTargetPoint. TargetPoint es un simple Actor con una imagen que lo identifica en el Editor cuando

lo agregamos al nivel, esta imagen no se ve en el juego, solo en el editor. Dale doble clic y créale una

variable int de nombre Position y márcale el iconito del ojito que tiene a la derecha de la variable para

hacerla pública. Esto nos permite acceder al contenido de esta variable desde un blueprint externo a ella,

el mismo concepto de un atributo publico en programación orientada a objetos. De hecho, es exactamente

esto, AIEnemyTargetPoint es una clase que hereda de TargetPoint y que tiene un atributo público de

nombre position y tipo int. En este atributo vamos a tener el “orden“, por llamarlo de alguna manera, que

tiene que seguir el personaje en su recorrido.

Page 4: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Agrega al nivel cuatro AIEnemyTargetPoints y distribúyelos en el escenario por distintos puntos,

definiendo el camino que quieres que recorra el personaje.

Ahora ve seleccionando uno a uno y en el panel de propiedades verás en la sección Default la variable

Position, ve por cada uno y ponle 0, 1, 2, y 3. La idea es que el personaje comience en el punto 0,

después se mueva el 1, después al 2, después al 3 y después de nuevo al 0. A mi me quedó así:

Muy bien, ya tenemos listo al enemigo y también los puntos que definen el camino que tendrá que

patrullar, ahora solo falta implementarle la “inteligencia“ para que se mueva de un punto a otro en ciclo y

esto lo haremos mediante el Behavior Tree y sus componentes. Vamos a agregarle las primeras ramas al

árbol de comportamiento de nuestro personaje.

Page 5: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Antes, déjame comentarte en general cual sería la lógica que seguirá ese personaje para lograr su

comportamiento. En el nivel tenemos 4 puntos que marcan el recorrido, el personaje necesita saber en

que punto está actualmente del recorrido y la posición de ese punto en el espacio (vector 3D). Una vez

que registre al punto al que se va a mover, que actualice su siguiente punto. Cuando termine el

movimiento que espere unos segundos en la zona y pase al siguiente punto.

Abre el BT que creamos y en el en el panel de Detalles en la sección BT selecciona para el atributo

Blackboard Asset el Blackboard que acabamos de crear. Ahora abre el Blackboard y vamos a agregar a

este los pares Clave-Valor que necesitamos. Da clic en el botón de agregar nueva variable y ponle de key

TargetPointNumber de tipo de dato int. Crea otra clave y ponle TargetPointPosition de tipo de dato Vector.

En la primera vamos a guardar el punto al que se tiene que dirigir el personaje y en TargetPointPosition

vamos a tener la posición de ese punto para poderle decir después que se mueve hacia ahí.

Ahora vamos a crear el primer Nodo de nuestro BT. Crea un nuevo Blueprint en el Content Browser que

herede de BTTask_BlueprintBase y ponle de nombre UpdateNextTargetPointTask.

BTTask_BlueprintBase es la clase que nos brinda el UE4 para los nodos de tipo Task del BT y que se van

a usar mediante el Blueprint.

Los nodos Task serán las hojas del árbol (los nodos que no tienen hijos) y su objetivo es ejecutar una

acción determinada. Este Task que acabamos de crear lo que hará será comprobar el valor que tiene la

clave TargetPointNumber en el Blackboard y validar que si es mayor igual que 4 la reinicia a 0 para que

del cuarto punto pase de nuevo al primero. Obtendrá la posición en el espacio del TargetPoint al que le

toca moverse y setteará el valor de la entrada TargetPointPosition del Blackboard, para que en otro Task

se ejecute la acción de moverse a ese punto. Esto se pudiera hacer en este mismo Task, pero vimos que

uno de los objetivos más claros que tiene el BT es separar las acciones en subacciones concretas. Por lo

que para esta tarea de patrullar en general la zona tendremos tres Task que serán nodos hijos de una

secuencia.

Dale doble clic al Task que acabas de crear y agrega dos variables TargetPointNumber y

TargetPointPosition ambas de tipo BlackboardKeySelector. Ahora comienza a agregar nodos y hacer

conexiones hasta que el árbol te quede como el siguiente:

… no voy a ir paso a paso en como tienes que hacer esto, porque si has seguido los tutoriales anteriores

lo tienes que saber hacer. Por supuesto, sí vamos a explicar la lógica que sigue el algoritmo.

Comenzamos el algoritmo cuando se dispara el Evento Execute del Task, lo primero que hacemos es

comprobar que el valor que esté registrado en el TargetPointNumber del BlackBoard sea mayor que 4.

Recuerda, en esa variable tenemos el Position del TargetPoint al que tiene que moverse el personaje, por

Page 6: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

lo que tenemos que comprobar que si llega al último reinicie su valor al primero. Para obtener el valor de

una variable que tenemos en el BlackBoard se usa el nodo GetBlackBoard As [Tipo de dato], en este caso

int y le tenemos que decir cual es el Key que vamos a modificar y es para esto que creamos las dos

variables al inicio de tipo BlackBoardKeySelector.

Una vez que tenemos el Position del TargetPoint al que le toca ir al NPC vamos a buscarlo en el nivel.

Para esto usamos el Nodo Get All Actors of Class que le podemos definir en el puerto Actor Class el tipo

de clase que queremos obtener, aquí seleccionamos AIEnemyTargetPoint y nos retornará en el puerto de

salida un arreglo con todos los actores que hay en el nivel de tipo AIEnemyTargetPoint.

Después recorremos este arreglo con el nodo ForEach, tenemos que castear cada elemento del ciclo a

AIEnemyTargetPoint y eso lo hacemos con la ayuda del nodo Cast To AIEnemyTargetPoint, dentro del

ciclo preguntamos en cada iteración si la propiedad Position (que creamos como publica en el Blueprint

del AIEnemyTargetPoint) es igual al valor que tenemos registrado en el TargetPointNumber del

BlackBoard, y si es igual obtenemos la posición en el espacio de ese actor y seteamos el valor del otro

Key que tenemos en el Blackboard, el TargetPointPosition para almacenar ahí el vector con la posición

del siguiente TargetPoint al que se moverá el enemigo en su recorrido.

Por último, como dijimos, los Task tiene que terminar de dos formas: con Éxito o con Fallo. Según la

forma en la que termine definirá el comportamiento de su nodo padre. En este caso, como todo el proceso

lo tendremos dentro de un Sequence, necesitamos que termine en éxito para que el nodo Sequence siga

ejecutando al siguiente nodo. Fíjate que al salir del puerto Completed del ForEach, el que se ejecuta

cuando termina el ciclo, incrementamos primero el valor del TargetPositionNumber, para que la próxima

vez que se ejecute este Task se obtenga la posición del siguiente TargetPoint en el nivel. Por último

terminamos la ejecución del Task con el nodo FinishExecute, asegúrate de marcar el puerto Success.

Guarda y compila.

Ahora solo queda configurar el Behavior Tree con este Task. Abre el AIEnemyBehaviorTree. Inicialmente

solo tenemos un nodo, el nodo raíz del árbol. Arrastra desde el borde inferior del nodo Root y conecta a

este un nodo Sequence, este nodo los usaremos cuando queramos ejecutar un grupo de Task en

secuencia, una detrás de la otra, comenzando por la izquierda, siempre ten en cuenta que para que se

ejecute el siguiente Task, el anterior tiene que haber retornado con Éxito.

Arrastra del borde inferior del nodo Sequence y agrega el Task UpdateNextTargetPointTask, selecciona el

nodo y en el panel de detalles en la sección Default, el campo Target Point Number selecciónale la opción

TargetPointNumber y en Target Point Position selecciona la opción Target PointPosition. Estos combos lo

que listan son todos los keys que tenemos registrado en el Blackboard, lo que estamos haciendo es

definiendo que esas dos variables que tenemos en el Task representan esos Keys del Blackboard

Un momento … sin tener que correr te darás cuenta que con esto no es suficiente, porque lo que hace el

Task es solamente darle valor a las variables TargetPointPosition y TargetPointNumber y más nada.

Arrastra del borde inferior del Sequence y agrega el Task: “Move To“. Este Task por defecto nos lo brinda

UE4 y lo que hace es decirle al AICharacter que se mueva a una posición determinada. Selecciona en el

panel de Detalles en la propiedad Blackboard Key la variable TargetPointPosition. Con esto estamos

definiendo cual será el Key del Blackboard que tendrá la posición a donde se moverá el personaje, está

posición es obtenida en el Task anterior según el TargetPoint al que le toque ir al personaje.

Con esto es suficiente, pero si corremos en este punto, el personaje irá caminando pasando por los 4

puntos sin detenerse, y evidentemente esto hará que se canse más rápido y se nos quede dormido al

momento :S … para evitar esto agrega un tercer Task al sequence, ahora el Task ”Wait”. Este Task,

también incluido en el UE4 nos permite detener la ejecución del árbol un tiempo determinado. En el panel

de Detalles en el atributo Wait Time puedes definir el tiempo que estará detenido, yo puse 2.

Listo !!, el BT te tiene que quedar así.

Page 7: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Es válido aclarar en este punto un detalle. El Nodo Wait lo que hace es detener completamente la

ejecución del árbol, por lo que si en este intervalo de tiempo nos acercamos al personaje, el NPC no nos

detectará, ya que todo el árbol está detenido. De cualquier forma creo que es un ejemplo válido del uso

del Task Wait.

Configurando el AIController para que use el Behavior Tree que define su comportamiento

Solo nos falta una cosa, sí, ya tenemos configurado el Behavior Tree, pero en ningún lado le hemos dicho

al AIController que use este BT para definir su comportamiento. Para esto, abre el AIEnemyController

agrega el nodo Begin Play y conéctalo a un nodo de tipo Run Behavior Tree en el puerto BTAsset

selecciona el AIBehaviorTree. Tan simple como eso.

Compila, ejecuta el juego y muévete hacia el enemigo. Verás como estará rondando de punto a punto y

en cada uno esperará 2 segundos antes de continuar. :) … puedes en lo que está en ejecución el juego

abrir el Behavior Tree para que veas porque rama está el árbol en cada momento.

Page 8: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Creando un Service en el Behavior Tree para que el personaje esté al tanto de quien se acerque a

la zona.

Ya tenemos al enemigo haciendo su recorrido rutinario, pero bastante poco eficiente es como vigilante

no ? . . . por más que nos acerquemos a la zona, ni se inmuta :(. Vamos entonces a “enseñarle“ a vigilar

la zona, o sea, a que detecte cuando nos acerquemos.

Como ya vimos, en el BT, los Task son para ejecutar una acción determinada cuando sea el momento

adecuado y retornar un resultado. Pero en este caso no es exactamente esto lo que necesitamos, aquí

necesitamos ejecutar un proceso constantemente ya que un buen vigilante tiene que estar atento,

contantemente, para saber si alguien se aproxima :) . Para esto tenemos otro tipo de Nodo en el Behavior

Tree, que son los Services. Service es el nodo que tenemos en el Behavior Tree para tener un proceso

ejecutándose contantemente cada un intervalo de tiempo determinado, y es exactamente este tipo de

nodo el que tenemos que usar para implementarle la tarea de vigilante a nuestro NPC.

En el Content Browser dentro de la carpeta AIEnemy crea un nuevo Blueprint que herede de

BTService_BlueprintBase y ponle de nombre CheckNearbyEnemy. Antes de editar este Blueprint,

tenemos que agregar una nueva entrada en el BlackBoard. Un nuevo “conocimiento“ que tendrá que tener

el NPC, y es el actor al que va a seguir cuando detecte que hay alguien en la zona.

Abre el Blackboard y agrega un nuevo Key de tipo Object y ponle de nombre TargetActorToFollow. Aquí

guardaremos la referencia a nuestro personaje protagónico cuando nos acercamos a la zona que vigila el

enemigo.

Abre el CheckNearbyEnemy y primero crea las siguientes variables que usaremos en este visualscript:

DesiredObjectTypes de tipo EObjectTypeQuery y en la sección Default Value del panel de Detalles de

esta variable, selecciona Pawn. En un segundo te explico donde la usaremos.

TargetActorToFollow: De tipo BlackBoardKeySelector y como ya hicimos en el Task que creamos, esta

variable la usaremos para obtener el valor de la clave con este nombre en el Blackboard. Esta variable

créala pública dando clic en el iconito del ojito que tiene a la derecha.

Te explico lo que tiene que hacer el algoritmo para que lo intentes crear por tu cuenta, de cualquier forma

te dejaré también la imagen de referencia.

Los nodos de tipo Service en el BT cuentan con el evento Event Receive Tick, este evento es muy

parecido al Tick que ya vimos en el tutorial pasado, solo que a diferencia de ese, este se ejecuta a un

intervalo fijo configurable. Lo que vamos ha hacer constantemente, gracias al evento Tick del Service, es

generar una esfera “imaginaria“ alrededor de todo el NPC mediante el Nodo Multi Sphere Trace for

Page 9: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Objects. Este es un método súper útil que nos da el UE4 y nos permite generar un esfera imaginaria en

una posición determinada con un radio que queramos y obtener todos los objetos que estén dentro de esa

zona de la esfera. Este método espera los siguientes parámetros:

Puntos de Inicio y fin de la línea que usará como referencia para generar la esfera, en este caso usamos

la posición del NPC como punto de inicio y el mismo punto en X y Y pero solo con un poco más de altura

para que sean distintos los puntos (si inicio y fin son iguales, no funcionará). Usamos la posición del NPC

para que siempre la esfera se genere alrededor de este personaje.

El parámetro Radio es bastante claro, es el radio de la esfera. Le damos un valor bastante grande y

definirá el alcance de vigilancia del enemigo a la redonda.

El parámetro Object Types es un arreglo que define los tipos de objetos que queremos tener en cuenta

para ese chequeo. En este caso queremos tener en cuenta a los objetos de tipo Pawn. Y para esto fue

que creamos la variable DesiredObjectTypes y le dimos el valor de Pawn por defecto. Fíjate que este

parámetro es un arreglo, podemos pasarle más de un tipo, pero en este caso solo queremos tener en

cuenta a los Pawns por eso es que creamos un arreglo de un solo elemento.

Trace Complex: No necesitamos tenerlo en true, se usa para chequeo en mallas de colisiones complejas,

pero requiere mayor consumo de recursos. Como este no es el caso de nosotros, lo podemos dejar en

false.

Actors To Ignore: Otro muy útil parámetro que recibe este método. Nos permite definir todos los actores

que queremos que ignore el método, o sea que aunque estén dentro de la esfera y sean un Pawn no se

van a devolver en el resultado. Y aquí lo usamos para ignorar el propio Pawn del enemigo. Lógico no ?, el

enemigo siempre va a estar dentro de la esfera pero él no será un resultado que nos interese.

Draw Debug Line es un muy útil parámetro que nos sirve para debugear la esfera en el nivel. Para esta

prueba ponlo en For Duration y podrás ver el comportamiento de esta esfera imaginaria en el nivel cuando

lo probemos.

Cada vez que se ejecute este método como resultado tendremos todos los actores que están dentro de la

esfera, y por tanto cercanos al enemigo o false si no se encuentra ninguno. Esto es lo que haremos en la

primera parte del algoritmo. Paso a paso sería:

1 – Casteamos el Actor que viene en el Tick a AIEnemyController que es el Controller de nuestro

enemigo.

2 – Obtenemos el Pawn de ese Controller con el nodo Get Controller Pawn.

3 – Obtenemos la posición del Pawn con el nodo Get Actor Location

4 – Creamos u nuevo vector a partir de la posición del Pawn incrementándole solo en el eje Z un poco.

Estas dos posiciones las pasamos como parámetro al nodo MultiSphereTrace for Objects. Además le

pasamos los otros parámetros que necesita. Fíjate que la línea blanca define el flujo de ejecución del

algoritmo.

Una vez que se ejecuta MultiSphereTrace for Objects tenemos en el puerto de salida Out Hits un arreglo

de objetos HitResults y en el puerto Return Value, true, si se encontró algún objeto y false en caso

contrario.

En este punto necesitamos una condición y para eso usamos el Nodo Branch que recibe el flujo de

ejecución (los puertos blancos) y permite separar el script en dos flujos según el resultado de la condición,

este nodo es el clásico IF de programación.

Si MultiSphereTrace for Objects retorna true quiere decir que encontró resultados. Entonces, continuamos

el flujo del programa con el nodo ForEachLoopWithBreak, este nodo como su nombre lo indica, es el

clásico for each de C++ que permite iterar un arreglo de elementos y detenerlo, llamando al break, cuando

se cumpla la condición que buscamos. Iteramos entonces todos los actores que se encontraron dentro de

la esfera. Fíjate que tenemos que conectar el puerto de salida Hits del MultiSphereTrace for Objects al

Page 10: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

puerto de entrada Array del ForEachLoopWithBreak para hacerle saber a este cuál es el array que va a

iterar.

El puerto Loop Body del ForEachLoopWithBreak es el flujo del programa en cada iteración del ciclo,

entonces, dentro del ciclo hacemos otro Branch para preguntar si el Actor que se encontró es el Player y

si es así lo guardamos en el Blackboard en el key TargetActorToFollow. El MultiSphereTrace for Objects

lo que retorna es un array de HitResults esta estructura tiene muchísima información del punto de

interacción, no es solo el actor, por lo que necesitamos el nodo Break Hit Result para obtener el actor,

este nodo en el puerto de salida Hit Actor nos da el Actor. Fíjate que para obtener el Pawn usamos el

método Get Player Character que ya hemos usado en los tutoriales anteriores. Fíjate también que una vez

que setteamos el valor del TargetActorToFollow pasamos el flujo al Break del ForEach porque ya no

necesitamos más nada.

Bien, este es el caso donde se encuentra al personaje protagónico dentro de la esfera. Pero para el caso

en el que el Actor que se encuentre dentro de la esfera no sea el personaje protagónico o que el método

MultiSphereTrace for Objects retorne false, que quiere decir que no hay ningún Pawn dentro de la esfera,

hay que dejar la variable TargetActorToFollow en NULL para poder determinar, con otro tipo de nodo del

BT que veremos ahora, si tenemos al personaje protagónico cerca y lo seguimos, o no, y entonces

seguimos con la rutina de vigilancia normal.

Por último, solo para testear, puedes agregar un nodo Print String cuando seteamos el valor del

TargetActorToFollow con el texto Detectado Enemigo cercano. Este nodo imprime en la pantalla el texto

que le indiquemos como parámetro y nos servirá aquí para ver el resultado de nuestro Service en

ejecución

Finalmente, el Blueprint te tendrá que quedar así:

Agregando el Service CheckNearbyEnemy al Behavior Tree.

Solo resta agregar este nuevo nodo al BT. Abre el AIEnemyBehaviorTree, elimina el Link entre el nodo

ROOT y el Sequence. Arrastra el borde inferior del ROOT y agrega un Selector nuevo. Da clic derecho

sobre ese nuevo selector/Add Service y selecciona CheckNearbyEnemy. Se te agregará dentro del

Selector. Selecciónalo y en el panel de Detalles la sección Default el atributo TargetActorToFollow (que es

la variable que le declaramos al blueprint como pública) selecciónale como valor TargetActorToFollow del

combo que hace referencia a las variables declaradas en el BlackBoard. Fíjate también que en la sección

Service podemos modificar el intervalo del Tick y un aleatorio de desviación, para que no siempre se

ejecute exactamente en un intervalo fijo. De momento podemos quedarnos con los valores por defecto.

Page 11: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Guarda y ejecuta el juego. Veras el debug en rojo de la esfera que se ejecuta alrededor del enemigo.

Recuerda que esto es un debug, después le quitas el valor que le dimos al parámetro Draw Debug Line y

no se verá nada. Ahora camina con el personaje cerca del enemigo. Inmediatamente que entres dentro

del espacio de la esfera, se imprime en la pantalla el texto “Detectado Enemigo Cercano“ (recuerda

agregar ese nodo Print String al Blueprint para que puedas ver esta salida)

Creando otro Task en el Behavior Tree para perseguir al personaje cuando el enemigo detecte que

está cerca.

Súper hasta ahora verdad !? :) … pero seguimos teniendo un enemigo un poco ”bobo” porque a pesar de

ya detectar que nos acercamos a su zona de seguridad, no hace nada, sigue sin inmutarse. Vamos a

solucionar esto, y para ello necesitamos otro Task. Como dijimos, los Task son para ejecutar acciones

concretas y es exactamente eso lo que queremos. Cuando el enemigo nos detecte, que nos persiga.

Vamos al Content Browser, en la carpeta AIEnemy y crea un nuevo Blueprint que herede de

BTTask_BlueprintBase y dale de nombre MoveToEnemyTask. Ábrelo para editarlo y agrega la variable

TargetActorToFollow de tipo BlackBoardKeySelector y hazla pública. Después crea el siguiente algoritmo.

Page 12: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Que hacemos aquí ?. Iniciamos el algoritmo cuando se lanza el evento Execute del Task, casteamos el

Owner Actor a nuestro AIEnemyController, obtenemos el Pawn del enemigo y usamos un método SUPER

GENIAL que nos da el UE4, el nodo AI Move To. Con este método podemos indicarle a un NPC que

persiga a otro actor … deja que lo veas funcionando ¡!!, parece mentira el poder lograr algo tan complejo

con un solo nodo :)

El nodo AI Move To espera como parámetros el Pawn, un Destino que será un vector fijo u otro Actor que

será al que perseguirá el Pawn pasado en el primer parámetro. En este caso usaremos el puerto Target

Actor y le pasaremos el actor que está guardado en el TargetActorToFollow del BlackBoard, que contiene

la referencia a nuestro personaje cuando nos acercamos a la zona de vigilancia del enemigo gracias al

Service que creamos hace unos minutos. Por último, llamamos al Finish Execute cuando el AI Move To

retorna On Success que quiere decir que el enemigo nos alcanzó. Fíjate que aquí también podemos usar

el Print String para ver en pantalla exactamente cuando el enemigo llega a nosotros.

Creando un Decorator en el Behavior Tree

Espera ! ! ! … de seguro que estás loc@ por conectar este Task al árbol y probar, pero tenemos un

pequeño detalle. Este Task solamente lo podemos ejecutar si la variable TargetActorToFollow tiene valor

en el BlackBoard, porque como vimos si el CheckNearbyEnemy determina que el personaje no está

cerca, deja en NULL está variable y entonces no tiene porqué ejecutarse esta rama del árbol de

comportamiento, sino que pasa a la sección de vigilante.

Para ejecutar condiciones en el Behavior Tree que determinen si continuar ejecutando una rama

determinada del árbol tenemos otro tipo de nodo que son lo Decorators. Vamos entonces a crear un

decorator para comprobar si la variable está seteada, y si es así, entonces se puede ejecutar este Task,

sino, pasa a la otra rama del árbol.

Arrastra del borde inferior del CheckNearbyEnemy y crea un nuevo Selector, dale clic derecho y

selecciona Add Decorator y selecciona Blackboard. En este caso no vamos a crear un Decorator

personalizado, vamos a usar como mismo usamos el Task Wait y Move To que ya vienen con el UE4, el

decorator Blackboard, que nos permite comprobar el valor de un key en el BlackBoard. Selecciona el

decorator y en la sección Flow Control de panel de Detalles, en el atributo Observer aborts selecciona

Both. Este parámetro define como se comportará el árbol cuando no se cumpla la condición. En este caso

lo que queremos es que inmediatamente que TargetActorToFollow esté en NULL se pase a ejecutar la

otra rama, para que continúe como vigilante. Prueba cambiar después este valor a Self para que notes la

diferencia del comportamiento.

En la sección Blackboard, en el atributo BlackBoard Key, selecciona TargetActorToFollow para definirle

que este es el key del blackboard que queremos comprobar y en Key Query selecciona Is Set para

definirle el tipo de condición que queremos comprobar sobre el TargetActorToFollow.

Por último arrastra del selector que tiene este Decorator y conecta el Task MoveToEnemyTask,

selecciónalo y en la sección Default, en el atributo Target Actor To Follow selecciona

TargetActorToFollow.

Page 13: Introducción a la Inteligencia Artificial en Unreal Engine 4umh2154.edu.umh.es/wp-content/uploads/sites/883/2016/05/Intro_-IA... · Dale doble clic y créale una variable int de

Listo ¡!! Guarda, compila y ejecuta el juego y muévete cerca del enemigo, cuando te vea corre rápido para

que te le alejes, verás como inmediatamente que nos alejamos se incorpora a su tarea rutinaria. Si nos

quedamos quietos, y nos alcanza, de momento solo imprimimos en la pantalla un mensaje. En próximos

tutoriales haremos algo más interesante.

Conclusión

Espero que este tutorial te sirva para iniciarte en el complejo mundo de la IA en los juegos desarrollados

con Unreal Engine 4. Como has notado es un tema relativamente complejo, por lo que te recomiendo que

le des más de una pasada al ejercicio, pruebes distintas cosas, juegua con los valores de retorno de los

Tasks para que puedas lograr buena soltura y entender al 100% como es que funciona esta genial

herramienta que nos da el Unreal Engine 4 para implementar la inteligencia artificial de los NPC en

nuestros juegos.

En el próximo tutorial vamos a continuar con este ejemplo implementado los mecanismos para que este

enemigo nos haga algo cuando llegue a nosotros. También le dedicaremos un tiempo a ver esta misma

implementación pero desde C++, y así analizamos las dos variantes ;)