viernes, 18 de julio de 2014

Como hacer un juego de Naves con Phaser. "El Heroe"

space invaders en html5

Voy a comenzar esta serie explicando todo lo relacionado con nuestro heroe, despues de terminar este tutorial la nave va estar en capacidad de moverse y de disparar.

 Vamos a necesitar dos objetos nuestra nave y la municion tambien vamos a agregar un fondo para dar un aspecto mas estilizado.


Comenzemos: 

Creamos una nueva instancia de Phaser.Game:


var game = new Phaser.Game(800, 680, Phaser.AUTO, 'ejemplo', { preload: preload, create: create, update: update });

Creamos las variables:


var jugador,
municion,
tiempoDisparo=0,
teclas,
teclaDisparo,
empieza=false;

Cargamos las imagenes necesarias:


function preload() {
    game.load.image('balaHEROE', 'BALAJ.png');
    game.load.image('HEROE', 'NAVE.png');
    game.load.image('_FONDO', 'FONDO.png');
}

En la funcion Create tenemos:


function create() {
    game.physics.startSystem(Phaser.Physics.ARCADE);
game.add.image(0,0,"_FONDO");
    municion = game.add.group();
    municion.enableBody = true;
    municion.physicsBodyType = Phaser.Physics.ARCADE;
    municion.createMultiple(30, 'balaHEROE');
    municion.setAll('anchor.x', 0.5);
    municion.setAll('anchor.y', 2);
    municion.setAll('outOfBoundsKill', true);
     comienza();
    teclas = game.input.keyboard.createCursorKeys();
    teclaDisparo = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
};

Primero inicializamos nuestro sistemas de físicas como vimos en Phaser Fisica, luego agregamos un fondo, ahora creamos la munición de nuestro heroe que no es mas que un grupo,sabemos que debemos activar la fisica a cada uno de los hijos del grupo municion.enableBody=true, municion.physicsBodyType=Phaser.Physics.ARCADE, ahora tenemos municion.createMultiple(30, 'balaHEROE');

Todo grupo tiene el metodo createMultiple(cantidad de elementos,nombre clave del sprite); que sirve para crear algo conocido como pool esto es un array con muchos elementos normalmente del mismo tipo en este caso un array "precargado" con 30 elementos(municion) con una caracteristica especial su propiedad exists visto en Reciclar objetos tiene valor "false" es decir no sera mostrado y su posicion por defecto sera (x:0,y:0). 

 La forma mas practica para modifcar la propiedad de los objetos contenidos en un grupo es municion.setAll('anchor.x', 0.5);, el metodo setAll(propiedad de los objetos,valor a modificar); coloca a todo hijo de grupo el valor que deseemos modificar de algunas de sus propiedades en este caso su "anchor" en sus respectivos ejes.

Como no todos nuestro disparos van a dar en el blanco entonces activamos la propiedad outOfBoundsKill(destruir fuera de los limites) pasandole true, como dice el metodo toda municion que salga de los limites del mundo sera destruidas y asi lista para reutilizarla.

La funcion comienza es donde vamos a agregar nuestra nave lo veremos mas adelante ,creamos nuestra teclas para mover nuestra heroe y agregamos la tecla barra esparceadora para disparar. 

 Veamos la funcion comienza y la funcion crearJugador:

function comienza (){
crearJugador();
empieza=true;
};

Aqui solo llamamos la funcion crearJugador la cual crear a nuestra nave y la variable empieza le pasamo true necesario para la funcion update que veremos ahora.

function crearJugador(){
    jugador = game.add.sprite(200, 600, 'HEROE');
    jugador.anchor.setTo(0.5, 0.5);
    game.physics.enable(jugador, Phaser.Physics.ARCADE);
};

Esta funcion es mas que todo por organizar el codigo porque no hacemos nada nuevo agregamos el sprite , modificamos su anchor para que los disparos aparezcan por la parte superior de la nave, Recuerda que el "anchor point" es un punto dentro de la textura. 



Por ultimo activamos la fisica a este sprite. Ahora vamos a dar un poco de vida a nuestro heroe:


function update() {
if(empieza){
    jugador.body.velocity.setTo(0, 0);
    if (teclas.left.isDown)
    {
       jugador.body.velocity.x = -200
    }
    else if (teclas.right.isDown)
    {
        jugador.body.velocity.x = 200;
    }
    if (teclaDisparo.isDown)
    {
        atacaHeroe();
    }
 };
};

Aqui vemos la utilidad de la variable comienza, la funcion update es llamada inmediatamente empezemos el juego y si no comprobamos primero que el jugador ha sido creado(funcion comienza) entonces cuando actualize(update) y llame al jugador este simplemente aun no ha sido definido por lo tanto se produce un error. 

Aqui no hay nada que no hayamos visto primero colocamos la velocidad del jugador en cero es lo mismo que hacer jugador.body.velocity.y=0;jugador.body.velocity.x=0 pero con el metodo .setTo(x,y) lo hacemos mas practico.

Luego modificamos las posicion del jugador dependiendo de la tecla presionada y si presionamos la teclaDisparo(barra Esparcedora) llamamos a la funcion atacaHeroe() que sera la encargarda de empezar el bombardeo(disparar). 

 Por ultimo y no menos importante:

function atacaHeroe () {
    if (game.time.now > tiempoDisparo)
    {
       var local = municion.getFirstExists(false);
        if (local)
        {
            local.reset(jugador.x, jugador.y + 20);
            local.body.velocity.y = -400;
            tiempoDisparo = game.time.now + 600;
        }
    }
};

Cuando creamos las variables entre ellas se encontraba tiempoDisparo con valor cero, con esta variable comprobamos cada cuanto puede disparar nuestro heroe ya que no queremos que parezca una metralleta y quiero que se asemeje mas al juego original.


Con game.time.now obtenemos el tiempo actual , el cual sera mayor al valor que esta almacenado en la variable, pero cuando entre en la comprobacion, a la variable tiempoDisparo le asignamos el tiempo actual mas un valor cualquiera:

tiempoDisparo = game.time.now + 600;

Para que cuando la funcion sea llamada nuevamente ya no se podra disparar inmediatamente sino cada cierto intervalo de tiempo, entre mas grande el tiempo sumado al tiempo actual tardara mas en disparar nuestro heroe, te invito a jugar con los valores y viendo a que equivale game.time.now en la consola javascript te adelanto que no es mas que la funcion nativa Date.now()


Si recuerdas la entrada reciclar los grupos tienen el metodo .getFirstExists(false); con el cual recuperamos los hijos del grupo con la propiedad exists en false, como explicaba al comienzo el metodo .createMultiple() crea los objetos con esta propiedad en false. 

Si existe algun objeto del grupo entonces usamos el metodo de los sprite .reset(x,y);

Este metodo restablece todas las propiedades del sprite:
(visible(true),exists(true),alive(true),renderable(true),health(valor),posicionen sus ejes(valor) ,etc). 

Tambien recuerda que el metodo .createMultiple() afecta la posicion de los hijos por defecto lo modifica a (x:0,y:0).

.Asi que con el metodo local.reset(jugador.x, jugador.y + 20); modificamos la nueva posicion de la municion en este caso queremos que aparezca dentro de la nave. 

Y esta saldra disparada con una velocidad en su eje Y con valor negativo es decir hacia arriba local.body.velocity.y = -400; 

Con esto nuestro heroe esta listo para la acción y enfrentar cualquier tipo de amenaza en cualquier momento. 

Parte 1: Heroe

 Mensaje de la Flota:

 Alpha1: Prepárense, Se Acerca el Enemigo..... 

 Hasta la proxima.



2 comentarios:

  1. Saludo, me encantan los tutoriales son muy buenos. Hay un problema con la municion, cuando llegas a 30 se para, puede estar el error en municion.setAll('outOfBoundsKill', true); ?

    Un Saludo

    ResponderEliminar
  2. Hola tienes razon, lo que pasa es que falto incluir municion.setAll('checkWorldBounds',true); para comprobar en cada actualizacion si el objeto sigue dentro de los limites sino se dispara el evento outBoundKill y listo. Saludos.

    ResponderEliminar