Carreras con Phaser

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.

Carga background

Cargamos la imagen de fondo del juego.

Carga jugadores con función

preload(){
	...
	this.load.spritesheet('player0', '../img/player0.png', {frameWidth: 64, frameHeight: 32});
	...
}

create() {
	...
	this.coordenadasPlayers = [
		{x: 280, y: 500},
		{x: 360, y: 500},
		{x: 300, y: 550},
		{x: 380, y: 550},
	];
	this.players = [];
	this.n_jugadores = 4;
	this.creaPlayers();
}

creaPlayers() {
	for (let i = 0; i < this.n_jugadores; i++) {
		//Creamos el jugador. Lo almacenamos en un array de jugadores porque nos será más fácil en adelante aplicar funcionalidades a todos los jugadores
		this.players[i] = this.matter.add.sprite(this.coordenadasPlayers[i].x, this.coordenadasPlayers[i].y, `player${i}`);
		//Creamos la animación del jugador
		this.anims.create({
			key: `volar${i}`,
			frames: this.anims.generateFrameNumbers(`player${i}`, {start: 0, end: 1}),
			frameRate: 10,
			repeat: -1,
		});
		//Vinculamos la animación al jugador creado y comenzamos su reproducción.
		//El primer parámetro de la función volar es el nombre de la animación, y el segundo, si la animación se va a ejecutar indefinidamente
		this.players[i].play(`volar${i}`, true);
	}
}

const config = {
	...
	physics: {
		default: 'matter',
		matter: {
			gravity: {
				y: 0,
			},
		},
	},
};

Mover jugador hacia delante

update() {
	for (let i = 0; i < this.n_jugadores; i++) {
		this.players[i].thrust(-0.001);
	}
}

Rotar

create(){
	...
	this.cursors = this.input.keyboard.createCursorKeys();
}
update() {
	for (let i = 0; i < this.n_jugadores; i++) {
		this.players[i].thrust(-0.003);
		if (this.cursors.left.isDown)
			this.players[i].setAngularVelocity(-0.065);
		else if (this.cursors.right.isDown)
			this.players[i].setAngularVelocity(0.065);
		}
	}
}

Controles visuales

create(){
	this.coordenadasPlayers = [
		{x: 280, y: 500, leftbtn: {x: 30, y: 540, rotacion :45}, rightbtn: {x: 90, y: 600, rotacion :45}},
		{x: 360, y: 500, leftbtn: {x: 870, y: 30, rotacion :45}, rightbtn: {x: 930, y: 90, rotacion :45}},
		{x: 300, y: 550, leftbtn: {x: 30, y: 90, rotacion :-45}, rightbtn: {x: 90, y: 30, rotacion :-45}},
		{x: 380, y: 550, leftbtn: {x: 870, y: 600, rotacion :-45}, rightbtn: {x: 930, y: 540, rotacion :-45}}
	];
    ...
}


creaPlayers() {
	for (let i = 0; i < this.n_jugadores; i++) {
		//Código del bucle que no pongo porque ocupa mucho
		this.controlesVisuales(i);
	}
}

controlesVisuales(n) {
	this.players[n].setData('direccionHorizontal', 0);

	const leftbtn = this.add.sprite(this.coordenadasPlayers[n].leftbtn.x, this.coordenadasPlayers[n].leftbtn.y, 'flecha').setInteractive();
	leftbtn.angle = this.coordenadasPlayers[n].leftbtn.rotacion;

	const rightbtn = this.add.sprite(this.coordenadasPlayers[n].rightbtn.x, this.coordenadasPlayers[n].rightbtn.y, 'flecha').setInteractive();
	rightbtn.angle = this.coordenadasPlayers[n].rightbtn.rotacion;
	rightbtn.flipX = true;
}

Añadir funcionalidad a los controles visuales

create(){
	...
	this.cursors = this.input.keyboard.createCursorKeys();
}
controlesVisuales(n) {
	...
	rightbtn.on('pointerdown', function() {
		this.scene.players[n].setData('direccionHorizontal', 1);
	});

	leftbtn.on('pointerdown', function() {
		this.scene.players[n].setData('direccionHorizontal', -1);
	});

	rightbtn.on('pointerup', function() {
		this.scene.players[n].setData('direccionHorizontal',  0);
	});

	leftbtn.on('pointerup', function() {
		this.scene.players[n].setData('direccionHorizontal', 0);
	});
}

update() {
	for (let i = 0; i < this.n_jugadores; i++) {
    	this.players[i].thrust(-0.003);
		if (this.cursors.left.isDown || this.players[i].getData('direccionHorizontal') === -1)
			this.players[i].setAngularVelocity(-0.065);
		else if (this.cursors.right.isDown || this.players[i].getData('direccionHorizontal') === 1)
			this.players[i].setAngularVelocity(0.065);
	}
}

Physics Editor

preload(){
	...
	this.load.image('ladoizquierdo', '../img/ladoizquierdo.png');
	this.load.image('ladoderecho', '../img/ladoderecho.png');
	this.load.image('centro', '../img/centro.png');
	this.load.image('luna', '../img/luna.png');
	this.load.image('saturno', '../img/saturno.png');
	this.load.image('astronauta', '../img/astronauta.png');
	this.load.json('muros', '../img/muros.json');
}

create(){
	...
	const muros = this.cache.json.get('muros');

	const ladoizquierda = this.matter.add.sprite(181, 303, 'ladoizquierdo', null, {shape: muros.ladoizquierdo});
	const ladoderecha = this.matter.add.sprite(797, 314.5, 'ladoderecho', null, {shape: muros.ladoderecho});
	const centro = this.matter.add.sprite(472, 344, 'centro', null, {shape: muros.centro});
	const luna = this.matter.add.sprite(170, 160, 'luna', null, {shape: muros.luna});
	const astronauta = this.matter.add.sprite(717, 240, 'astronauta', null, {shape: muros.astronauta});
	const saturno = this.matter.add.sprite(740, 150, 'saturno', null, {shape: muros.saturno});
}

La luna rebota

luna.setBounce(1.5);

Detectar vueltas

create(){
	...
	this.matter.add.rectangle(175, 500, 135, 30, {
		isSensor: true,		
		marker: 1,	
		angle: -0.25 * Math.PI
	});

	//collisionstart detecta la colisión entre cualquier forma y cualquier forma
	this.matter.world.on('collisionstart', (event) => {	
	//  La variable pairs almacena todos los objetos que están colisionando
		var pairs = event.pairs;

		for (var i = 0; i < pairs.length; i++) {
			//  Estamos detectando la colisión entre objetos de cualquier tipo. Por tanto, también estamos detectando la colisión entre jugadores y objetos de diversa índole que no interesan. El siguiente condicional filtra para sólo tener en cuenta las colisiones donde uno de los elementos colisionados tiene la propiedad isSensor con valor True
			if (pairs[i].isSensor) {		
				alert('colision');
			}
		}
	});
}

Poner marcador

pintarMarcador() {
	this.add.text(400, 275, 'P1:', {fontFamily: 'font1', fontSize: 24, color: '#00ff00'});
	this.add.text(480, 275, 'P2:', {fontFamily: 'font1', fontSize: 24, color: '#00ff00'});
	this.add.text(400, 315, 'P3:', {fontFamily: 'font1', fontSize: 24, color: '#00ff00'});
	this.add.text(480, 315, 'P4:', {fontFamily: 'font1', fontSize: 24, color: '#00ff00'});

	this.marcadorCoche0 = this.add.text(440, 275, '0', {fontFamily: 'font1', fontSize: 24, color: '#ffffff'});
	this.marcadorCoche1 = this.add.text(520, 275, '0', {fontFamily: 'font1', fontSize: 24, color: '#ffffff'});
	this.marcadorCoche2 = this.add.text(440, 315, '0', {fontFamily: 'font1', fontSize: 24, color: '#ffffff'});
	this.marcadorCoche3 = this.add.text(520, 315, '0', {fontFamily: 'font1', fontSize: 24, color: '#ffffff'});
}

Aumentar marcador

for (var i = 0; i < pairs.length; i++) {

	if (pairs[i].isSensor) {
		// En las dos siguientes líneas estamos haciendo una deconstrucción de una propiedad en Javascript. En lugar de este código podíamos haber hecho también: const bodyA = pairs[i].bodyA
		const {bodyA} = pairs[i];
		const {bodyB} = pairs[i];
		// Cuando se produce la colisión que nos interesa detectar, uno de los elementos que colisionan será el area que colocamos en el paso anterior y el otro será el jugador. El jugador no tiene la propiedad isSensor a true, por tanto, utilizaremos esa propiedad para evaluar quien es quien. De esta forma, la variable zone almacenará  el area, mientras que la variable playerBody almacenará el jugador.

		let zone;
		let playerBody;
		if (bodyA.isSensor) {
			zone = bodyA;
			playerBody = bodyB;
		} else if (bodyB.isSensor) {
			zone = bodyB;
			playerBody = bodyA;
		}
		const playerSprite = playerBody.gameObject;
		const playerKey = playerSprite.texture.key;
		const playerIndex = playerKey.substr(-1);
		playerSprite.data.values.marcador++;
		this[`marcadorCoche${playerIndex}`].text = `${playerSprite.getData('marcador')}`;
	}
}
creaPlayers() {
	...
	this.players[i].setData('marcador', 0);
}

Evitar que el jugador haga trampas

create() {
	...
	this.matter.add.rectangle(450, 150, 30, 60, {isSensor: true, marker: 2});
	this.matter.add.rectangle(760, 400, 90, 30, {isSensor: true, marker: 3});
	this.matter.add.rectangle(500, 505, 30, 70, {isSensor: true, marker: 4});
	...

	if (pairs[i].isSensor) {
		...
		const playerIndex = playerKey.substr(-1);

		if (playerSprite.getData('marker') === zone.marker) {
			if (zone.marker === 4) {
				playerSprite.setData('marker', 1);
			} else {
				if (zone.marker === 1) {
					playerSprite.data.values.marcador++;
					this[`marcadorCoche${playerIndex}`].text = playerSprite.getData('marcador');
					
				}
				playerSprite.data.values.marker++;
			}
		}
	}
}
this.players[i].setData('marker', 2)

Alguien gana el juego

if (playerSprite.getData('marcador') === 2) {
	alert(`gano el player  ${playerKey}`);
	this.scene.start('ganarScene');
}

Con portada

class EscenaPortada extends Phaser.Scene {
	constructor() {
		super({key: 'EscenaPortada'});
	}
	preload() {
		this.load.image('portada', '../img/portada/portada.jpg');
		this.load.image('2jugadores', '../img/portada/2.png');
		this.load.image('3jugadores', '../img/portada/3.png');
		this.load.image('4jugadores', '../img/portada/4.png');
	}
	create() {
		this.add.sprite(480, 320, 'portada');
		const opcion2Jugadores = this.add.sprite(280, 550, '2jugadores').setInteractive();
		const opcion3Jugadores = this.add.sprite(480, 550, '3jugadores').setInteractive();
		const opcion4Jugadores = this.add.sprite(680, 550, '4jugadores').setInteractive();
		opcion2Jugadores.on('pointerdown', () => this.opcionJugador(2));
		opcion3Jugadores.on('pointerdown', () => this.opcionJugador(3));
		opcion4Jugadores.on('pointerdown', () => this.opcionJugador(4));
	}
	opcionJugador(nJugadores) {
		this.scene.start('EscenaJuego', {nJugadores});
	}
}
class Escena extends Phaser.Scene {
	constructor() {
		super({key: 'EscenaJuego'});
	}
	init(data){
		this.n_jugadores = data.nJugadores;
	}

Cargar música

this.load.audio('musica', '../img/Loyalty_Freak_Music_-_13_-_Ghost_Surf_Rock.mp3');
const musica = this.sound.add('musica');
musica.play({
  volume: 1
})

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.