Inteligencia Artificial en Juegos, Parte 1: Waypoints

1

Tagged

Attached Files

The following files have been attached to this tutorial:

.capx

airplane-shooter.capx

Download now 317.35 KB

Stats

4,470 visits, 4,780 views

Tools

License

This tutorial is licensed under CC BY 4.0. Please refer to the license text if you wish to reuse, share or remix the content contained within this tutorial.

La Inteligencia Artificial puede ser una adición significativa para muchos tipos diferentes de vídeo juegos. Técnicas de inteligencia artificial crean una experiencia mas irresistible para el jugador al simular comportamientos casi humanos en sus oponentes computarizados.

Nuestra meta es crear personajes controlados por la computadora que:

* actúen de manera que no sea completamente predecible ni puramente aleatoria

* respondan/adapten al jugador y el entorno (como con elementos/obstáculos en el layout)

* presenten un nivel de desafío que no sea ni muy fácil ni muy difícil

En este artículo, discutiremos la I.A. en el contexto del movimiento enemigo en un juego de disparos de aeroplanos, con énfasis en la idea de "waypoints": puntos de referencia en el espacio usados para navegación.

La idea principal es crear un objeto Sprite (pequeño e invisible) que funciona como Waypoint; cada uno contendrá dos variables de instancia: ID y NextID. Cada Sprite Enemigo contendrá una variable de instancia llamada TargetID, la cual almacena el ID de un Waypoint, y el angulo de movimiento de cada Enemigo es actualizado constantemente así que se está moviendo hacia el Waypoint correspondiente. Una vez que un Enemigo alcanza el Waypoint deseado, el Enemigo actualiza su TargetID al valor almacenado en la variable NextID del Waypoint, y el recorrido del Enemigo continua. En las secciones siguientes, discutiremos los detalles de implementación, así como también introduciremos técnicas adicionales (cada una de las cuales es opcional) para hacer que el movimiento del enemigo parezca mas "realista"

Este artículo incluye un archivo Capx descargable que ilustra las ideas presentadas.

Configuración Inicial

Movimiento

Primero, crearemos un nuevo proyecto con tamaño de ventana y tamaño de layout de 800 por 600 pixeles. entonces, creamos:

* un objeto Sprite llamado "Jugador" con comportamientos 8-Direction y Bound to Layout.

* un objeto Sprite llamado "Enemigo" con comportamientos Bullet y Timer , y una variable de instancia llamada TargetID con valor inicial 1.

* un objeto Sprite llamado "MisilEnemigo" con comportamientos Bullet and Destroy Outside Layout.

* un objeto Sprite llamado "Waypoint" con dos variables de instancia: ID y NextID

Entonces crea múltiples instancias del sprite Waypoint en tu layout y organízalas como desees. Cambia los valores de su variable de instancia ID a números 1, 2, 3,... en el orden en que deban ser visitados por los enemigos. Para cada Waypoint, cambia el valor de NextID al valor siguiente al de ID , excepto el último Waypoint, cuyo valor en NextID debe fijarse a 1 (asumiendo que estarán recorriendo un bucle).

Hay dos eventos para mantener a los enemigos en movimiento a través de los waypoints. Primero, para cada Enemigo necesitas seleccionar el Waypoint cuyo ID es igual al valor del TargetID de Enemigo, y fijar el angulo de movimiento hacia ese Waypoint (el angulo puede ser calculado usando la función angle(Enemigo.X, Enemigo.Y, Waypoint.X, Waypoint.Y)). También, asegúrate de que la propiedad "Set Angle" del comportamiento Bullet de Enemigo está fijada en "No", y rota a Enemigo hacia Jugador (esto será importante cuando los enemigos creen objetos al disparar al jugador). Segundo, si un enemigo choca con un Waypoint y el ID de ese Waypoint's es igual al TargetID de Enemigo, entonces Enemigo detemina su siguiente objetivo fijando el valor de su TargetID igual al valor de NextID del Waypoint.

Disparando

Hemos configurado el código así que los enemigos están orientados hacia el jugador; a continuación, nos gustaría que ellos disparen a intervalos regulares. Hay un número de maneras para lograr este resultado:

* Podemos configurar el evento: Every X Seconds -- Enemigo Spawns MisilEnemigo. Esto causa el poco realista (y por lo tanto indeseable) resultado que todos los enemigos en pantalla dispararán al Jugador en el mismo momento exacto.

* Cuando nuevos enemigos son creados, iniciamos una acción Timer en cada uno para activar regularmente, digamos cada, 1.5 segundos, y usar la condición On Timer para que un Enemigo cree un MisilEnemigo. Esto es ligeramente mejor, ya que los enemigos pueden dispara en diferentes momentos, pero un jugador atento detectará una regularidad mecánica en los patrones de disparo.

* Una mejor opción es modificar el enfoque anterior, y configurar un Timer con una duración calculada aleatoreamente (digamos, random(1.0, 2.0)) activarla una vez, y entonces tras la activación de la condición On Timer, configurar otro Timer de la misma manera (duración aleatoria, tipo Once). Allí no hay patrones en el tiempo de disparo, y así puede sentirse mas realista.

Adicionalmente, puede no ser el caso que todos los enemigos tengan puntería perfecta, así para incorporar esta característica puedes calcular y configurar el angulo de movimiento del misil para que sea el angulo hacia el jugador más un pequeño valor aleatorio entre -X y X. (Para que esto funcione correctamente, los objetos MisilEnemigo necesitan tener la propiedad Set Angle de Bullet fijada en "No".)

Variaciones de Ruta

Tras un momento, los jugadores pueden ver que todos los enemigos parecen estar moviéndose a lo largo del mismo recorrido (aunque el recorrido pueda ser complicado). ¡De seguro, enemigos inteligentes no actuarían de tal manera!

Como nuestra siguiente mejora, agregaremos una segunda variable de instancia a los objetos Waypoint, llamada NextID2. La manera en que usaremos esta variable es la siguiente: cuando un Enemigo alcance su Waypoint objetivo, agregaremos un sub-evento con la condición Compare Two Values: random(0.0 , 1.0) < 0.5 (alias "lanzar una moneda"). Si esto es verdadero, entonces el nuevo TargetID de Enemigo será fijado a NextID, de lo contrario (usando una condición Else), el nuevo TargetID será fijado a NextID2.

Esto puede lograrse usando los eventos debajo; este evento reemplazaría entonces los eventos previos con la condición Enemigo On collision with Waypoint.

Además, por cada instancia de Waypoint en nuestro layout, necesitamos fijar manualmente los valores de NextID2. Por ejemplo,el Waypoint con ID=1 puede fijarse NextID=2 y NextID2=3. El Waypoint con ID=4 puede fijarse NextID=5 and NextID2=1.

Movimiento de Waypoint

Nuevamente, después un momento el jugador puede notar que aunque los enemigos pueden seguir recorridos diferentes, los puntos en los cuales los enemigos cambian de dirección son predecibles.

Para remediar esta situación, también haremos que todo Waypoint se muevan mientras los sprites Enemigo se dirigen hacia ellos. Esto puede lograrse fácilmente agregando el comportamiento Sine a los Waypoints. El comportamiento Sine puede cambiar el valor de una propiedad de un sprite (tales como posición horizontal, posición vertical, tamaño, opacidad, etc.) de manera periódica. el tipo de valor que será cambiado es exhibido junto a la propiedad "Movement" en el panel de propiedades; dejaremos la configuración por defecto fijada en"Horizontal". La propiedad "Magnitude" del comportamiento Sine controla la cantidad de cambio; la dejaremos en su valor por defecto de 50.

Para obtener un movimiento de Waypoint más interesante, podemos agregar un segundo comportamiento Sine al objeto Waypoint (el cual aparecerá como "Sine2" bajo el encabezado de comportamientos. Si cambiamos el movimiento a "Vertical" y cambiamos el valor de "Period Offset" a 1 (o en general, 1/4 del valor del periodo), esto causará que los Waypoints se muevan en círculo.

((Nota técnica matemática: al cambiar la compensación del periodo en una función Sine a 1/4 del valor del periodo, en realidad estamos creando una función Cosine, y en general, la ecuación de un círculo puede ser parametrizada usando una función Sine o Cosine para la coordenada X , y usando la otra para la coordenada Y.))

Velocidad de Enemigo

Otro toque realista que podemos agregar a los movimientos de Enemigo es variar la velocidad. Moverse siempre a la misma velocidad cae dentro de la categoría de patrones no realistas que deseamos evitar. En su lugar, configuraremos nuestros objetos Enemigo para alternar entre entre movimiento lento y rápido.

Para lograr esto, agregaremos un comportamiento Sine a los objetos Enemigo, y fijaremos la propiedad Movement en "Value Only". Entonces, en la hoja de eventos, tendremos la velocidad de Bullet para Enemy es 150 + Enemy.Sine.Value, y con la Magnitude de Sine fijada a 50, esto significa que la velocidad fluctuará entre 150 - 50 = 100 y 150 + 50 = 200.

Conclusión

En este artículo, hemos visto una variedad de maneras para implementar comportamientos que pueden ayudar que los objetos Enemigo parezcan más realistas o inteligentes de lo que son en realidad. Hay probablemente muchas otras mejoras que pueden ser hechas; ¡siéntete en libertad de compartir tus ideas en los comentarios!

.CAPX

airplane-shooter.capx

Download now 317.35 KB
  • 0 Comments

  • Order by
Want to leave a comment? Login or Register an account!