Como desarrollar el juego Asteroides con el motor de juegos Impact de HMTL5.
En este tutorial voy a mostraros cómo podemos crear un sencillo juego de combate espacial, el conocido Asteroides utilizando el motor de juegos Impact, uno de los más potentes a día de hoy.
Impact es un motor comercial, de manera que tendrás que adquirir una licencia antes de poder utilizarlo, pero eso no es motivo para que no puedas, al menos, intentar seguir este tutorial y tener una idea razonablemente buena de cómo funciona.
También, para este tutorial, necesitarás estos recursos de juego, por lo que te recomiendo que los descargues ahora si tienes intención de seguir mis explicaciones.
A veces conviene gastarse algo de dinero
Quiero dejarlo claro: Impact no es gratis. Para cualquiera como yo seguramente esta frase no le guste mucho. Después de todo, aquí estamos utilizando tecnologías Web abiertas, así que ¿por qué me quieren cobrar por algo que, al fin y al cabo, se basa en tecnologías gratuitas? Bueno, por decirlo en pocas palabras: el desarrollador tiene que vivir de algo. Pero por encima de todo, quizá lo más importante es que el motor Impact es tremendamente potente, sólido y bien documentado. Incluso iría más lejos diciendo que es el mejor motor de juegos en HTML5 que jamás he tenido ocasión de utilizar hasta ahora, a pesar de que no es perfecto al 100% (¿y quién lo es?).Estoy absolutamente seguro de que el precio de este motor solo cubre una mínima parte del tiempo y del esfuerzo que les ha llevado crearlo y darle soporte. En resumen, si no te puedes permitir pagar los 99 dólares de la licencia, te recomiendo que eches un vistazo a algunos de los motores de juegos para HTML5 gratuitos que también existen.
Introducción al motor de juegos Impact
Como decía antes, Impact es un motor de juegos potente cuyo desarrollo ha supuesto, obviamente, una gran cantidad de tiempo y esfuerzo. Funciona con la mayoría de navegadores compatibles con el canvas de HTML5, y también con el iPhone, iPod Touch y el iPad. Desde mi punto de vista, lo que hace que Impact sea muy superior a sus competidores es su excelente documentación, los tutoriales, y un soporte realmente útil al que se puede acceder a través de los foros. Impact sin duda da una impresión de producto de calidad que los demás motores no ofrecen.ç
Impact incluye una auténtica mina de funcionalidades, en forma de entidades (objetos dentro del juego), funciones de física, animaciones, entrada de usuario, detección de colisiones, audio e incluso un editor de niveles completo (que veremos más adelante). En esta lista no se incluye ni mucho menos todo lo que lleva dentro este motor, pero espero que en este ejemplo pueda mostrar de alguna manera la inmensa cantidad de trabajo tedioso que nos ahorra. Además nos ayuda la manera en que está construido el motor, utilizando un modelo de orientación a objeto que nos permite, de la manera más eficiente, conectarlo y jugar con nuevas posibilidades en nuestros juegos, ampliando lo que ya tenemos. Es rápido y sencillo y es todo lo que realmente necesitas si te gusta programar juegos.
El Editor de Niveles Weltmeister es una de las joyas de la corona de Impact, y es quizá una de las funcionalidades visuales más impresionantes del motor. Este editor de niveles es realmente una interfaz gráfica que nos permite crear todo el escenario del juego, desde el simple diseño de los niveles a la colocación de los objetos y la definición de sus interacciones mutuas. ¡Es sinceramente, impresionante!
Conviene destacar que en este tutorial no vamos a utilizar el Editor de Niveles Weltmeister, puesto que quiero enseñar cómo se crea el juego descendiendo al nivel del código bruto, pero os recomiendo que veáis este tutorial en vídeo sobre el editor para haceros una idea de lo que puede conseguirse con él.
Qué vamos a hacer
En este tutorial vamos a crear un juego de ambiente especial sencillo, en el que una nave tiene que evitar que le atropellen los asteroides que circulan a su alrededor. Nos vamos a olvidar de momento de funciones como el audio o el disparo, que solamente servirían en este momento para complicar las cosas, pero tranquilo, porque son funciones que sin duda podrás añadir más tarde por ti mismo sin demasiado esfuerzo.
¿Por qué no echas un vistazo a este video del juego para ver cómo funciona?
Instalación de Impact
Antes de meternos en harina con el juego necesitaremos instalar el motor Impact. Es bastante sencillo, pero le voy a dedicar un rato porque interesa saber cómo va. MI recomendación es que os leáis la página que habla de la instalación de Impact si queréis una guía más detallada del proceso.Utilizar un dominio para el desarrollo
Una de las cosas más importantes a tener en cuenta a la hora de desarrollar con Impact es que necesitamos trabajar sobre un servidor Web, y que necesitamos acceder al juego con un nombre de dominio. Podemos utilizar un servidor online o bien un servidor de desarrollo local empleando algún software de publicación web como MAMP (para Mac), WAMP (para Windows), o XAMPP (multiplataforma). Este requisito se debe a temas de seguridad en Javascript y no tiene nada que ver con el motor Impact.Descarga y descompresión
Después de comprar una licencia de Impact recibirás un correo con un enlace al sitio de descarga y la clave de licencia. Mantén esta clave bien guardada. Este enlace de descarga está asociado a tu cuenta, de modo que si lo pierdes, tendrás que ponerte en contacto con los desarrolladores de Impact para que te lo vuelvan a enviar. Los primeros pasos son, por tanto, descargar tu copia personal del motor, descomprimir el archivo ZIP, abrir la carpeta impact y mover archivos y carpetas a tu servidor Web. Sabrás si todo ha ido bien porque podrás ver un mensaje en pantalla cuando hayas cargado el archivo index.html en tu navegador:
Los archivos que acabas de copiar son los archivos del núcleo del motor (la carpeta lib/impact) y algunos archivos de ejemplo que puedes utilizar para crear tus juegos (la carpeta lib/game). Los demás archivos que ves forman parte del Editor de Niveles Weltmeister (la carpeta lib/weltmeister) y algunas otras herramientas de ayuda para cuando quieras crear un paquete con tu juego para su distribución al público (la carpeta tools). A lo largo de este tutorial solo tienes que atender a la carpeta lib/game, y al archivo index.html
Configuración del HTML y CSS
El archivo HMTL por defecto incluye CSS dentro del código, así que vamos a ir haciendo algunos cambios y sacamos el CSS fuera, a un archivo independiente.Abre el archivo index.html, borra todo y añade este código:
1. <!DOCTYPE html>
2. <html>
3. <head>
4. <title>Impact Asteroids Game</title>
5. <meta charset="utf-8">
6. <link href="game.css" rel="stylesheet" type="text/css">
7. <script type="text/javascript" src="lib/impact/impact.js"></script>
8. <script type="text/javascript" src="lib/game/main.js"></script>
9. </head>
10. <body>
11. <canvas id="gameCanvas"></canvas>
12. </body>
13. </html>
Después créate un archivo nuevo, llamado game.css y lo pones en la misma carpeta que index.html. En el archivo de estilos CSS añade esto:
1. * { margin: 0; padding: 0; }
2. html, body { height: 100%; width: 100%; }
3. canvas { display: block; }
4. body {
5. background: #000;
6. }
7.
8. #gameCanvas {
9. height: 512px;
10. left: 50%;
11. margin: -256px 0 0 -384px;
12. position: relative;
13. top: 50%;
14. width: 768px;
15. }
Lo que hace es simplemente forzar un reset básico por CSS y centrar el juego en la pantalla del navegador. Puedes ver aquí que hemos establecido la anchura y altura de la ventana de juego con una directiva CSS y tiene un tamaño bastante considerable. Luego explicaré por qué he elegido estas dimensiones.
Instalación del archive principal del juego
Si actualizas la pantalla del navegador lo único que vas a ver es un fondo negro. Esto es porque hemos eliminado el juego al eliminar el código HMTL y cambiar el ID del elemento canvas. ¡No pasa nada!, ahora lo arreglamos.Primero, abre el archivo lib/game/main.js, que es el archive principal del juego. Luego explicaré qué hace cada una de las secciones en que se divide este archivo, pero por ahora nos vamos a limitar a hacer un par de cambios para que podamos ver otra vez el mensaje en pantalla.
Busca la llamada a ig.main al final del archivo y cámbiala por esta otra:
1. ig.main( "#gameCanvas", MyGame, 60, 768, 512, 1 );
Lo que acabamos de hacer es cambiar el primer argumento para que apunte al nuevo id del elemento canvas, el que hemos modificado antes. También hemos modificado los argumentos cuarto y quinto para adaptarlos a las nuevas dimensiones del juego, y también el último argumento, para evitar que el programa amplíe la pantalla de juego al doble de este tamaño. Estamos utilizando dimensiones concretas porque el juego se basa en teselas y las teselas que vamos a utilizar son de 64x64 pixels, y como sabes, 768 y 512 son dos múltiplos de 64. Esto es todo.
Comprueba la documentación sobre la función ig.main si quieres saber cómo funciona con más detalle.
Si actualizas ahora el contenido de la pantalla podrás ver de nuevo el mensaje que salía al principio, aunque esta vez parece algo más pequeño, porque ahora impedimos que el juego cambie de escala y se amplíe en pantalla.
Módulos
Será muy conveniente que dedique un rato a explicar cómo funcionan los módulos, puesto que serán lo que vamos a utilizar para crear el juego. Aquí puedes ver el código del módulo dentro del archivo main.js que andábamos mirando antes, con algunas partes eliminadas en las que no hace falta que entremos ahora:
1. ig.module(
2. "game.main"
3. )
4. .requires(
5. "impact.game",
6. "impact.font"
7. )
8. .defines(function(){
9. // Main module code
10. });
La primera sección, ig.module, define un módulo nuevo, cuyo nombre se declara en la segunda línea, en este caso game.main. El nombre es representativo de su estructura de carpetas, de modo que "game.main" se refiere a un archivo llamado main.js, que está dentro de la carpeta lib/game. Es una fórmula realmente inteligente. La segunda sección, requires, enumera los módulos necesarios que deben cargarse antes de ejecutar este módulo. Cada módulo sigue la misma convención de nombre indicada antes, y así impact.game" se refiere al archivo game.js de la carpeta lib/impact. Por último, la sección defines es donde debe aparecer el código del módulo. Esta sección no se ejecutará hasta que se hayan terminado de cargar todos los módulos necesarios.
Creación de las entidades del juego
Ahora que ya está preparado el archive principal, es el momento de crear las entidades del juego. Las entidades son cualquier cosa dentro del juego que tengan interactividad: el jugador, los enemigos o ciertos elementos del paisaje, por ejemplo.La entidad Asteroide
Vamos a crear nuestras entidades en forma de módulos, así que lo primero que haremos es crear un archivo llamado asteroid.js dentro de la carpeta lib/game/entities. Abre el archive y añade el siguiente código para crear el módulo de la entidad:
1. ig.module(
2. "game.entities.asteroid"
3. ).requires(
4. "impact.entity"
5. ).defines(function() {
6. // Subclase de ig.Enitity
7. EntityAsteroid = ig.Entity.extend({
8. // Establece alguna de las propiedades
9. size: {x: 64, y: 64},
10.
11. // Tipo de entidad
12. type: ig.Entity.TYPE.B,
13.
14. init: function(x, y, settings) {
15. // Llama al constructor del elemento padre
16. this.parent(x, y, settings);
17. },
18.
19. // Se llama a este método para cada frame en cada entidad.
20. update: function() {
21. // Llama al método update() del padre para mover la entidad siguiendo sus normas de física
22. this.parent();
23. }
24. });
25. });
Este es el código fundamental de la mayoría de las entidades, en el que supongo que puedes reconocer mucho de lo que había en el archivo main.js que acabamos de ver antes: es un módulo.
Le ponemos nombre al módulo de la entidad al principio en ig.module, y le llamamos "game.entities.asteroid", y luego declaramos en la parte requires t que tiene que estar cargado antes el módulo "impact.entity". Ese modulo aporta toda la funcionalidad básica para crear entidades.
Dentro de la sección defines vamos a configurar la entidad finalizando el módulo de entidad de esta forma:
1. EntityAsteroid = ig.Entity.extend({
La primera parte es el nombre de la entidad y la segunda es simplemente para decirle que queremos extender la clase de entidad básica de Impact.
Dentro de la llamada a ig.Entity.extend ponemos todo el código que define la entidad asteroid, empezando por sus propiedades size y type. La propiedad size se refiere al tamaño de la entidad y no tiene por qué coincidir con el tamaño de la imagen (sprite) que vamos a utilizar para representarla en pantalla (eso se lo indicaremos al definir la entidad player). La propiedad type nos permite agrupar entidades dentro de un grupo A o grupo B. Por ejemplo, podemos utilizar esto para distinguir entidades peligrosas (tipo B) de entidades amistosas (tipo A). Se hace una llamada a la función init al crear una entidad y se hacen llamadas a update a cada ciclo del juego, antes de dibujar nada.
En este momento la entidad del asteroide no hace nada, así que vamos a hacerlo visible dentro del juego.
Añade este código debajo de donde aparece definida la propiedad type:
1. // Carga una página de animación
2. animSheet: new ig.AnimationSheet("media/asteroid.png", 64, 64),
Este código configure un sprite para el asteroide y nos permite darle animación si queremos. En este caso vamos a utilizar el sprite del asteroide que viene con la librería de recursos de juego que te indicaba al principio del tutorial, pero puedes utilizar cualquier otra imagen que prefieras.
El paso siguiente consiste en definir una animación para el sprite, que es bastante fácil en este caso ya que no queremos animación de ningún tipo para nuestro sprite. Añade este código justo debajo de this.parent en la función init:
1. // Añade animaciones de la hoja de animación
2. this.addAnim("idle", 1, [0]);
El primer argumento designa el nombre de la animación y puede ser el que quieras. El segundo es el tiempo que debe mantenerse visible cada escena (o frame) de la animación, en segundos. El tercero es un array de números de frames de la animación. En este caso solo hay un frame que es el cero (el primer elemento de un array es siempre el elemento cero).
Aún no podrás ver nada, así que volvamos al archivo main.js y añade el siguiente código dentro de la sección requires en la parte superior (recuerda poner una coma después del archivo anterior):
1. "game.entities.asteroid"
Y añade lo siguiente dentro de la función init:
1. var asteroidSettings;
2. for (var i = 0; i < 8; i++) {
3. asteroidSettings = {vel: {x: 100-Math.random()*200, y: 100-Math.random()*200}};
4. this.spawnEntity(EntityAsteroid, ig.system.width/2, ig.system.height/2, asteroidSettings);
5. };
Con esto vamos a crear hasta ocho asteroides dentro del juego en medio de la pantalla, cada uno con una velocidad calculada aleatoriamente, pero para que se puedan ver tenemos que añadir al juego la imagen del sprite del asteroide. Para ello tienes que mover el archivo asteroid.png desde la carpeta de recursos de juego y lo guardas en la carpeta media.
Si todo ha ido bien, ahora ya aparecerán en pantalla una serie de asteroides que se mueven desde el centro de la pantalla. ¡Ajá!
En el mismo archivo main.js, elimina todo el código que hay en la función draw excepto lo que corresponde a this.parent. Así eliminamos el mensaje de la pantalla, que ya no nos hace falta para nada.
Comprobación de límites
Ahora el problema es que los asteroides desaparecen fuera de los límites de la pantalla y no regresan. Esto no nos interesa. Volvamos al archivo de la entidad asteroids.js y le añadimos el código siguiente debajo de this.parent en la función update:
1. // Comprobación de límites
2. if (this.pos.x > ig.system.width) {
3. this.pos.x = -64;
4. } else if(this.pos.x < -64) {
5. this.pos.x = ig.system.width;
6. };
7.
8. if (this.pos.y > ig.system.height) {
9. this.pos.y = -64;
10. } else if (this.pos.y < -64) {
11. this.pos.y = ig.system.height;
12. };
Lo que hace esto es comprobar la posición del asteroide al finalizar cada ciclo de refresco y, si está fuera del área de juego, lo desplaza hasta el otro lado de la ventana. Así nos da el efecto de un universo curvo, donde las entidades no se pierden por los laterales.
Entidad jugador (player)
Ahora que ya tenemos creada la entidad básica del asteroide, es el momento de crear la entidad del jugador. Lo bueno es que gran parte de la lógica es la misma.Créate un archivo nuevo, llamado player.js y ponlo dentro de la carpeta lib/game/entities. Añádele este código:
1. ig.module(
2. "game.entities.player"
3. ).requires(
4. "impact.entity"
5. ).defines(function() {
6. // Subclase de ig.Enitity
7. EntityPlayer = ig.Entity.extend({
8. // Establece las dimensiones y distancias para la colisión
9. size: {x: 28, y: 50},
10. offset: {x: 18, y: 7},
11.
12. // Tipo de entidad
13. type: ig.Entity.TYPE.A,
14.
15. // Carga una hoja de animaciones
16. animSheet: new ig.AnimationSheet("media/player.png", 64, 64),
17.
18. init: function(x, y, settings) {
19. // Llama al constructor del padre
20. this.parent(x, y, settings);
21.
22. // Añade animaciones para la hoja de animación
23. this.addAnim("idle", 0.05, [0]);
24. this.addAnim("thrust", 0.05, [1,2]);
25. },
26.
27. // Se llama a este método en cada frame de cada entidad.
28. update: function() {
29.
30. // Llama al método update() del padre para mover la entidad siguiendo sus leyes de física
31. this.parent();
32.
33. // Comprobación de límites
34. if (this.pos.x > ig.system.width) {
35. this.pos.x = -64;
36. } else if(this.pos.x < -64) {
37. this.pos.x = ig.system.width;
38. };
39.
40. if (this.pos.y > ig.system.height) {
41. this.pos.y = -64;
42. } else if (this.pos.y < -64) {
43. this.pos.y = ig.system.height;
44. };
45. }
46. });
47. });
Nada extraño por aquí. Una diferencia es que la entidad tiene un nombre distinto en la sección ig.module y también en la sección defines. Otra diferencia es que la propiedad size ya no es de 64x64 pixels, sino 28x50, y que la hoja de animación va a cargar la imagen del jugador (que puedes encontrar en los recursos del juego). El tamaño mejor de la propiedad size representa el tamaño del cohete dentro de la imagen del sprite del jugador, que puedes ver en esta imagen:
La zona en azul es el área definida por la propiedad size y su posición viene definida por la propiedad offset. Esto es importante para asegurarnos de que se emplea la zona correcta del sprite a la hora de calcular las colisiones.
Por último, en esta ocasión se configuran dos animaciones, una cuando el jugador no se mueve (idle), y la otra cuando el jugador está en movimiento (thrust):
1. this.addAnim("idle", 1, [0]);
2. this.addAnim("thrust", 0.05, [1,2]);
En la animación thrust tenemos dos frames, y esto quiere decir que el sprite irá pasando del frame 1 al dos y luego volverá a mostrar el 1 y así mientras siga en movimiento. Cada frame se verá en pantalla durante 0,05 segundos. Lo vamos a ver ahora, pero el efecto es, básicamente, el del movimiento de la llama por la tobera del cohete.
El paso final consiste en añadir la entidad del jugador al archivo principal del juego, igual que hicimos antes con el asteroide. Abre el archivo main.js y añade la siguiente línea a la sección requires al principio (Y no olvides añadirle una coma detrás del nombre de archivo anterior):
1. "game.entities.player"
Después añade este código delante de la función init:
1. player: null,
Y añade este otro código después de donde pusiste antes el de los asteroides, en la función init:
1. // Load player entitiy
2. var playerSettings = {};
3. this.player = this.spawnEntity(EntityPlayer, 150, 150, playerSettings);
Todavía no vamos a ver nada, igual que pasaba antes con los asteroides: tenemos que pasar el archivo player.png desde la carpeta de recursos de juego que has descargado al principio del tutorial, a la carpeta media.
Actualiza la pantalla y ahora verás un pequeño cohete en la esquina superior izquierda. ¡Cómo mola!
Fondo de la escena
Aunque no se trata de una entidad en sí misma, es algo que también tenemos que poner en nuestro juego. En el mismo archivo main.js, añade el siguiente código por encima de la función init:1. background: new ig.Image("media/background.png"),
De esta forma ponemos una imagen de fondo, pero aún no se ve en pantalla. Para que se vea tenemos que sustituir la línea this.parent en la función draw por esta otra:
1. // Dibuja el fondo
2. this.background.draw(0, 0);
3.
4. // Dibuja las entidades
5. for(var i = 0; i < this.entities.length; i++) {
6. this.entities[i].draw();
7. };
Con esto sustituiros el método que se utiliza por defecto para dibujar las entidades del juego con este otro que nos permite algo más de control.
Como siempre, para que se vea tenemos que mover el archivo background.png desde la carpeta de recursos de juego a la carpeta media. El resultado será un hermoso cielo estrellado como fondo de escenario para nuestro juego.
Añadir controles de teclado
Aunque ya tenemos la entidad del jugador, todavía no la podemos mover, y como eso no nos viene nada bien, vamos a hacer que el cohete se mueva utilizando controles de teclado.Manejadores de eventos y listeners
El primer paso consiste en añadir listeners de eventos que van a detectar si pulsamos alguna tecla. Añade este código dentro de main.js y lo pones al principio de la función init:
1. ig.input.bind(ig.KEY.LEFT_ARROW, "left");
2. ig.input.bind(ig.KEY.RIGHT_ARROW, "right");
3. ig.input.bind(ig.KEY.UP_ARROW, "up");
4. ig.input.bind(ig.KEY.ENTER, "play");
Parece evidente cuáles son las teclas a las que se va a atender aquí: las flechas izquierda, derecha y arriba para mover el cohete, y la tecla Intro para reiniciar el juego cuando ha terminado. Los nombres que damos a cada uno de los listeners (left, right, up y play) se pueden cambiar y podemos usar el nombre que más nos guste. De todas formas, te recomiendo dejarlos así, los vamos a utilizar en un momento.
Mover el jugador
El sentido que tiene añadir controles de teclado es poder mover el jugador (el cohete) en la pantalla. Sorprendentemente, este manejo es sumamente sencillo y demuestra la potencia con que Impact nos resuelve este tipo de cosasAbre (si no lo tienes abierto aún) el archivo de entidad player.js en el editor y añádele este código debajo de la propiedad offset que aparece al principio:
1. // Ángulo en grados, para una rotación más suave
2. angle: 0,
3.
4. // Potencia, indica cuánto hay que acelerar
5. thrust: 0,
Y ahora añade este código encima de this.parent en la función update:
1. // "input.pressed" se le llama cada vez que se pulsa una tecla
2. // "input.state" se le llama en cada frame mientras permanence pulsada la tecla
3. if (ig.input.state("left")) {
4. this.angle -= 3;
5. };
6.
7. if (ig.input.state("right")) {
8. this.angle += 3;
9. };
10.
11. if (ig.input.state("up")) {
12. // Acelera el jugador enla dirección correcta
13. this.accel.x = Math.sin(this.angle*Math.PI/180)*this.thrust;
14. this.accel.y = -(Math.cos(this.angle*Math.PI/180)*this.thrust);
15. this.currentAnim = this.anims.thrust;
16. } else {
17. this.accel.x = 0;
18. this.accel.y = 0;
19. this.currentAnim = this.anims.idle;
20. };
21.
22. // Establedce el ángulo para la animación actual
23. this.currentAnim.angle = this.angle*(Math.PI/180);
Puede parecer mucha faena, pero sorprendentemente es bien poco lo que necesitamos añadir para tener control total sobre los movimientos del jugador. Veamos cómo va esto.
La primera sentencia condicional que nos encontramos es ig.input.state("left"), que significa que está evaluando el estado actual del evento de teclado que hemos llamado left en la sección anterior (por eso es importante no cambiar los nombres). Si resulta que esta tecla está pulsada, el método ig.input.state nos devuelve el valor true, o false en caso contrario.
En el caso de las teclas de fleche izquierda y derecha, lo que hace es aumentar o reducir el valor de la propiedad angle (ángulo con respecto a la vertical) del jugador, dependiendo de la tecla pulsada. Después, en el caso de la fecha arriba, mantiene el valor de ángulo y, junto con la propiedad thrust, calcula la aceleración del cohete desplazándolo en la dirección marcada por el ángulo. Este cálculo se basa en un cálculo de trigonometría muy habitual, pero puedes leerte este artículo si quieres saber en detalle cómo se hace.
Aparte, con la tecla de la fleche arriba empieza a funcionar la animación del cohete, que cambia hacia la animación thrust si la tecla se mantiene pulsada (y por tanto, el cohete se mueve), volviendo a la animación idle cuando soltamos la tecla (el cohete se queda quieto).
Finalmente, utilizamos la propiedad angle del jugador para establecer el ángulo de rotación de la imagen del sprite. Esta línea es importante puesto que la imagen no cambia su ángulo de ninguna otra forma. La fórmula extraña al final es precisamente la que transforma el ángulo en grados a radianes, que es lo que realmente utiliza Javascript.
Si todo ha ido bien, tendremos que ver ahora cómo el cohete da vueltas al pulsar las flechas derecha e izquierda, y cómo le sale fuego por la tobera al pulsar la flecha arriba.
Ya, pero ¿por qué no se mueve el cohete hacia adelante? Es porque aún no hemos definido la propiedad thrust con un valor distinto de cero. Vamos otra vez al archivo main.js y cambiamos la línea playerSettings por esta otra:
1. var playerSettings = {thrust: 250, maxVel: {x: 300, y: 300}};
La propiedad maxVel nos sirve para limitar la velocidad de desplazamiento de la entidad, algo necesario si queremos evitar que se acelere hasta alcanzar la velocidad de la luz.
Y ahora vamos a echarlo a correr en el navegador. Ahora podemos hacer volar el cohete por la pantalla con unos efectos de física bastante realistas, ¡verdaderamente bonito!
Rematando el juego
Bueno, ya tenemos un juego que funciona, pero aún no tenemos un sistema para terminarlo, lo que lo hace algo aburrido. La buena noticia es que añadirle la lógica de fin de juego es verdaderamente fácil con Impact.Abre el archive de entidad player.js (será la última vez, lo prometo), y pon el código siguiente detrás de la propiedad type en la parte superior:
1. checkAgainst: ig.Entity.TYPE.B,
Parece bastante razonable. Está indicando que compruebe si la entidad player (que es del tipo A) coincide con objetos de tipo B (los asteroides). Para poder utilizar esto necesitamos poner una función más, así que añade este código encima de la función update:
1. check: function(other) {
2. // Game over
3. this.health = 0;
4. },
La función check recibe una llamada siempre que la entidad actual (el jugador) se superpone a cualquier otra del mismo tipo que la declarada en la propiedad checkAgainst. Si ambas entidades se superponen, es el momento en que el jugador muere y en ese caso, la propiedad health cambia su valor a cero. Realmente con esto no se para el juego, pero se puede añadir esta parte después.
Abre por última vez el archive main.js y añade este código debajo de la propiedad background al principio:
1. gameOver: false,
Esta propiedad nos sirve para que el juego sepa cuándo ha terminado. Añade ahora el siguiente código por encima de this.parent en la función update:
1. // Se ejecuta si el juego ha terminado
2. if (this.gameOver) {
3. if(ig.input.pressed("play") ) {
4. ig.system.setGame(MyGame);
5. };
6.
7. // Vuelve para parar cualquier cosa que esté actualizándose
8. return;
9. };
Con esto el juego cesa de actualizarse cuando termina y se reinicializa utilizando el método ig.system.setGame cuando se pulsa la tecla Intro. ¡Solo hacen falta unas cuantas líneas de código para conseguir todo esto!
Para cambiar el valor de la propiedad gameOver nos hará falta poner el código siguiente debajo de this.parent en la función update:
1. // Comprueba la condición de finalización del juego
2. if (this.player.health == 0) {
3. this.gameOver = true;
4. };
Si el jugador ha muerto, el juego se pasa al final en la siguiente iteración. Sencillo.
El paso final en el juego (que está prácticamente hecho ya) consiste en mostrar un mensaje que diga Game Over o lo que prefieras, para indicar que ha terminado. Añade este código debajo de this.background.draw en la función draw:
1. // Pantalla de finalización
2. if (this.gameOver) {
3. this.font.draw("Game Over!", ig.system.width/2, 132, ig.Font.ALIGN.CENTER);
4. this.font.draw("Press Enter", ig.system.width/2, 232, ig.Font.ALIGN.CENTER);
5. this.font.draw("to Restart", ig.system.width/2, 272, ig.Font.ALIGN.CENTER);
6.
7. // Vuelve para dejar de mostrar en pantalla el resto de entidades
8. return;
9. };
Utiliza el mismo código que se incluía dentro del código de Impact al principio, para mostrar el mensajito de demo, excepto que esta vez lo estamos utilizando para mostrar el mensaje en tres líneas. De todas formas, si lo ejecutas en tu navegador, verás que el tipo de letra es minúsculo.
Para hacerlo más grande tenemos que sustituir el archivo 04b03.font.png en la carpeta media con el que viene en los recursos de juego del tutorial, o crearnos nuestro propio archive de imágenes de fuentes de letra utilizando la herramienta Impact Font Tool. El resultado será un letrero mucho más grande y legible.
Resumen
El juego que hemos creado es sencillo, pero muestra la funcionalidad básica que nos proporciona el motor Impact tal y como se distribuye. Mi sugerencia es que mejores el juego añadiéndole audio, disparos para destruir asteroides, imágenes de más calidad para los pedruscos y quizás controles basados en la interfaz táctil para dispositivos móviles.Bueno, hemos llegado al final de este tutorial sobre el motor de juegos Impact. Espero que lo hayas encontrado tan interesante y atractivo como lo vi yo cuando empecé a utilizarlo la primera vez. En mi opinión, Impact es un motor de juegos verdaderamente potente, y sin duda debes tenerlo en mente cuando quieras desarrollar juegos para HTML5 en el futuro.
Si quieres ver el juego en acción, aquí tienes una demo en vivo o también puedes descargarte el código fuente y jugar en tu propio equipo. Pero ten en cuenta que para ejecutar la demo tienes que hacerte con una copia con licencia del motor de juegos Impact para HTML5.
Buena suerte!
Rob Hawkes
Evangelista técnico de Mozilla