Bienvenido a nuestro primer tutorial sobre Crear un Juego con Phaser. Aqu<71> aprenderemos c<>mo crear un juego peque<75>o con un jugador corriendo y saltando por unas plataformas y recogiendo estrellas. Durante el proceso, explicaremos algunas de las caracter<65>sticas internas del framework.
<ahref="http://phaser.io">Phaser</a> es un framework de juegos HTML5 cuyo prop<6F>sito es ayudar a los desarrolladores a hacer juegos HTML5 potentes y portables. El <20>nico requisito del navegador es que soporte la etiqueta canvas. Adem<65>s, toma muchas cosas prestadas de Flixel.
<li>Adem<EFBFBD>s aseg<65>rate de que sigues la <ahref="http://phaser.io/getting-started-js.php">Gu<EFBFBD>a de Iniciaci<63>n</a>, te mostrar<61> c<>mo descargar el framework, configurar un entorno de desarrollo, y un poco por encima la estructura de un proyecto de Phaser y sus funciones b<>sicas.</li>
Si ya has finalizado la Gu<47>a de Iniciaci<63>n tendr<64>s Phaser bajado y todo listo para programar. Descarga los recursos de este tutorial aqu<71> y descompr<70>melos en un directorio de tu servidor web.
Abre la p<>gina part1.html en tu editor favorito y ech<63>mosle un vistazo al c<>digo. Despu<70>s de un poco de texto est<73>ndar HTML que incluye Phaser, la estructura del c<>digo tiene esta pinta:
En la l<>nea 1 es donde le das vida a Phaser creando una instancia del objeto Phaser.Game y asign<67>ndola a una variable local llamada 'game'. Llamarla 'game' es una pr<70>ctica com<6F>n pero no obligatorio, pero es lo que encontrar<61>s en los ejemplos de Phaser.
Los dos primeros par<61>metros son el ancho y el alto del elemento canvas que Phaser va a crear. En este caso 800 x 600 pixels. El mundo de tu juego puede ser del tama<6D>o que quieras, esto es simplemente la resoluci<63>n a la que se va a mostrar la pantalla del juego. El tercer par<61>metro puede ser Phaser.CANVAS, Phaser.WEBGL, o Phaser.AUTO. Esto es el contexto de renderizado que quieres usar. El par<61>metro recomendable es Phaser.AUTO que autom<6F>ticamente intenta usar WEBGL, pero si el navegador o el dispositivo no lo soporta se usar<61> Canvas.
El cuarto par<61>metro es una cadena de texto vac<61>a, que es el id del elemento DOM en el que te gustar<61>a insertar el elemento canvas que Phaser crea para el juego. Como lo dejamos en blanco simplemente ser<65> a<>adida al final del body de la p<>gina. El par<61>metro final es un objeto conteniendo cuatro referencias a las funciones esenciales de Phaser. Su uso est<73><ahref="http://www.html5gamedevs.com/topic/1372-phaser-function-order-reserved-names-and-special-uses/">ampliamente explicado aqu<71></a>. Ten en cuenta que este objeto no es obligatorio - Phaser soporta un Sistema de Estados completo que te permite dividir tu c<>digo en objetos individuales de una manera m<>s limpia. Pero para una gu<67>a de iniciaci<63>n como esta usaremos este m<>todo pues permite un prototipado m<>s r<>pido.
Vamos a cargar los recursos que necesitamos para nuestro juego. Haces esto a<>adiendo llamadas game.load() dentro de una funci<63>n llamada preload. Phaser autom<6F>ticamente buscar<61> esta funci<63>n cuando arranca y cargar<61> todo lo que en ella se indique.
Esto cargar<61> 4 recursos: 3 im<69>genes y un sprite sheet. Puede que para algunos les resulte obvio, pero me gustar<61>a se<73>alar el primer par<61>metro, la identificador del recurso. Esta cadena de texto es un enlace al recurso cargado y es lo que usar<61>s en tu c<>digo cuando crees sprites. Eres libre de usar cualquier cadena de texto de JavaScript v<>lida como identificador.
El orden en el cual se dibujan los sprites en pantalla coincide con el orden en el que los creas. As<41> que si quieres colocar un fondo detr<74>s del sprite de la estrella deber<65>as asegurarte de que lo a<>ades antes de ella.
<h3>Construcci<EFBFBD>n del Mundo</h3>
Internamente game.add.sprite est<73> creando un objeto <ahref="http://gametest.mobi/phaser/docs/Phaser.Sprite.html">Phaser.Sprite</a> nuevo y a<>adi<64>ndo el sprite al mundo del juego. Este mundo es donde todos tus objetos viven, podr<64>a compararse al Stage de ActionScript3
<strong>Nota:</strong> El mundo del juego no tiene tama<6D>o fijo y se extiende infinitamente en todas direcciones, siendo 0,0 su centro. Por comodidad Phaser te coloca 0,0 en la esquina superior izquierda del juego, pero puedes moverte alrededor usando la clase Camera que incorpora Phaser.
Puedes acceder al objeto mundo mediante game.world, que viene con mont<6E>n de m<>todos y propiedades <20>tiles para ayudarte a distribuir tus objetos dentro del mundo. Incluye algunas propiedades simples como game.world.height, pero tambi<62>n algunas m<>s avanzadas que usaremos en otro tutorial.
La primera parte es lo mismo que hicimos antes con el sprite de la estrella, solo que en su lugar cambiamos el identificador a 'sky' ('cielo') y nos ha mostrado nuestro fondo de cielo, que es un PNG de 800x600 que llena la pantalla del juego.
Los grupos son muy potentes. Como su nombre implica te permiten agrupar objetos similares juntos y controlarlos como si fueran una unidad individual. Tambi<62>n puedes comprobar colisiones entre Grupos, y para este juego usaremos dos grupos diferentes, uno de los cuales lo creamos en el c<>digo anterior para las plataformas:
Como con los sprites, game.add crea nuestro objeto Group. Se lo asignamos a una nueva variable local llamada platforms. Ahora que est<73> creado podemos a<>adir objetos en <20>l. Primero el suelo, que se posiciona en la parte inferior del juego y usa la image 'ground' que hemos cargado antes. El suelo se escala para cubrir todo el ancho del juego. Finalmente ponemos a true su propiedad 'immovable'. Si no hubi<62>semos hecho esto el suelo se mover<65>a cuando el jugador choque con <20>l (m<>s sobre esto en la secci<63>n 'F<>sicas').
Esto crea un nuevo sprite llamdo 'player', colocado a 32 pixels del borde izquierdo y 150px del borde inferior del juego. Le diremos que use el recurso 'dude' anteriormente cargado. Si echas un ojo a la funci<63>n preload ver<65>s que 'dude' fue cargado como un sprite sheet, no una imagen. Esto es as<61> porque contiene frames de animaci<63>n. El sprite sheet completo es as<61>:
Puedes ver 9 frames en total, 4 para correr a la izquierda, 1 para mirar a c<>mara y 4 para correr a la derecha. (Nota: Phaser puede invertir los sprites para ahorrar frames de animaci<63>n, pero para este tutorial lo haremos a la vieja usanza). Definimos dos animaciones llamadas 'left' ('izquierda') y 'right' ('derecha'). La animaci<63>n 'left' usa los frames 0, 1, 2 y 3 y se reproduce a 10 frames por segundo. El par<61>metro 'true' hace que la animaci<63>n se repita. Este es nuestro ciclo de correr est<73>ndar y lo mismo para la direcci<63>n opuesta. Con las animaciones configuradas creamos un par de propiedades f<>sicas.
<h3>El Cuerpo y la Velocidad: Un Mundo de F<>sicas</h3>
En Phaser todos los sprites tienen una propiedad body, que es una instancia de <ahref="http://gametest.mobi/phaser/docs/Phaser.Physics.Arcade.Body.html">ArcadePhysics.Body</a>. Esto interpreta el sprite como un cuerpo f<>sico en el motor de f<>sicas integrado en Phaser. El objeto body tiene a su vez un mont<6E>n de propiedades con el que podemos jugar. Para simular los efectos de la gravedad en un sprite, es tan simple como escribir esto:
Este es un valor arbitrario, pero l<>gicamente, cuanto m<>s alto sea el valor, m<>s pesado ser<65> tu objeto y m<>s r<>pido caer<65>. Si a<>ades esto a tu c<>digo o abres part5.html ver<65>s que el jugador cae sin parar, ignorando completamente el suelo que creamos antes:
La raz<61>n de esto es que no estamos testeando las colisiones entre el suelo y el jugador. Ya le dijimos a Phaser que nuestro suelo y plataformas deber<65>an ser inm<6E>viles ('immovable'). Si no lo hubi<62>semos hecho entonces el jugador colisionar<61>a con ellos, se parar<61>a un momento y luego todo se colapsar<61>a. Esto es porque a menos que se lo digamos, el sprite del suelo es un objeto f<>sico movible (tambi<62>n conocido como cuerpo din<69>mico, o 'dynamic body') y cuando el jugador choca con <20>l, la fuerza resultante de la colisi<73>n se aplica al suelo, los dos cuerpos intercambian sus velocidades y el suelo comienza a caer tambi<62>n.
As<EFBFBD> que para hacer que el jugador colisione y aproveche las propiedades f<>sicas necesitamos introducir una comprobaci<63>n de las colisiones en la funci<63>n update:
La funci<63>n update se llama desde el bucle interno del juego con cada frame. La funci<63>n <ahref="http://gametest.mobi/phaser/docs/Phaser.Physics.Arcade.html#toc22">Physics.collide</a> es la que hace la magia. Recibe dos objetos, checkea sus colisiones y los separa en caso necesario. En este caso estamos pas<61>ndole el sprite del jugador y el grupo de las plataformas. La funci<63>n es suficientemente inteligente como para comprobar la colisi<73>n en todos los miembros del grupo, as<61> que esta llamada comprobar<61> la colisi<73>n del jugador con el suelo y las plataformas. El resultado es una plataforma firme:
Las colisiones van bien, pero realmente necesitamos que el jugador se mueva. Probablemente pensar<61>as en ir a la documentaci<63>n y buscar c<>mo a<>adir un detector de eventos ('event listener'), pero no es necesario. Phaser tiene un gestor de teclado integrado y uno de los beneficios de usarlo es esta peque<75>a y <20>til funci<63>n:
Esto rellena el objeto cursors con cuatro propiedades: up, down, left, right ('arriba', 'abajo', 'izda', y 'derecha', resp.), que son todas instancias de objetos <ahref="http://gametest.mobi/phaser/docs/Phaser.Key.html">Phaser.Key</a>. Despu<70>s de esto todo lo que tenemos que hacer es consultarlas en nuestro bucle update:
<preclass="lang:js decode:true "> // Resetear la velocidad del jugador (movimiento)
Aunque hemos a<>adido un mont<6E>n de c<>digo, deber<65>a ser bastante legible. Lo primero que hacemos es resetear la velocidad horizontal del sprite. Luego comprobamos si el cursor izquierdo est<73> presionado. Si es as<61> le aplicamos una velocidad negativa horizontal (-150) e iniciamos la animaci<63>n de correr 'left'. Si en cambio est<73> presionado el cursor derecho hacemos exactamente lo opuesto. Inicializando la velocidad a 0 y haciendo todo esto, cada frame, conseguimos un estilo de movimiento del tipo 'parar-andar'.
El sprite del jugador se mover<65> solamente cuando una tecla est<73> presionada y parar<61> inmediatamente cuando no. Phaser te permite tambi<62>n crear movimientos m<>s complejos, con momentum y aceleraci<63>n, pero de momento ya tenemos el efecto que necesitamos para este juego. La parte final de la comprobaci<63>n de teclas cambia al frame 4 si no hay ninguna tecla presionada. El frame 4 en el sprite sheet es el del jugador mirando hacia t<>, quieto.
La parte final del c<>digo a<>ade la habilidad de saltar. El cursor arriba es nuestra tecla de salto y testeamos si est<73> presionada. Sin embargo, tambi<62>n tenemos que testear si el jugador est<73> tocando el suelo, sino podr<64>a saltar mientras est<73> en el aire. Si ambas condiciones se cumplen aplicamos una velocidad vertical de 350px por segundo. El jugador caer<65> autom<6F>ticamente al suelo por la gravedad que tiene aplicada. Con los controles listos ahora s<> que tenemos un mundo que podemos explorar. Carga part7.html y juega un poco. Intenta ajustar valores (como el 350 del salto) para ver el efecto que tienen.
Es hora de darle a nuestro jueguillo un prop<6F>sito. Salpiquemos la escena con estrellas y dejemos que el jugador pueda recolectarlas. Para conseguirlo crearemos un nuevo grupo llamado 'stars' y lo llenaremos. En nuestra funci<63>n create a<>adiremos el siguiente c<>digo (puedes verlo en part8.html):
<preclass="lang:js decode:true"> // A<>adimos un grupo para meter las estrellas
El proceso es similar a cuando creamos el grupo de las plataformas. Usando un bucle 'for' de JavaScript creamos 12 estrellas. Tienen una coordenada x de i * 70, que significa que estar<61>n uniformemente separadas en la escena de 70 en 70 pixels. Como con el jugador les damos un valor de gravedad para que caigan, y un valor de rebote ('bounce') para que reboten un poco cuando chocan con las plataformas. Bounce es un valor entre 0 (sin rebote) y 1 (rebote completo). Nuestras estrellas rebotar<61>n un valor aleatorio entre 0.7 y 0.9. Si ejecut<75>semos el c<>digo tal cual est<73> ahora las estrellas caer<65>an al borde inferior del juego. Para pararlas necesitamos que se comprueben sus colisiones contra las plataformas en nuestro bucle update:
Esto le dice a Phaser que compruebe si el jugador se solapa con cualquier estrella del grupo de estrellas. Si es as<61> se llama a la funci<63>n 'collectStar' con ambos como par<61>metros:
De manera f<>cil la estrella se mata ('kill'), lo que la elimina de la pantalla. Si ejecutas el juego ahora tenemos un jugador que puede andar, saltar, rebotar por las plataformas y recoger estrellas que caen desde arriba. No est<73> mal para unas pocas l<>neas de c<>digo -espero- bastante legible :)
El <20>ltimo retoque que haremos ser<65> a<>adir un marcador. Para ello haremos uso de un objeto <ahref="http://gametest.mobi/phaser/docs/Phaser.Text.html">Phaser.Text</a>. Aqu<71> creamos dos variables nuevas, una para guardar el marcador real y otra para el propio objeto de texto:
16,16 son las coordenadas en las que mostrar el texto. 'score: 0' es el texto a mostrar por defecto y el objeto siguiente contiene el tama<6D>o de fuente y el color de relleno. Si no especificamos la fuente a utilizar usaremos la que use el navegador por defecto, as<61> que en Windows ser<65> Arial. A continuaci<63>n necesitamos modificar la funci<63>n collectStar para que cuando el jugador coja una estrella su puntuaci<63>n se incremente y el texto se actualice para reflejarlo:
De esta manera se a<>aden 10 puntos por cada estrella y scoreText se actualiza para mostrar el nuevo total. Si abres part9.html ver<65>s el juego final.
Has aprendido c<>mo crear un sprite con propiedades f<>sicas, para controlar su movimiento y hacer que interaccione con otros objetos, en un peque<75>o juego. Hay muchas m<>s cosas que puedes hacer para mejorarlo: por ejemplo, no hay sensaci<63>n de final o de riesgo. <20>Por qu<71> no a<>adir algunos pinchos que debes evitar? Podr<64>as crear un nuevo grupo 'pinchos' y comprobar su colisi<73>n contra el jugador, y en lugar de eliminar el sprite del pincho eliminar<61>as al jugador. O para un estilo de juego no violento podr<64>as hacer un juego de correr y desafiar al jugador a recolectar las estrellas tan r<>pido como pueda. Hemos inclu<6C>do unos cuantos gr<67>ficos extra en el archivo zip para que te ayuden a inspirarte.
Con la ayuda de lo que has aprendido en este tutorial y los <ahref="http://www.gametest.mobi/phaser/examples">m<EFBFBD>s de 160 ejemplos</a> que tienes disponibles, deber<65>as tener una base s<>lida para un proyecto futuro. Pero como siempre si tienes preguntas, necesitas consejo o quieres compartir lo que has estado haciendo, no dudes en pedir ayuda y contactar en el <ahref="http://www.html5gamedevs.com/">foro de Phaser</a>.