Desarrollo de un videojuego de naves en HTML5 – Parte 2: Nave disparando

Este es el segundo artículo sobre desarrollo de un videojuego de naves en HTML5. La primera entrega de esta explicación la encuentran en el siguiente enlace: Desarrollo de un videojuego de naves en HTML5 – Parte 1

Recordemos dónde nos habíamos quedado. La nave hasta este momento puede ser controlada por el jugador, a través de las flechas del teclado: Ejemplo, primera parte

Ahora lo llevaremos a la siguiente etapa: Ejemplo, segunda parte. En esta nueva parte, la nave dispara unos proyectiles azules si se presiona la flecha de arriba.

Objetivo de esta segunda etapa

Antes de empezar a meternos con el código, hay que tener bien claro cuál es nuestro objetivo. Vamos a hacer que cuando se presione la flecha de arriba, aparezca un proyectil en el vértice de la nave y que luego sólo se desplace hacia arriba. Cuando llegue a la parte superior de la pantalla, deberá desaparecer. Y esto está referido a dos aspectos. Deberá dejar de visualizarse y además, la o las instancias asociadas a ese proyectil deberán ser eliminadas.

Como añadido, la nave no podrá disparar un nuevo proyectil hasta que el actual no haya desaparecido.

Encontrarán el código fuente completo de esta segunda parte aquí. Se agrega una clase y algunos métodos importantes. Las modificaciones son sutiles y están explicadas en este mismo artículo.

La clase Proyectil

¿Qué va a disparar la nave? Proyectiles. Entonces vamos a necesitar un nuevo objeto que los represente. La clase Proyectil tiene la siguiente estructura.

Nuevamente uso Options para manejar los atributos. Recuerden que no es obligatorio, pero a veces resulta más sencillo su uso. Los atributos son la posición en x del proyectil, la posición en y y además, la imagen que lo representa. La imagen la he dejado, aunque como verán más adelante en realidad no se usa. El motivo es que encuentro muchas similitudes entre los objetos Nave, Proyectil y Alien (que aparecerá después) y quizás, en un futuro convenga hacerlos heredar de una clase en común.

El constructor intialize se encarga de definir los atributos que vienen en options. Hay dos métodos getters para obtener la posición del proyectil y un método que se llama avanzar. Noten que lo que hace este último es decrementar la posición en y del proyectil. ¿Por qué resta y no suma si está avanzando? Porque recuerden que el canvas incrementa hacia abajo en el eje y y queremos que el proyectil vaya hacia arriba.

La nave dispara

La que dispara es la nave que controlamos. Por lo tanto, el proyectil deberá aparecer en el lugar donde esté ubicada. El método que crea el proyectil lo tiene la misma clase Nave, así de esta manera puede acceder a la posición en x y en y para poder posicionar el proyectil donde corresponde. Se agrega el método disparar a Nave.

En este nuevo método, se crea un proyectil pasándole como parámetros la posición de la nave. Pero recuerden que en el canvas, la ubicación de la nave se define por el punto que coincide con la esquina superior izquierda. Así que inicialmente el proyectil aparecerá ahí y no en el medio de la nave, donde realmente queremos. Más adelante corregiremos esto. El objeto Proyectil creado es devuelto como valor de retorno.

Secuencia de disparo

Ahora que tenemos una idea de cómo es la nueva clase Proyectil y el nuevo método que incorpora Nave, podemos empezar a ver la secuencia que produce el disparo. Primero se presiona la tecla que corresponde a la flecha de arriba, eso nos llevará al método ejecutar de la clase Juego, que como ya vimos, es el que gestiona los eventos de teclado.

Se agrega un caso más al switch. Si la tecla es ‘up’, entonces pasará lo siguiente. Primero le preguntaremos al objeto Universo si ya existe un proyectil. La primera vez que disparemos esto será claramente falso, así que se ejecutará el código dentro del if. A continuación, se creará un nuevo Proyectil accediendo a Universo, luego a Nave y finalmente a su método disparar que analizamos antes. El valor de retorno de este método era un objeto Proyectil. Le pasamos esta instancia a universo en su método crearProyectil. Veamos cómo es Universo ahora.

Aparecen dos métodos nuevos: crearProyectil y proyectilOut. El primero lo único que hace es recibir una instancia de Proyectil y guardarla como atributo. De ahora en más, cada vez que que accedamos a este objeto Universo, el proyectil formará parte de él. Quiere decir que si volvemos a presionar la flecha de arriba, esta vez no se creará un nuevo proyectil dado que ya hay uno creado y por lo tanto no ingresará en aquel if que habíamos visto.

El método proyectilOut lo analizaremos en unos instantes. Pero ahora veamos cómo se pasa de tener el proyectil de forma abstracta a visualizarlo por pantalla. En el switch que estábamos viendo quedaba aún una instrucción: this.dibujante.dibujarBala(bala);

Por supuesto, sirve para dibujar el proyectil por pantalla. A Dibujante se le suma un nuevo método.

dibujarBala recibe una instancia de la clase Proyectil. Se crea una imagen que referencia al archivo que corresponde. Aquí pueden ver que aquel atributo que había definido en Proyectil no se está usando. this.kinBala será el atributo que almacenará el proyectil que se visualizará por pantalla. Es, nuevamente un objeto de la clase Kinetic.Image, al igual que la nave. Los parámetros que se necesitan para crearlo son exactamente los mismos: un objeto Image, las coordenadas x e y, un ancho y un alto y, opcionalmente, un nombre.

Se agrega la nueva imagen Kinetic al layer o capa y luego se dibuja el escenario con los nuevos elementos ya incluidos. De esta forma, el proyectil ya aparecerá en pantalla.

El avance y la destrucción del proyectil

Con lo que hemos visto hasta aquí, al presionar la flecha de arriba deberíamos ver aparecer el proyectil y quedarse congelado en esa posición. Sin embargo, va a avanzar y esto ocurre porque se ha modificado el método actualizar de Juego.

Se obtiene la posición en y del proyectil y se chequea si es menor que 0, es decir, si se salió de la pantalla. Si es así, el atributo this.bala pasa a ser null y de esta forma se pierde la referencia al objeto Proyectil que se había creado. Esta es la forma de destruir el proyectil del modelo. Devuelve verdadero si debió destruirse y falso si no.

Volviendo al método actualizar de Juego, lo único que resta es borrar del canvas el proyectil. Esto lo hace el método destruirBala, de Dibujante.

Como ven, una única línea. Se utiliza el método remove al cual se le pasa el objeto kinBala que recuerden es un Kinetic.Image. Lo que hace remove es quitar de la capa el objeto que le pasamos por parámetro. Por eso es importante mantener referencia a los objetos que se van agregando al layer.

Sin embargo, si se fijan, luego no se vuelve a redibujar el stage. No hace falta porque recuerden que actualizar se ejecuta a períodos regulares de tiempo y este llama en cada una de esas ocasiones a redibujar de Dibujante.

Si el proyectil está definido, por lo tanto disparado, se le cambiará la posición a la que corresponda tomando las coordenadas de la instancia Proyectil que ahora empieza a recibir (antes no lo hacía).

Si se fijan, el posicionamiento de la Nave tiene una diferencia en la coordenada x respecto a la etapa anterior de desarrollo. Esto está asociado con la posición en la que aparece el proyectil al disparar. La idea era que apareciera en el vértice de la nave y es por eso que aquí está hecho este ajuste donde la constante CENTRO_NAVE_X tiene un valor que hace que el proyectil se ubique donde necesitamos.

A la vista, el proyectil saldrá del medio de la nave. Sin embargo esto está mal ya que no está representando el modelo. Hay un desfase entre lo que indica el modelo y lo que muestra la vista. Este error no lo noté cuando programé esta etapa. Sólo me preocupe por lo que veía y no por lo que pasaba realmente. Recién en la cuarta etapa, cuando empezaron las colisiones empecé a tener problemas, descubrí el error y lo corregí. Pero como este ejemplo busca mostrar también la forma de construir el código, opté por dejarlo.

Resumiendo

El disparo de la nave se produce cuando el usuario presiona la flecha de arriba del teclado. Esto activa un mecanismo que hace que se cree un objeto Proyectil, luego se dibuje por pantalla. Mediante la continua actualización del modelo del juego, ese proyectil irá cambiando su posición, a la vez que seguirá siendo redibujado.

Cuando el proyectil abandone los límites del juego, será eliminado del modelo y borrado de la pantalla, dejando el lugar para que se cree uno nuevo y comience otra vez el ciclo.

Referencias

Soy programador web y me desempeño como Líder Técnico en Polar Bear Development. Trabajo con tecnologías como PHP, Javascript, MySQL y HTML5 para el desarrollo de sitios y sistemas web. Me especializo en Zend Framework 2 y otros frameworks MVC, como también en WordPress y otros CMS. Lidero equipos de desarrolladores trabajando con Scrum. Vivo en Buenos Aires, Argentina.
 

2 thoughts on “Desarrollo de un videojuego de naves en HTML5 – Parte 2: Nave disparando

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *


*