Personajes enemigos
En Unreal Engine, los PNJ y enemigos se suelen configurar de forma muy parecida al jugador, en el sentido de que un enemigo tiene un controlador y un peón. De hecho, los enemigos y el jugador comparten algunas funciones en una clase base común, AParrotCharacterBase. En el juego Parrot, se utilizan árboles de comportamiento para controlar el comportamiento enemigo de forma que la clase de controlador de IA se utiliza par crear los controladores de nuestro enemigo. Comenzaremos por el blueprint de animación ya que la configuración es común para todos los enemigos.
Los blueprints de animación del enemigo
Plantillas de blueprints de animación
En casos en los que hay múltiples mallas esqueléticas y por lo demás se comparten las mismas necesidades de animación, puedes crear lo que se llama una plantilla de blueprint de animación (consulta Animation Blueprint Linking (Vinculación de blueprints de animación). En este caso, hay cuatro tipos de enemigo, un esqueleto decapitado, un esqueleto, un tiburoncito y un jefe tiburón. Como todos comparten un esqueleto y una configuración de animación idénticos, se puede reutilizar una plantilla que implementa el grafo de animación y el grafo de eventos para todos. Puedes ver cómo se ajusta esta plantilla desde aquí. Blueprints > Enemy > EnemyBase > ABT_EnemyBase. Se parece mucho al blueprint de animación del jugador porque se ha configurado una plantilla casi igual a la de ese blueprint de animación.
Blueprints de animación
En este caso, toda la implementación está en la plantilla, mientras que las animaciones en sí no están presentes, dado que la plantilla no referencia un esqueleto específico. Cada enemigo tiene su propio blueprint de animación derivado de la plantilla, y cada uno tiene sus animaciones de esqueleto asignada en las sobrescrituras del grafo de animación. Puedes consultar aquí un ejemplo: Blueprints > Enemy > HeadlessSkeleton > ABP_Enemy_HeadlessSkeleton.
Abajo, hemos seleccionado las animaciones correspondientes para la malla esquelética del esqueleto decapitado para cada estado de animación.
El peón de enemigo
Los personajes enemigos de Parrot comparten muchas funciones con el jugador, más en concreto, los puntos de impacto y funciones similares (como la muerte). Esta implementación compartida se puede ver en AParrotCharacterBase. En el caso de la implementación específica de enemigos, tenemos una subclase AParrotEnemyCharacterBase. Esta se encarga de toda la implementación de cómo funciona el sistema de patrulla, el combate y otros. Puedes encontrar más información acerca de cómo funciona el combate en la documentación de El combate en Parrot.
En esta configuración, las comprobaciones de impacto y daño se realizan en el enemigo con una implementación sobre cómo activar volúmenes en la clase base de blueprint que se encuentra aquí: Blueprints > Enemy > EnemyBase > BP_EnemyCharacter_Base.
Esta implementación se hace aquí, en vez de gestionarse en la clase nativa de C++. porque los volúmenes de activación tienen que ser distintos para cada enemigo debido a las formas, tamaños y complejidades de malla diferentes de cada enemigo. Esto es necesario porque un volumen de activación heredado no se puede modificar en la clase derivada.
El controlador de IA del enemigo
Como ya se ha mencionado, los enemigos se controlan mediante árboles de comportamiento, de forma que hay una clase base derivada de AIController en AParrotEnemyAIControllerBase. Aquí verás una gama de BlueprintImplementableEvents que se utilizan para enviar datos a la pizarra para que se utilicen en los árboles de comportamiento.
Puedes ver cómo el controlador de IA pasa los datos al árbol de comportamiento y cómo gestiona la detección de la presencia del jugador en Blueprints > AI > EnemyBase > BP_EnemyController_Base.
Cómo crear IA con árboles de comportamiento
Unreal Engine ofrece una infraestructura flexible y potente para compilar IA mediante árboles de comportamiento. Puedes encontrar una guía de inicio rápido para configurar la plantilla básica con algunos comportamientos en la Behavior Tree Quick Start Guide (Guía de inicio rápido a los árboles de comportamiento). Los resultados de dicha guía se utilizan como base para la IA del enemigo y crear cambios para configurar los comportamientos necesarios.
Hay dos tareas de árbol de comportamiento para proporcionar las funciones básicas necesarias para patrullar y atacar al jugador: Blueprints > AI > EnemyBase > BTT_FindNextPatrol y Blueprints > AI > EnemyBase > BTT_AttackPlayer. Hay una pizarra compartida, Blueprints > AI > EnemyBase > BB_Enemy_Base, ya que la infraestructura para que todas las funciones sean compatibles está presente en todos los enemigos. De la implementación del árbol de comportamiento depende decidir qué funciones utilizar para personalizar los comportamientos del enemigo.
Cada enemigo tiene una configuración de árbol de comportamiento diferente, para que cada uno se comporte de forma diferente. Puedes echar un vistazo a los cuatro en las siguientes ubicaciones:
Blueprints> AI > HeadlessSkeleton > BT_HeadlessSkeleton
Blueprints > AI/Skeleton > BT_Skeleton
Blueprints > AI > Sharky > BT_Sharky
Blueprints > AI > BossShark > BT_Boss_Shark
Cómo crear un sistema de puntos de ruta de patrulla configurable
En el caso de los enemigos, Parrot no quiere la misma función de patrulla que se muestra en la guía de árboles de comportamiento, que es una en la que la IA selecciona al azar un punto navegable en un radio cada 4 segundos. Lo que se quiere es que los enemigos puedan seguir una ruta de patrulla de ida y vuelta de forma activa, como sucede en muchos otros juegos de plataformas.
Para ello, Parrot ha creado su propio sistema que proporciona las funciones necesarias. Este consiste en el UParrotEnemyPatrolRigComponent, que se puede colocar en la escena para configurar una patrulla. Este componente utiliza subobjetos de clase por defecto para instanciar un spline. El spline se utiliza para configurar los puntos de ruta de patrulla, dos volúmenes de activación (que sirven para activar comportamiento de la IA) y un visualizador exclusivo del editor que traza el orden de los puntos de ruta. El orden de los puntos de ruta permite que se vea la dirección de la ruta de patrulla durante la edición. Puedes consultar los detalles de la implementación en C++ en UParrotEnemyPatrolRigComponent.
Esta implementación es un componente para que se pueda adjuntar un rig de patrulla a cualquier actor en una escena. Esto permite colocar un rig a un objeto en movimiento y significa que la patrulla seguirá colocada en el lugar correcto del espacio local. Si quieres colocar un rig de patrulla que no esté vinculado a un actor existente en una escena, contamos con AParrotEnemyPatrolRigActor, que es un actor que se puede colocar en cualquier lugar de una escena y genera un UParrotEnemyPatrolRigComponent como un subobjeto por defecto en sí mismo.
Esta implementación permite seleccionar que aparezca un enemigo durante el tiempo de ejecución para que siga la ruta de patrulla. En este proceso se utiliza una función de Unreal Engine que se llama generación diferida de actores, que permite que un actor aparezca en dos fases: en la primera, se construye el objeto actor sin ejecutar ninguna inicialización de AActor, como BeginPlay. Esto brinda la oportunidad de realizar cualquier configuración o ajuste necesario para que el actor lo tenga antes de inicializarse. Tras realizar esta configuración, se invoca la segunda fase para terminar la aparición e inicializar el actor. Esto se hace para el rig de patrulla de forma que cuando aparezca el actor enemigo, el spline de patrulla y los volúmenes de activación se puedan conectar al actor, permitiendo que se procesen durante la inicialización del actor y que la secuencia de patrulla comience automáticamente.
Puedes ver el código que realiza la generación diferida de actores en UParrotEnemyPatrolRigComponent. En blueprint existe una función similar en la que puedes etiquetar propiedades en un actor de blueprint como Expose on Spawn (Exponer al generar), lo que permite pasar argumentos al nodo de generación de blueprint, que se establecen antes de que se realice la inicialización del actor.
En la imagen de abajo, se puede ver un ejemplo de cómo está configurado uno de estos rigs de patrulla en Maps > Level_1 > Level_1 con un rig de patrulla de dos puntos, que empieza en el punto 0 a la derecha. Este rig de patrulla se ha configurado para que genere un BP_EnemyCharacter_Skeleton.
Debajo, hay un menú de activación de habilitador de visibilidad para identificar de forma sencilla los puntos de ruta del rig de patrulla durante la edición. La mayoría de las casillas de verificación del habilitador de visibilidad están seleccionadas. Puedes ver más información sobre cómo se implementa esto, en un componente de visualizador exclusivo del editor: UParrotPatrolRigDebugVisualizer.