Introducción al Desarrollo de Juegos en HTML5 con Phaser y la API de Orientación a Dispositivos

Esta traducción está incompleta. Por favor, ayuda a traducir este artículo del inglés.

Borrador
Esta página no está completa.

Introducción

En este tutorial vamos a ir paso a paso a través del proceso de construcción de un juego en HTML5 para móviles que utilizará el API de Orientación para Dispositivos en el modo de juego desde su núcleo y estará construido utilizando el framework Phaser. Se requieren conocimientos básicos de JavaScript para aprender el siguiente artículo. Iremos a través de este tutorial y al final de cada etapa encontrarás el código fuente del trabajo realizado para poder jugar. Al final del tutorial tendremos una demo completamente funcional del juego.

Configuración del proyecto

Puedes ver el código de ejemplo de este proyecto en GitHub. La estructura de carpetas no es nada complicada: el punto de partida es el archivo index.html que inicializa el framework, configura el canvas y permite jugar con el juego. Puedes hacer clic en el archivo desde tu navegador favorito para iniciar el juego y probarlo. También hay dos carpetas: img y srcLa primera de ellas contiene todas las imágenes utilizadas mientras que la segunda carpeta almacenará los archivos de JavaScript con todo el código fuente del juego definido dentro.

Vamos a comenzar con el código reutilizable escrito específicamente en el framework Phaser, con este podemos enfocarnos en la construcción del juego en sí, pero acompañare con una breve explicación los puntos claves del código proporcionado.

Introducción a Phaser

Phaser es un framework que nos permite construir juegos en HTML5 para equipos de escritorio y dispositivos móviles. Es bastante nuevo, pero tiene involucrada una apasionada comunidad en el proceso de desarrollo, por lo que crece rápidamente. Puedes comprobarlo en GitHub donde encontrarás el código abierto, podrás leer la documentación básica o ir a través de la gran colección de ejemplos. El framework Phaser proporciona un conjunto de herramientas que aceleran el desarrollo y ayuda a manejar tareas genéricas necesarias para completar el juego.

Configuración del canvas

Vamos a renderizar nuestro juego sobre el elemento <canvas>, por lo que vamos a configurarlo: nuestro punto de partida es el archivo index.html con el siguiente contenido:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Cyber Orb</title>
    <style> body { margin: 0; background: #333; } </style>
    <script src="src/phaser.min.js"></script>
    <script src="src/Boot.js"></script>
    <script src="src/Preloader.js"></script>
    <script src="src/MainMenu.js"></script>
    <script src="src/Game.js"></script>
</head>
<body>
<script>
(function() {
    var game = new Phaser.Game(320, 480, Phaser.CANVAS, 'game');
    game.state.add('Boot', Ball.Boot);
    game.state.add('Preloader', Ball.Preloader);
    game.state.add('MainMenu', Ball.MainMenu);
    game.state.add('Game', Ball.Game);
    game.state.start('Boot');
})();
</script>
</body>
</html>

Esto se parece a una simple página web HTML con el contenido básico en la sección <head>: configuración de caracteres, título, estilo y las inclusión de los archivos JavaScripts. El <body> contiene la inicialización del framework Phaser y las definiciones del estado del juego. Nosotros no tenemos que configurar por nuestra cuenta el elemento <canvas>, porque este será generado por el framework.

Gestión de estados

Los estados en Phaser son partes separadas de la lógica del juego, en nuestro caso los estamos cargando de archivos JavaScript independientes para un mejor mantenimiento. En este juego tenemos estados básicos: Boot, Preloader, MainMenu y Game. Boot se hará cargo de la inicialización de algunas opciones de configuración, Preloader cargará a todos los gráficos utilizados, MainMenu es la pantalla que ves después de cargar el juego y permite hacer clic en el botón de inicio para ir al estado del Game y de comenzar a jugar el juego.

Puedes comprobar ahora el código reutilizable y consultar cómo funciona. En este tutorial vamos a modificar el estado del juego desde el archivo Game.js donde está toda la diversión. Toda la inicialización será destinada a la función create() (lanzada una vez al comienzo del juego), algunas cosas requerirán ser agregadas a la función update() (ejecutadas en cada frame), o incluso escribiremos nuestras propias funciones para manejar tareas más complicadas.

Agregando la pelota y el mecanismo de movimiento

Primero vamos a ir a la función create, inicializamos el objeto ball y asignamos unas cuantas propiedades:

ball = this.add.sprite((320-22)/2, 450, 'ball');
ball.anchor.setTo(0.5, 0.5);
ball.body.bounce.setTo(0.3, 0.3);
ball.body.setCircle(10, 11, 11);
ball.body.linearDamping = 1;

Vamos a agregar un sprite en un lugar de la pantalla y a utilizar la imagen "ball". También estamos configurando el anchor que realizará los cálculos de física para el centro de la pelota, configurando el valor de bounce 'rebote' y el círculo para la detección de colisiones. linearDamping se utiliza para aplicar fricción y detener siempre el rebote de la pelota.

Controlando la pelota

Es genial tener lista la pelota para lanzarla en torno a la zona del juego, pero también es importante poder hacerlo. Ahora vamos a añadir la capacidad de controlar con el teclado la pelota en los dispositivos de escritorio, y luego pasaremos a la implementación de la API de Orientación de Dispositivo. Ahora, vamos a enfocarnos en el teclado:

keys = this.game.input.keyboard.createCursorKeys();

Como puedes ver hay una función especial Phaser que nos dará un objeto con las cuatro teclas de flecha para jugar con: arriba, abajo, izquierda y derecha. Los objetos de las teclas se comparan con las entradas que realiza el jugador desde el teclado, por lo que la pelota puede reaccionar en consecuencia: 

var force = 10;
if(keys.left.isDown) {
    ball.body.velocity.x -= force;
}
else if(keys.right.isDown) {
    ball.body.velocity.x += force;
}
if(keys.up.isDown) {
    ball.body.velocity.y -= force;
}
else if(keys.down.isDown) {
    ball.body.velocity.y += force;
}

El código anterior será añadido a la función update, con lo que será lanzado en cada fotograma. De esta manera podemos comprobar qué tecla es presionada en el momento dado y así aplicar la fuerza definida a la pelota, por lo tanto incrementamos la velocidad en la dirección correcta.

La implementación de la API de Orientación del Dispositivo

La parte interesante del juego es que utiliza la API de Orientación para Dispositivos móviles. Gracias a esto se puede jugar el juego con la inclinación del dispositivo en dirección que quieres que la pelota ruede. Aquí está el código responsable de esto:

window.addEventListener("deviceorientation", this.handleOrientation, true);

Vamos a añadir un detector de eventos para el evento "deviceorientation" y vincularlo a la función handleOrientation que se ve como esto:

handleOrientation: function(e) {
    var x = e.gamma; // range [-90,90]
    var y = e.beta;  // range [-180,180]
    ball.body.velocity.x -= x*2;
    ball.body.velocity.y -= y*4;
}

Mientras más se inclina el dispositivo, más fuerza se aplica a la pelota y la velocidad es mayor.

Agregando el agujero

El principal objetivo del juego es mover la pelota desde la posición inicial a la posición final que en nuestro caso será un agujero (hole) en el suelo. Esta implementación es muy similar a la parte anterior en donde creamos la pelota:

hole = this.add.sprite((320-22)/2, 90, 'hole');
hole.body.immovable = true;
hole.anchor.setTo(0.5, 0.5);
hole.body.setCircle(5,15,15);

La diferencia está en que el cuerpo del agujero se configura como inamovible por lo que no se moverá cuando acertamos con la pelota y tiene calculada la detección de colision (esto se tratará más adelante en este artículo).

Construyendo los bloques del laberinto

Para la construcción del laberinto y hacerle la vida más dificil (al jugar) al jugador podríamos usar un editor de niveles, pero para el propósito de este artículo vamos a crear de forma manual desde cero un nivel.

walls = this.game.add.group();
walls.add(panel);

En primer lugar vamos a comenzar con la creación del grupo al cual le añadiremos el panel superior, por lo que la pelota puede rebotar fuera de ella.

walls.create(220-32, 480-128, 'element-h').body.immovable = true;
walls.create(92, 480-128-32, 'element-w').body.immovable = true;
walls.create(0, 240, 'element-w').body.immovable = true;
walls.create(128, 240, 'element-w').body.immovable = true;
walls.create(256, 240, 'element-h').body.immovable = true;
walls.create(180, 58, 'element-h').body.immovable = true;
walls.create(52, 154, 'element-w').body.immovable = true;

Entonces las paredes son creadas configurando las posiciones superior e izquierda, pasando el nombre de la imagen y definiendo el atributo immovable en true, con esto el muro no se puede mover cuando es golpeado por una pelota. Por supuesto, puedes hacerlo a tu manera o leer acerca de la aplicación de los datos de un editor de niveles externo, que está soportado en Phaser.

Detección de colisiones

Tenemos la pelota que puede ser controlada por el jugador, el agujero que se tiene que alcanzar y los obstáculos que bloquean el camino y la pelota que tiene que ser guiada a través de ellos. Sin embargo, hay un problema – nuestro juego no tiene detección de colisiones, así que no sucede nada cuando la pelota golpea los bloques, sólo los atraviesa. Ese no es el efecto que queríamos conseguir, así que vamos a trabajar en él. La buena noticia es que el framwork cuida sobre todo la detección de colisiones, entonces sólo debemos que especificar que es lo que chocará con que:

this.game.physics.collide(ball, walls, this.wallCollision, null, this);

Esto le dirá al framework que ejecute la función wallCollision cuando la pelota golpee cualquiera de las paredes. La función wallCollision esta vacia, pero podemos usarla para añadir cualquier funcionalidad que querramos, como por ejemplo agregar el sonido de rebote.

También debemos devolver al objeto pelota y limitarlo a moverse sólo en la zona visible, para que no salga de la pantalla. Hay una función muy útil en Phaser que se llama collideWorldBounds:

ball.body.collideWorldBounds = true;

Hace exactamente lo que necesitamos - ahora la pelota rebotará en los bordes de la pantalla como de las paredes.

Agregando el tiempo transcurrido

Para mejorar la jugabilidad y dar a los jugadores la opción de competir con los demás podemos introducir el tiempo transcurrido. Gracias a esto, el jugador puede jugar los niveles dados una y otra vez tratando de mejorar su puntuación. Para implementar esto en el juego tenemos que crear una variable para almacenar el número actual de segundos transcurrido desde el inicio del juego y mostraselo al jugador en el juego. Vamos a definir la primer variable :

timer = 0;

Entonces podemos inicializar el objeto de texto:

timerText = this.game.add.text(15, 20, "Time: "+timer, { font: "24px Arial", fill: "#333333" });

Estamos definiendo la posición superior e izquierda del texto, el contenido que se muestra y también podemos aplicar un estilo CSS básico al texto, para que se vea mejor. Lo hemos impreso en pantalla, pero sería bueno actualizar su valor a cada segundo:

loop = this.game.time.events.loop(Phaser.Timer.SECOND, this.updateCounter, this);

Este bucle ejecutará la función updateCounter a cada segundo desde el comienzo del juego, por lo que podemos aplicar los cambios en consecuencia. Este es el núcleo de la función updateCounter:

updateCounter: function() {
    timer++;
    timerText.content = "Time: "+timer;
},

Como puedes ver estamos incrementando la variable timer del temporizador y actualizando el contenido del objeto de texto con el valor actual, por lo que el jugador verá el tiempo transcurrido.

Terminando el nivel

La bola está rodando en la pantalla, el tiempo se está acabando, tenemos el agujero al que tenemos que llegar, así que vamos a configurar la posibilidad de finalizar el juego. Vamos a cubrir algunos aspectos de la situación cuando la pelota llega al agujero.

this.game.physics.collide(ball, hole, this.finishLevel, null, this);

Que activará la función finishLevel tan pronto como la pelota alcance el agujero:

alert('HOLE!');
timer = 0;
level++;
this.game.state.start('Game');

Por ahora el popup alert salta con información breve de que se alcanzó el agujero. Tenemos que restablecer el temporizador, incrementar el contador del nivel y reiniciar el estado del juego con los ajustes actuales guardados en las variables.

Ideas para nuevas características

Esto no es más que una demostración de trabajo de un juego que podría tener un montón de características diferentes. Por ejemplo podemos añadir poderes para recoger en el camino que harán que nuestra pelota ruede más rápido, otro podría detener el temporizador durante unos segundos o dar la pelota poderes especiales. También hay espacio para los obstáculos y trampas que harán más lenta la pelota, acelerar el tiempo o trampas de la propia pelota. Puedes crear más niveles con dificultades diferentes para cada uno. Incluso puedes obtener logros, tablas de clasificación y medallas para diferentes acciones en el juego. Hay un sinfín de posibilidades - que sólo dependen de tu imaginación.

Resumen

Espero que el tutorial anterior haya sido fácil de aprender y puedes bucear en Phaser, leer la documentación, hackear los ejemplos y crear juegos impresionantes por tu cuenta. Puedes jugar la demo del juego Cyber ​​Orb o revisar el código fuente en GitHub. La tecnología nos da las herramientas, los frameworks son cada vez más rápido y mejor, por lo que sólo tiene que comenzar tu propio viaje y ver si el desarrollo HTML5 de juegos es algo con lo que deseas pasar tu tiempo.

Etiquetas y colaboradores del documento

 Colaboradores en esta página: chebit
 Última actualización por: chebit,