En este artículo, continuamos nuestra discusión de técnicas de inteligencia artificial en los juegos e introducimos la idea de máquinas de estado. Una máquina de estados es un método para modelar el comportamiento del personaje que tiene las siguientes características:
* Cada personaje puede estar en uno de un número de estados predefinidos, como "ataque", "defensa", "correr", "patrulla", etc., cada uno de los cuales tiene una o más acciones asociadas.
* El personaje controla lo que sucede en su entorno, cosas tales como la distancia al jugador, las medidas adoptadas por el jugador, la ubicación de los obstáculos y los artículos, etc.
* Bajo ciertas condiciones, el personaje va a cambiar su estado; por ejemplo, si el jugador está cerca de un personaje y el jugador está atacando, entonces el personaje puede cambiar su estado a "defender".
En este artículo, analizamos la implementación de máquinas de estado para controlar el comportamiento del enemigo en un juego de plataformas.
Configuración inicial
Empezamos con un layout y una ventana de tamaño de 800 por 600, un Sprite con el comportamiento sólido para el suelo y las paredes del layout, y un Sprite con el comportamiento Jump-Thru para plataformas.
Creamos un Sprite llamado "Enemy" con el comportamiento de la plataforma, y cambiamos los "controles por defecto" (Default Controls) a "No", porque no queremos que estos objetos sean controlados por el teclado. Agregar una variable de instancia al enemigo llamada "State", y es una buena idea añadir una descripción a esta variable que liste los diferentes valores y las correspondientes acciones. Para este ejemplo, utilizaremos lo siguiente: 0 representa "sin movimiento", 1 representa "mover hacia la izquierda", 2 representa "mover hacia la derecha".
Luego, en la hoja de eventos, creamos los eventos para controlar cada enemigo en función de su estado actual. Por cada enemigo, habrá una serie de eventos secundarios para comprobar el valor de la variable de instancia llamada "State". En particular, si State=0 entonces no hay ninguna acción, si State=1 simulamos el botón hacia la izquierda del control de la plataforma, y si State=2 simulamos el botón hacia la derecha del control de la plataforma.
Además, cada enemigo en el layout debería tener su valor State ajustado a algo, probablemente un valor diferente a 0.
Respondiendo al Entorno
A continuación, necesitamos que el enemigo se mueva de una manera inteligente alrededor de su entorno.
Paredes
En los primeros eventos vamos a hacer que el enemigo invierta la dirección cuando se encuentre con una pared. El comportamiento de la plataforma tiene una condición útil para esta ocurrencia: Platform - Is By Wall. Selecciona esta condición, y selecciona la opción "izquierda"; en este caso, cuando hay una pared directamente a la izquierda del enemigo, queremos que el enemigo se mueva hacia la derecha, por lo que vamos a establecer la variable State del enemigo en 2. Del mismo modo, usamos la condición "Platform - Is By Wall " y seleccionamos la opción "derecha", en este caso ponemos la variable State del enemigo a 1, lo que provocará que el enemigo se mueva a la izquierda.
Ahora bien, si hemos creado parte de nuestro layout con este aspecto: (los rectángulos marrones son objetos sólidos, los rectángulos verdes son objetos Jump-Thru, y el Sprite rojo es nuestro enemigo controlado por I.A.)
nuestro enemigo se moverá hacia atrás y adelante, cambiando de dirección una vez que alcanza el lado.
Los disparadores fijos
Como nuestra próxima mejora, podemos querer que el sprite enemigo cambie de dirección en posiciones distintas de las paredes, y en algunos lugares es posible que queramos que salte; podemos incluso querer que deje de moverse por completo (tal vez si se ha llegado a su destino). Para lograr esto, vamos a crear un conjunto de objetos Sprite, uno para cada uno de los cambios de comportamiento deseados; también vamos a configurar eventos correspondientes, de modo que cuando un enemigo se superpone a uno de estos sprites, ya sea cambia su estado o simula presionando Jump en el caso de una acción de salto. En el ejemplo capx proporcionado, estos sprites se nombran TriggerLeft, TriggerRight, TriggerStop y TriggerJump. Es un simple dibujo hecho a mano para cada uno, para que sea más fácil visualizar los correspondientes cambios de estado enemigo con el fin de diseñar el nivel; cuando se ha logrado la configuración deseada, tendrá que establecer la propiedad Visibility de cada una de ellas en False, de manera que el jugador no pueda ver cómo se controlan los enemigos.
El conjunto de eventos para estos disparadores es como sigue:
Y a continuación se muestra un layout de ejemplo utilizando los disparadores. Esta configuración hará que el enemigo voltee cuando se alcanza el final de la plataforma (para que no se caiga), y cuando alcanza el centro de la plataforma, saltará.
Disparadores al azar
Una de las reglas cardinales para la I.A. es que, con el fin de parecer realista, no debe haber un patrón predecible de acciones. Para aumentar la variación de las acciones de cada enemigo, creamos otro conjunto de objetos Sprite de tal manera que, cuando se toca, tiene una oportunidad de 50/50 de que el enemigo cambie de estado o simule una acción de salto. En el archivo capx ejemplo, éstos son nombrados TriggerLeft50, TriggerRight50, TriggerJump50 y TriggerLeftRight50. Los eventos para encontrarse con los primeros tres de éstos son similares a sus contrapartes de disparadores fijos anteriores, excepto que se añade un factor de azar a la condición (System - Compare Two Values: random(0.0, 1.0) < 0.5), y tenemos que utilizar la condición "On Collision With" en lugar de "Is Overlapping", porque los sprites enemigos sólo deben tener una oportunidad de desencadenar la condición aleatoria por acción. El sprite TriggerLeftRight50 añade aún más impredecibilidad a la mezcla; cuando un enemigo se encuentra con este objeto, existe una probabilidad del 50 por ciento que el enemigo cambie de dirección.
Los eventos para este disparador son los siguientes:
La siguiente imagen muestra una de las muchas configuraciones posibles con estos disparadores al azar; si ejecuta el archivo capx, verá al sprite enemigo correr y saltar alrededor del nivel de una manera aún impredecible.
Respondiendo al jugador: Línea-de-visión
Otra característica que puede valer la pena añadir es una interacción del enemigo con el jugador. En particular, cuando un enemigo ve un jugador, a menudo el enemigo persigue al jugador. Aquí, vamos a discutir cómo lograr esto mediante el comportamiento Line-Of-Sight.
Hemos creado un sprite jugador con el comportamiento de 8 Dirección, controlado por el usuario.
También hemos añadido el comportamiento Line-Of-Sight al objeto Enemy. Para hacerlo más realista, vamos a cambiar algunas de las propiedades de este comportamiento.
En primer lugar, por defecto los únicos objetos que bloquean la visión enemigo son los que tienen el comportamiento sólido, pero en nuestro ejemplo, sería también con cualquier objeto con el comportamiento Jump-Thru para bloquear la visión del enemigo. Para lograr esto, en el panel de propiedades del enemigo, por debajo de "Line of Sight", cambie la propiedad de Obstáculos de "Solids" a "Custom". A continuación, en la ficha de evento, tenemos que añadir un evento que ocurra en "On Start of Layout", siendo las acciones "Enemy - Line of Sight: Add Obstacle", y seleccione los objetos de forma que la pared sea sólido; a continuación, añadir esta acción nuevamente, esta vez la selección los objetos de la plataforma como con el comportamiento Jump-Thru .
En segundo lugar, podemos (y debemos) cambiar la propiedad Range del comportamiento "Line of Sight" a algo más pequeño, teniendo en proporción con el tamaño de tu diseño; en este ejemplo, hemos establecido a 300.
Por último, tenemos que especificar en la hoja de eventos lo que sucede cuando un enemigo puede ver al jugador (que en nuestro ejemplo significa que el enemigo está dentro de los 300 píxeles del jugador en cualquier dirección y no hay obstáculo visual entre el enemigo y el jugador ). Para este ejemplo, se compara la coordenada X del jugador a la coordenada X del enemigo para determinar si el jugador está a la izquierda o a la derecha del enemigo, y ponemos el estado del enemigo en consecuencia. Además, si el enemigo ve al jugador saltar, a continuación, después de un breve retraso (utilizando System - Wait action) el enemigo también tratará de saltar. Puedes descargar y ejecutar el archivo de ejemplo capx y presiona las teclas de dirección para mover el jugador, para tener una idea de cómo funcionan estos eventos, y para cambiar los valores de los parámetros, si lo deseas.
Esperamos que este artículo te haya dado algunas ideas sobre cómo implementar I.A. en tu propio juego de plataformas. Si tienes alguna idea adicional, ¡no dudes en compartirlas en los comentarios!