Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Artificial Intelligence
Gamedevelopment

Cree Un Juego de Hockey Utilizando Comportamientos de Direccionamiento de IA: Fundamentos

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Create AI for a Hockey Game Using Steering Behaviors.
Create a Hockey Game AI Using Steering Behaviors: Attack

Spanish (Español) translation by Jorge Montoya (you can also view the original English article)

Hay diferentes maneras de hacer cualquier juego en particular. Por lo general, un desarrollador elige algo que se ajuste a sus habilidades, utilizando las técnicas que ya sabe para producir el mejor resultado posible. A veces, las personas aún no saben que necesitan una cierta técnica - tal vez incluso una más fácil y mejor - simplemente porque ya saben una manera de crear ese juego.

En esta serie de tutoriales, aprenderá cómo crear inteligencia artificial para un juego de hockey usando una combinación de técnicas tales como comportamientos de direccionamiento, que he explicado anteriormente como conceptos.

Nota: Aunque este tutorial está escrito con AS3 y Flash, debería ser capaz de usar las mismas técnicas y conceptos en casi cualquier entorno de desarrollo de juegos.


Introducción

El hockey es un deporte divertido y popular y como videojuego, incorpora muchos temas de desarrollo de juegos, tales como patrones de movimiento, trabajo en equipo (ataque, defensa), inteligencia artificial y tácticas. Un juego de hockey jugable encaja muy bien para demostrar la combinación de algunas técnicas útiles.

Simular la mecánica del hockey, con los atletas corriendo y moviéndose, es un desafío. Si los patrones de movimiento están predefinidos, incluso con caminos diferentes, el juego se vuelve predecible (y aburrido). ¿Cómo podemos implementar un entorno tan dinámico manteniendo a su vez el control sobre lo que está pasando? La respuesta es: usar comportamientos de direccionamiento.

Los comportamientos de direccionamiento tienen como objetivo crear patrones de movimiento realistas con la navegación improvisada. Se basan en fuerzas simples que se combinan cada actualización del juego, por lo que son muy dinámicos por naturaleza. Esto los convierte en la opción perfecta para implementar algo tan complejo y dinámico como un partido de hockey o de fútbol.

Determinación del Alcance del Trabajo

Por el bien del tiempo y la enseñanza, vamos a reducir el alcance del juego un poco. Nuestro juego de hockey seguirá sólo un pequeño conjunto de reglas originales del deporte: en nuestro juego no habrá penalizaciones ni porteros, así que cada atleta puede moverse por la pista:

Juego de hockey utilizando reglas simplificadas

Cada portería será reemplazada por una pequeña "pared" sin red. Para anotar, un equipo debe mover el puck (el disco) para que toque cualquier lado de la meta del oponente. Cuando alguien marca, ambos equipos se reorganizarán, y el puck será colocado en el centro; el partido se reiniciará unos segundos después de eso.

Respecto a la manipulación del puck: si un atleta, digamos A, tiene el puck, y es tocado por un oponente, digamos B, entonces B gana el puck y A se vuelve inamovible por unos segundos. Si el puck alguna vez deja la pista, se colocará en el centro de la pista inmediatamente.

Voy a utilizar el motor de juego Flixel para ocuparme de la parte gráfica del código.  Sin embargo, el código del motor se simplificará o se omitirá en los ejemplos, para mantener el foco en el juego en sí.

Estructuración del Entorno

Comencemos con el entorno del juego, que está compuesto por una pista, un número de atletas y dos porterías. La pista está formada por cuatro rectángulos colocados alrededor del área de hielo; estos rectángulos chocarán con todo lo que los toque, por lo que nada saldrá del área de hielo.

Un atleta será descrito por la clase Athlete:

La propiedad mBoid es una instancia de la clase Boid, una encapsulación de la lógica matemática utilizada en la serie de comportamientos de direccionamiento. La instancia mBoid tiene, entre otros elementos, vectores matemáticos que describen la dirección actual, la fuerza de direccionamiento y la posición de la entidad.

El método update() en la clase Athlete se invocará cada vez que se actualice el juego. Por ahora, sólo borra cualquier fuerza de direccionamiento activa, añade una fuerza errante, y finalmente llama a mBoid.update(). El comando anterior actualiza toda la lógica de comportamiento de direccionamiento encapsulada dentro de mBoid, haciendo que el atleta se mueva (usando la integración de Euler).

La clase de juego, que es responsable del bucle del juego, se llamará PlayState. Tiene la pista, dos grupos de atletas (un grupo por equipo) y dos porterías:

Asumiendo que un solo atleta fue agregado al partido, abajo está el resultado de todo hasta ahora:

Siguiendo el cursor del ratón

El atleta debe seguir el cursor del ratón, por lo que el jugador realmente puede controlar algo. Ya que el cursor del ratón tiene una posición en la pantalla, puede utilizarse como destino para el comportamiento de llegada.

El comportamiento de llegada hará que un atleta busque la posición del cursor, suavemente disminuya la velocidad cuando se aproxima al cursor, y finalmente se detienga allí.

En la clase Athlete, reemplacemos el método errante con el comportamiento de llegada:

El resultado es un atleta que puede seguir el cursor del ratón. Ya que la lógica del movimiento se basa en comportamientos de dirección, los atletas navegan la pista de una manera convincente y suave.

Utilice el cursor del ratón para guiar al atleta en la demostración a continuación:

Agregar y Controlar el Puck

El puck será representado por la clase Puck. Las partes más importantes son el método update() y la propiedad mOwner:

Siguiendo la misma lógica del atleta, el método update() del puck será invocado cada vez que el juego se actualiza. La propiedad mOwner determina si el puck está en posesión de cualquier atleta. Si mOwner es null, significa que el disco está "libre", y se moverá, eventualmente rebotando en las paredes de pista.

Si mOwner no es null, significa que el puck está siendo llevado por un atleta. En este caso, ignorará cualquier control de colisión y se ubicará a la fuerza delante del atleta. Esto se puede lograr usando el vector de velocity del atleta, que también coincide con la dirección del atleta:

Explicación de cómo se posiciona el puck delante del atleta.

El vector ahead es una copia del vector de velocity del atleta, por lo que apuntan en la misma dirección. Después de que ahead se normaliza, puede ser escalado por cualquier valor-digamos 30-para controlar hasta qué punto el puck se colocará por delante del atleta.

Finalmente, la position del puck recibe la position del atleta añadida al ahead, colocando el puck en la posición deseada.

A continuación se muestra el código para todo aquello:

En la clase PlayState, hay una prueba de colisión para comprobar si el puck se superpone a cualquier atleta. Si lo hace, el atleta que acaba de tocar el puck se convierte en su nuevo propietario. El resultado es un disco que "pega" al atleta. En la demostración de abajo, guíe al atleta para que toque el disco en el centro de la pista para ver esto en acción:


Golpeando el Puck

Es hora de hacer que el disco se mueva como resultado de ser golpeado por el palo. Independientemente del atleta que lleva el disco, todo lo que se requiere para simular un golpe por el palo es calcular un nuevo vector de velocidad. Esa nueva velocidad moverá el disco hacia el destino deseado.

Un vector de velocidad puede ser generado desde un vector de posición hacia otro; el vector recién generado irá entonces de una posición a otra. Eso es exactamente lo que se necesita para calcular el nuevo vector de velocidad del puck después de un golpe:

Cálculo de la nueva velocidad del puck luego de un golpe de bastón.

En la imagen de arriba, el punto de destino es el cursor del ratón. La posición actual del puck puede ser usada como punto de partida, mientras que el punto donde debe estar el puck después de haber sido golpeado por el palo puede ser usado como el punto final.

El pseudo-código siguiente muestra la implementación de goFromStickHit(), un método en la clase Puck que implementa la lógica ilustrada en la imagen anterior:

El vector new_velocity va desde la posición actual del puck hasta el destino (theDestination) Después de eso, es normalizado y escalado por theSpeed, que define la magnitud (longitud) de new_velocity. Esa operación, en otras palabras, define la rapidez con que el puck se moverá desde su posición actual hasta el destino. Finalmente, el vector velocity del disco es reemplazado por new_velocity.

En la clase PlayState, se invoca el método goFromStichHit() cada vez que el reproductor hace clic en la pantalla. Cuando sucede, el cursor del ratón se utiliza como destino para el golpe. El resultado se ve en esta demostración:

Añadiendo la I.A.

Hasta ahora, hemos tenido un solo atleta moviéndose por la pista. A medida que se agregan más atletas, la IA debe ser implementada para hacer que todos estos atletas parezcan estar vivos y pensando.

Para lograr esto, usaremos una máquina de estado finito basada en pila (FSM de pila, para abreviar). Como se ha descrito anteriormente, las FSM son versátiles y útiles para implementar IA en juegos.

Para nuestro juego de hockey, una propiedad llamada mBrain será añadida a la clase de Athlete:

Esta propiedad es una instancia de StackFSM, una clase utilizada anteriormente en el tutorial FSM. Utiliza una pila para controlar los estados de IA de una entidad. Cada estado se describe como un método; cuando un estado es empujado a la pila, se convierte en el método activo y se invoca durante cada actualización del juego.

Cada estado llevará a cabo una tarea específica, como mover al atleta hacia el puck.  Cada estado es responsable de terminarse a sí mismo, lo que significa que es responsable de quitarse a sí mismo de la pila.

El atleta puede ser controlado por el jugador o por la IA ahora, por lo que el método update() en la clase Athlete debe ser modificado para verificar esa situación:

Si la IA está activa, se actualiza mBrain, que invoca el método de estado actualmente activo, haciendo que el atleta se comporte en concordancia. Si el jugador está en control, mBrain se ignora del todo y el atleta se mueve según lo guiado por el jugador.

En cuanto a los estados para empujar en el cerebro: por ahora vamos a implementar sólo dos de ellos. Un estado permitirá que un atleta se prepare para un partido; cuando se prepara para el partido, un atleta se moverá a su posición en la pista y se quedará quieto, mirando al disco. El otro estado hará que el atleta simplemente se quede quieto y contemple el disco.

En las siguientes secciones, implementaremos estos estados.

El Estado Libre

Sie el atleta está en estado idle, dejará de moverse y mirará el puck. Este estado se utiliza cuando el atleta ya está en posición en la pista y está esperando que algo suceda, como el comienzo del partido.

El estado se codificará en la clase Athlete, bajo el método idle():

Debido a que este método no se retira a sí mismo de la pila, permanecerá activo para siempre. En el futuro, este estado se abrirá para dejar espacio a otros estados, como ataque, pero por ahora basta con lo que hace.

El método stopAndStareAt() sigue el mismo principio utilizado para calcular la velocidad del disco después de un golpe. Un vector de la posición del atleta a la posición del puck es calculado por the Point - mBoid.position y usado como el nuevo vector de la velocidad del atleta.

Ese nuevo vector de velocidad moverá al atleta hacia el puck. Para asegurar que el atleta no se mueva, el vector es escalado en 0.01, "encogiendo" su longitud a casi cero. Hace que el atleta deje de moverse, pero lo mantiene mirando el disco.

Preparación Para un Partido

Si el atleta está en el estado prepareForMatch, se moverá hacia su posición inicial, deteniéndose sin suavemente allí. La posición inicial es donde el atleta debe estar justo antes de que empiece el partido. Dado que el atleta debe detenerse en el destino, el comportamiento de llegada puede ser utilizado de nuevo:

El estado usa el comportamiento de llegada para mover al atleta hacia la posición inicial. Si la distancia entre el atleta y su posición inicial es menor de 5, significa que el atleta ha llegado al lugar deseado. Cuando esto sucede, prepareForMatch se retira de la pila y empuja a idle, convirtiéndolo en el nuevo estado activo.

A continuación se muestra el resultado del uso de un FSM basado en pilas para controlar a varios atletas. Presione G para colocarlos en posiciones aleatorias en la pista, empujando el estado prepareForMatch:


Conclusión

Este tutorial presentó las bases para implementar un juego de hockey utilizando comportamientos de direccionamiento y máquinas de estado finito basadas en pila. Usando una combinación de estos conceptos, un atleta es capaz de moverse en la pista, siguiendo el cursor del ratón. El atleta también puede golpear el puck hacia un destino.

Usando dos estados y un FSM basado en pilas, los atletas pueden reorganizarse y moverse a su posición en la pista, preparándose para el partido.

En el siguiente tutorial, aprenderás a hacer que los atletas ataquen, llevando el puck hacia el gol, evitando a los oponentes.

Referencias

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.