jairogarcíarincón
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.
<!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>
body{
text-align: center;
background-color: #000;
}
canvas{
margin: 3em auto;
}
//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');
//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();
}
}
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");
}
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");
}
}
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:
Publicado el 05 de Febrero de 2025
phasertiled map editor