Contenido

Últimos retoques y código completo

Antes de finalizar, vamos a darle al juego otro aspecto visual en cuanto a menús, fuentes, etc. Para ello, descárgate las imágenes necesarias haciendo clic AQUÍ y cópialas en la carpeta juegoknights/img. Además, vamos a aprovechar para implementar el resto de estados y resolver algunos bugs:
  • Apagar sonidos al cambiar de estado
  • Cambiar los controles desde el menú de opciones
  • Salir del juego desde el menú de opciones
Quedaría pendiente que el juego no se reiniciara al cambiar de estado, pero por su complejidad no se plantea hacerlo en este momento. Además, es posible detectar algunos bugs relativos al audio, pero son motivados por los problemas de compatibilidad real del audio en HTML5. A continuación y para finalizar, se muestra el código completo de cada fichero en su versión final.

index.html


<!DOCTYPE html>
<html>
    <head>
        <title>Juego Phaser</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" type="text/css" href="css/style.css">
        <script type="text/javascript" src="js/phaser.min.js"></script>
        <script type="text/javascript" src="js/estadoInicio.js"></script>
        <script type="text/javascript" src="js/estadoOpciones.js"></script>
        <script type="text/javascript" src="js/estadoJugar.js"></script>
        <script type="text/javascript" src="js/estadoSalir.js"></script>
        <script type="text/javascript" src="js/script.js"></script>
    </head>
    <body>
    </body>
</html>

style.css


body{
    text-align: center;
    background-color: #000;
}

canvas{
    margin: 3em auto;
}

script.js


//Variable juego
var juego = new Phaser.Game(800, 600);

//Controles por defecto (que se pueden cambir en opciones)
var controles = "flechas";

//Creador de estados
juego.state.add('EstadoInicio', MiJuego.EstadoInicio);
juego.state.add('EstadoOpciones', MiJuego.EstadoOpciones);
juego.state.add('EstadoJugar', MiJuego.EstadoJugar);
juego.state.add('EstadoSalir', MiJuego.EstadoSalir);

//Inicio el estado por defecto
juego.state.start('EstadoInicio');

estadoInicio.js


//Variable MiJuego
var MiJuego = {};

//Inicializo la función EstadoInicio vacía
MiJuego.EstadoInicio = function (juego) {

};

//Este es el prototipo o "plantilla" que se cargará cada vez que inicialice una nueva función  MiJuego.EstadoInicio
MiJuego.EstadoInicio.prototype = {

    preload: function () {

        //Imagen de fondo y título
        this.load.image('fondo', 'img/fondo.png');
        this.load.image('titulo', 'img/titulo.png');

        //Imagenes para los botones
        this.load.spritesheet('botonInicio', 'img/botonInicio.png', 104, 19);
        this.load.spritesheet('botonOpciones', 'img/botonOpciones.png', 137, 19);
        this.load.spritesheet('botonJugar', 'img/botonJugar.png', 104, 19);
        this.load.spritesheet('botonSalir', 'img/botonSalir.png', 104, 19);

    },

    create: function () {

        //Dibujo el fondo y el título
        juego.add.sprite(0, 0, 'fondo');
        juego.add.sprite(171, 225, 'titulo');

        //Creo el menú de botones
        crearMenu();

    },

};

function crearMenu(){

    //Creo un grupo para el menú
    menu = juego.add.group();

    //Añado los cuatro botones al menú
    this.botonInicio = juego.make.button(105, 30, 'botonInicio', cambiarEstado, this, 2, 1, 0); //over, out, pressed
    menu.add(this.botonInicio);

    this.botonOpciones = juego.make.button(260, 31, 'botonOpciones', cambiarEstado, this, 2, 1, 0);
    menu.add(this.botonOpciones);

    this.botonJugar = juego.make.button(450, 31, 'botonJugar', cambiarEstado, this, 2, 1, 0);
    menu.add(this.botonJugar);

    this.botonSalir = juego.make.button(600, 31, 'botonSalir', cambiarEstado, this, 2, 1, 0);
    menu.add(this.botonSalir);

    //Fijo los botones para que no se muevan al moverse la cámara
    menu.fixedToCamera = true;

}

function cambiarEstado(item){

    //Cambio el estado al hacr clic en un botón
    this.estado = 'Estado'+item.key.substr(5);
    juego.state.start(this.estado);
    //Si no es EstadoJugar paro la música
    if (this.estado !== "EstadoJugar"){
        juego.sound.stopAll();
    }

}

estadoOpciones.js


MiJuego.EstadoOpciones = function (juego) {

};

MiJuego.EstadoOpciones.prototype = {

    preload: function () {

        //Fondo y textos
        this.load.image('fondo', 'img/fondo.png');
        this.load.image('controles', 'img/controles.png');
        this.load.spritesheet('flechas', 'img/flechas.png', 125, 18);
        this.load.spritesheet('aswd', 'img/aswd.png', 84, 17);
    },

    create: function () {

        //Dibujo el fondo
        juego.add.sprite(0, 0, 'fondo');

        //Menú Botones
        crearMenu();

        //Texto
        juego.add.sprite(300, 225, 'controles');

        //Menú opciones
        opciones = juego.add.group();

        //Añado los dos botones al menú
        this.botonFlechas = juego.make.button(250, 300, 'flechas', cambiarControles, this, 2, 1, 0); //over, out, pressed
        opciones.add(this.botonFlechas);

        this.botonAswd = juego.make.button(450, 300, 'aswd', cambiarControles, this, 2, 1, 0); //over, out, pressed
        opciones.add(this.botonAswd);

    }

};

function cambiarControles(item){

    //Cambio los controles al hacer clic en un botón y vuelvo al estado jugar
    controles = item.key;
    juego.state.start("EstadoJugar");

}

estadoSalir.js


MiJuego.EstadoSalir = function (juego) {

};

MiJuego.EstadoSalir.prototype = {

    preload: function () {

        //Fondo y textos
        this.load.image('fondo', 'img/fondo.png');
        this.load.image('texto', 'img/textoConfirmacion.png');
        this.load.spritesheet('si', 'img/si.png', 22, 17);
        this.load.spritesheet('no', 'img/no.png', 40, 16);
    },

    create: function () {

        //Dibujo el fondo
        juego.add.sprite(0, 0, 'fondo');

        //Menú Botones
        crearMenu();

        //Texto
        juego.add.sprite(142, 225, 'texto');

        //Menú salir
        salir = juego.add.group();

        //Añado los dos botones al menú
        this.botonSi = juego.make.button(300, 300, 'si', accionSalir, this, 2, 1, 0); //over, out, pressed
        salir.add(this.botonSi);

        this.botonNo = juego.make.button(450, 300, 'no', accionSalir, this, 2, 1, 0); //over, out, pressed
        salir.add(this.botonNo);

    }

};

function accionSalir(item){

    //Salgo a inicio o vuelvo al juego
    if (item.key == 'si'){
        juego.state.start("EstadoInicio");
    }
    else{
        juego.state.start("EstadoJugar");
    }

}

estadoJugar.js


MiJuego.EstadoJugar = function (juego) {

};

MiJuego.EstadoJugar.prototype = {

    preload: function () {

        //Imágenes para los mapas
        this.load.image('piedra', 'img/piedra.png');
        this.load.image('metal', 'img/metal.png');
        this.load.image('madera', 'img/madera.png');

        //Archivo JSON de mapa
        this.load.tilemap('mapa', 'maps/mapaknights.json', null, Phaser.Tilemap.TILED_JSON);

        //Imágenes adicionales
        juego.load.spritesheet('gema', 'img/gemas.png', 16, 15);
        this.load.image('trampa', 'img/trampa.png');
        this.load.image('plataforma', 'img/plataforma.png');
        this.load.image('pastilla', 'img/pastilla.png');

        //Imágenes información
        this.load.image('textoVidas', 'img/textoVidas.png');
        this.load.image('textoGemas', 'img/textoGemas.png');

        //Jugador
        juego.load.spritesheet('blueKnight', 'img/blueKnight.png', 50, 50);

        //Dirección
        this.direccion = "Derecha";
        this.direccionPlataforma = 1;

        //Potencia
        this.potencia = -150;

        //Cantidad
        this.cantidadGemas = 15;
        this.infoGema = new Array();

        //Vidas
        this.vidas = 2;
        this.infoVida = new Array();

        //Movimiento
        this.movimiento = 1;

        //Audios
        this.load.audio('musica', 'audio/musica.mp3');
        this.load.audio('gema', 'audio/gema.mp3');
        this.load.audio('vida', 'audio/vida.mp3');
        this.load.audio('ganar', 'audio/ganar.mp3');
        this.load.audio('morir', 'audio/morir.mp3');

    },

    create: function () {

        //Establezco los límites del juego (el tamaño de los mapas en este caso)
        juego.world.setBounds(0, 0, 1600, 640);

        juego.stage.smoothed = true;

        //Habilitamos el sistema de física Arcade de Phaser (velocidad, movimientos, colisiones, etc)
        juego.physics.startSystem(Phaser.Physics.ARCADE);

        //Mapa
        this.mapa = this.add.tilemap('mapa');

        //Tilesets para el mapa
        this.mapa.addTilesetImage('piedra');
        this.mapa.addTilesetImage('madera');
        this.mapa.addTilesetImage('metal');

        //Capas
        this.fondo = this.mapa.createLayer('fondo');
        this.adornos = this.mapa.createLayer('adornos');
        this.lamparas = this.mapa.createLayer('lamparas');
        this.suelo = this.mapa.createLayer('suelo');
        this.paredes = this.mapa.createLayer('paredes');

        //Gemas
        this.gemas = juego.add.group();
        this.gemas.enableBody = true;
        this.mapa.createFromObjects('gemas', 'gema', 'gema', 0, true, false, this.gemas);
        this.gemas.callAll('animations.add', 'animations', 'girar', [0, 1, 2, 3, 4, 5, 6], 10, true);
        this.gemas.callAll('animations.play', 'animations', 'girar');

        //Trampas
        this.trampas = juego.add.group();
        this.trampas.enableBody = true;
        this.mapa.createFromObjects('trampas', 'trampa', 'trampa', 0, true, false, this.trampas);

        //Blue Knight
        this.blueKnight = juego.add.sprite(50, juego.world.height - 100, 'blueKnight');
        juego.physics.arcade.enable(this.blueKnight);
        this.blueKnight.anchor.setTo(0.5,0.5);
        this.blueKnight.body.gravity.y = 800;
        this.blueKnight.body.bounce.y = 0.2;
        this.blueKnight.body.setCircle(25,0,0);
        this.blueKnight.scale.set(0.75);

        //Animaciones derecha
        this.blueKnight.animations.add('andarDerecha', [1, 2, 3, 4, 5, 6], 10, true);
        this.blueKnight.animations.add('ataque1Derecha', [7, 8], 10, true);
        this.blueKnight.animations.add('ataque2Derecha', [9, 10], 10, true);
        this.blueKnight.animations.add('ataqueAbajoDerecha', [11, 12], 10, true);
        this.blueKnight.animations.add('saltarDerecha', [13, 14], 10, true);
        this.blueKnight.animations.add('agacharseDerecha', [15, 16], 10, true);
        this.blueKnight.animations.add('ganarDerecha', [17, 18], 10, true);
        this.blueKnight.animations.add('vidaDerecha', [19, 20, 21, 22], 10, true);
        this.blueKnight.animations.add('morirDerecha', [23, 24, 25, 26, 27], 5, false);

        //Animaciones izquierda
        this.blueKnight.animations.add('andarIzquierda', [29, 30, 31, 32, 33, 34], 10, true);
        this.blueKnight.animations.add('ataque1Izquierda', [35, 36], 10, true);
        this.blueKnight.animations.add('ataque2Izquierda', [37, 38], 10, true);
        this.blueKnight.animations.add('ataqueAbajoIzquierda', [39, 40], 10, true);
        this.blueKnight.animations.add('saltarIzquierda', [41, 42], 10, true);
        this.blueKnight.animations.add('agacharseIzquierda', [43, 44], 10, true);
        this.blueKnight.animations.add('ganarIzquierda', [45, 46], 10, true);
        this.blueKnight.animations.add('vidaIzquierda', [47, 48, 49, 50], 10, true);
        this.blueKnight.animations.add('morirIzquierda', [51, 52, 53, 54, 55], 5, false);

        //Inicializo el movimiento de los cursores
        if (controles == 'flechas'){
            this.cursoresBlue = juego.input.keyboard.addKeys({
                left: Phaser.KeyCode.LEFT,
                right: Phaser.KeyCode.RIGHT,
                up: Phaser.KeyCode.UP,
                down: Phaser.KeyCode.DOWN,
            });
        }
        else{
            this.cursoresBlue = juego.input.keyboard.addKeys({
                left: Phaser.KeyCode.A,
                right: Phaser.KeyCode.D,
                up: Phaser.KeyCode.W,
                down: Phaser.KeyCode.S,
            });
        }


        //Cámara
        juego.camera.follow(this.blueKnight);

        //Pastilla para resaltar el menú
        this.pastilla = juego.add.sprite(100, 20, 'pastilla');
        this.pastilla.alpha = 0.5;
        this.pastilla.fixedToCamera = true;

        //Menú
        crearMenu();

        //Colisiones del suelo
        this.mapa.setCollisionByExclusion([0],true,'suelo');
        this.suelo.enableBody = true;

        //Colisiones de las paredes
        this.mapa.setCollisionByExclusion([0],true,'paredes');
        this.paredes.enableBody = true;

        //Plataforma oculta
        this.plataforma = juego.add.sprite(1200, 300, 'plataforma');
        juego.physics.arcade.enable(this.plataforma);
        this.plataforma.body.immovable = true;
        this.plataforma.visible = false;

        //Audio de fondo
        this.audioMusica = juego.add.audio('musica');
        this.audioMusica.loopFull(0.2);
        this.audioMusica.play();

        //Audios de acciones
        this.audioGema = juego.add.audio('gema');
        this.audioVida = juego.add.audio('vida');
        this.audioGanar = juego.add.audio('ganar');
        this.audioMorir = juego.add.audio('morir');

        //Menú info
        this.info = juego.add.group();
        this.info.fixedToCamera = true;
        this.info.create(110, 65, 'textoVidas');
        this.info.create(310, 65, 'textoGemas');
        this.infoVidas();
        this.infoGemas();

    },

    update: function () {

        //Colisiones
        this.colisionSuelo = juego.physics.arcade.collide(this.blueKnight, this.suelo);
        this.colisionParedes = juego.physics.arcade.collide(this.blueKnight, this.paredes);
        this.colisionPlataforma = juego.physics.arcade.collide(this.blueKnight, this.plataforma);

        //Gemas
        juego.physics.arcade.overlap(this.blueKnight, this.gemas, this.recogerGema, null, this);

        //Trampas
        juego.physics.arcade.overlap(this.blueKnight, this.trampas, this.caidaEnTrampa, null, this);

        //Movimiento (Solo si no está en una trampa)
        if (this.movimiento == 1){
            this.movimientoBlue();
        }

        //Ganar
        if (this.cantidadGemas == 0) {
            this.ganar();
        }

        //Mover plataforma
        if (this.cantidadGemas <= 7){
            this.plataforma.visible = true;
            this.movimientoPlataforma();
        }

        //Caer al vacío por abajo y morir
        if (this.blueKnight.y >= 640){
            this.caer();
        }

    },

    movimientoBlue: function () {

        //Reseteo la velocidad del jugador y frame quieto
        this.blueKnight.body.velocity.x = 0;

        //Mover a la izquierda
        if (this.cursoresBlue.left.isDown) {

            this.direccionBlue = "Izquierda";
            this.blueKnight.body.velocity.x = -200;
            this.blueKnight.animations.play('andar' + this.direccionBlue);

        }

        //Mover a la derecha
        else if (this.cursoresBlue.right.isDown) {

            this.direccionBlue = "Derecha";
            this.blueKnight.body.velocity.x = 200;
            this.blueKnight.animations.play('andar'+this.direccionBlue);

        }

        //Quedarse quieto
        else {

            this.blueKnight.animations.stop();
            //Compruebo dirección para mostrar el frame correspondiente
            if (this.direccionBlue == "Izquierda"){
                this.blueKnight.frame = 28;
            }
            else{
                this.blueKnight.frame = 0;
            }
        }

        //Saltar
        if (this.cursoresBlue.up.isDown && this.colisionSuelo && !this.colisionParedes){

            this.blueKnight.animations.play('saltar'+this.direccionBlue);
            this.blueKnight.body.velocity.y = this.potencia;

        }

        //Saltar plataforma
        if (this.cursoresBlue.up.isDown && this.colisionPlataforma){

            this.blueKnight.animations.play('saltar'+this.direccionBlue);
            this.blueKnight.body.velocity.y = this.potencia;

        }

        //Agacharse
        if (this.cursoresBlue.down.isDown){

            this.blueKnight.animations.play('agacharse'+this.direccionBlue);

        }

    },

    recogerGema: function (knight, gema) {

        gema.kill();
        this.audioGema.play(); //Reproduzco sonido
        this.potencia -= 25;
        this.cantidadGemas--;
        this.infoGema[this.cantidadGemas].kill(); //Borro la info de gema

    },


    caidaEnTrampa: function (knight, trampa) {

        //Le obligo a pararse
        this.movimiento = 0;
        this.blueKnight.body.velocity.x = 0;
        //Animación de quitar vida
        this.blueKnight.animations.play('vida' + this.direccionBlue);
        this.audioVida.play(); //Reproduzco sonido
        //Elimino trampa
        trampa.kill();
        //Quito vida (después de 1 segundo de animación)
        juego.time.events.add(Phaser.Timer.SECOND * 1, this.quitarVida, this);

    },

    quitarVida: function () {

        this.vidas --; //Resto una vida
        if (this.vidas >= 0){ //Continuar con una vida menos
            this.infoVida[this.vidas].kill(); //Borro la info de vida
            this.movimiento = 1; //Permito seguir
        }
        else{ //Morir
            this.audioMorir.play(); //Reproduzco sonido
            this.audioMusica.fadeOut(4000); //Fade out música
            this.blueKnight.animations.play('morir' + this.direccionBlue); //Animación morir
            this.audioMorir.stop(); //Reproduzco sonido

        }

    },

    movimientoPlataforma: function () {

        //Alterno movimiento arriba y abajo
        if (this.plataforma.y == 520){
            this.direccionPlataforma = -1
        }
        if (this.plataforma.y == 200){
            this.direccionPlataforma = 1
        }
        this.plataforma.y += this.direccionPlataforma;

    },

    ganar: function () {

        //Le obligo a pararse
        this.movimiento = 0;
        this.blueKnight.body.velocity.x = 0;
        this.audioGanar.play(); //Reproduzco sonido
        this.audioMusica.fadeOut(4000); //Fade out música
        this.audioGanar.stop(); //Reproduzco sonido
        this.blueKnight.animations.play('ganar' + this.direccionBlue); //Animación de ganar

    },

    infoVidas: function () {

        //Vidas
        for (var i=0; i<this.vidas; i++){
            var x = 20 * i;
            this.infoVida[i] = this.info.create(180 + x, 50, 'blueKnight');
            this.infoVida[i].scale.set(0.60);
        }

    },

    infoGemas: function () {

        //Gemas
        for (var i=0; i<this.cantidadGemas; i++){
            var x = 20 * i;
            this.infoGema[i] = this.info.create(395 + x, 62, 'gema');
            this.infoGema[i].scale.set(0.90);
        }

    },

    caer: function () {

        //Quito las vidas
        this.vidas = 0;
        this.infoVida[0].kill();
        this.infoVida[1].kill();
        //Muero
        this.movimiento = 0;
        this.audioMorir.play(); //Reproduzco sonido
        this.audioMusica.fadeOut(2000); //Fade out música
        this.audioMorir.stop(); //Reproduzco sonido

    },

};
El resultado final del juego debería ser similar al siguiente: