Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.
Carga de estructura y fondo
Definimos la estructura global del juego y cargamos la imagen de fondo.
Cargar el mapa
class Platform extends Phaser.Scene {
preload() {
this.load.tilemapTiledJSON('jsonLevel', jsonLevel);
this.load.image('tilesetPNG', tilesetPNG);
}
create(){
const map = this.make.tilemap({ key: 'jsonLevel' });
const tileset = map.addTilesetImage('nombreDelTilesetEnTiled', 'tilesetPNG');
this.collisionLayer = map.createLayer('collisionLayer', tileset);
// Esta línea hace un zoom para alejar, para que se vea el mapa al completo, una vez hayamos centrado el mapa en la posición correcta, podemos comentarla
this.cameras.main.setZoom(0.5);
}
Ubicar el punto de origen de la cámara en pantalla.
Para ello podremos usar la siguiente clase:
export class DebugMapCamera {
scene;
controls;
debugCoords;
constructor(scene) {
this.scene = scene;
this.debugCoords = scene.add.text(0, 0, '').setScrollFactor(0);
const cursors = scene.input.keyboard.createCursorKeys();
this.controls = new Phaser.Cameras.Controls.FixedKeyControl({
camera: scene.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
speed: 0.5
});
}
update(time, delta) {
this.controls.update(delta);
this.debugCoords.setText([
`scrollX: ${this.scene.cameras.main.scrollX}`,
`scrollY: ${this.scene.cameras.main.scrollY}`
]);
}
}
export default DebugMapCamera;
Y luego, dentro de la escena donde queremos fijar la cámara, usaremos:
create() {
...
this.debugCamera = new DebugMapCamera(this);
}
update(time, delta) {
this.debugCamera.update(time, delta);
}
Con esto podremos obtener las coordenadas en las que ubicaremos la cámara:
this.cameras.main.setScroll(440, -304);
Cargar Player con el JSON de Tiled
Primero definiremos la clase del Player en un fichero externo, para que todo quede más ordenado.
class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
super(scene, x, y, 'player');
scene.add.existing(this);
scene.physics.add.existing(this);
this.body.gravity.y = 500;
}
static loadAssets(scene) {
scene.load.spritesheet('player', player, { frameWidth: 180, frameHeight: 180 });
}
}
export default Player;
Hemos creado el método estático loadAssets para poder cargar los assets del player dentro de la propia clase del player. Sin embargo, para poder hacer esto, habrá que llamar ese método desde el preload de la escena:
preload(){
...
Player.loadAssets(this);
}
Necesitaremos el siguiente método en la escena:
findObjectsByClassInObjectsLayer(classParam, tilemap) {
const result = [];
tilemap.objects.forEach(function (element) {
if (element.name === 'objectsLayer') {
element.objects.forEach(function (element2) {
if (element2.type === classParam) {
element2.y -= tilemap.tileHeight;
result.push(element2);
}
});
}
});
return result;
}
Finalmente, cargamos el player en la escena.
const playersFromTiled = this.findObjectsByClassInObjectsLayer('player', map);
this.player = new Player(this, playersFromTiled[0].x, playersFromTiled[0].y);
Hacer la capa collisionLayer colisionable con el player
// Hacemos que los tiles que pertenecen a la collisionLayer sean colisionables
this.collisionLayer.setCollisionByExclusion([-1]);
// Establecemos la colisión entre el jugador y la collisionLayer
this.physics.add.collider(this.player, this.collisionLayer);
Cargar el resto de capas no colisionables (grassLayer y backgroundLayer)
create(){
...
map.createLayer('grassLayer', tileset).setDepth(100);
map.createLayer('backgroundLayer', tileset);
}
Ajustando la boundingbox del player
this.setSize(90, 180, true);
El jugador se mueve
class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
...
this.cursors = this.scene.input.keyboard.createCursorKeys();
}
update() {
if (this.cursors.left.isDown) {
this.leftWalk()
} else if (this.cursors.right.isDown) {
this.rightWalk();
}
}
leftWalk() {
this.body.setVelocityX(-250);
}
rightWalk() {
this.body.setVelocityX(250);
}
}
class Platform extends Phaser.Scene {
...
update() {
this.player.update();
}
}
Animación del jugador
Definimos la función que cargará las animaciones y que será llamada dentro del método create de la Escena:
class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
...
this.createAnimations();
}
createAnimations() {
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('player', {start: 2, end: 5}),
frameRate: 10,
repeat: -1,
});
}
leftWalk(){
this.body.setVelocityX(-250);
this.flipX = true;
this.play('walk', true);
}
rightWalk(){
this.body.setVelocityX(250);
this.flipX = false;
this.play('walk', true);
}
}
Reposo
class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
...
this.play('idle', true);
}
createAnimations() {
...
this.anims.create({
key: 'idle',
frames: this.anims.generateFrameNumbers('player', { start: 0, end: 1 }),
frameRate: 4,
repeat: -1,
});
}
update(){
...
}else {
this.idle();
}
}
idle() {
this.body.setVelocityX(0);
this.play('idle', true);
}
}
Saltar
Añadimos el método saltar a la clase Player. El jugador sólo debe poder saltar si está tocando el suelo:
class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
this.onGround= false;
}
update(){
this.onGround= this.body.onFloor();
...
if (this.cursors.up.isDown) {
this.jump();
}
}
jump(){
if(this.onGround){
this.body.setVelocityY(-500);
}
}
}
Añadir nuevos controles para un segundo jugador (paso opcional)
this.cursors2 = scene.input.keyboard.addKeys({ up: 'W', left: 'A', down: 'S', right: 'D' });
Además, debemos tener en cuenta que los nombres de los recursos y de las animaciones no pueden estar repetidos.
static loadAssets(scene) {
scene.load.spritesheet('run2', playerImgRun, { frameWidth: 52, frameHeight: 34 });
scene.load.spritesheet('idle2', playerImgIdle, { frameWidth: 52, frameHeight: 34 });
}
createAnimations() {
this.anims.create({
key: 'walk2',
frames: this.anims.generateFrameNumbers('run2', { start: 0, end: 13 }),
frameRate: 30,
repeat: -1,
});
this.anims.create({
key: 'idle2',
frames: this.anims.generateFrameNumbers('idle2', { start: 0, end: 17 }),
frameRate: 30,
repeat: -1,
});
}
Animación de Caer
class Player extends Phaser.Physics.Arcade.Sprite {
...
createAnimations() {
...
this.anims.create({
key: 'fall',
frames: this.anims.generateFrameNumbers('player', {start: 6, end: 7}),
frameRate: 7,
repeat: -1,
});
}
jump(){
if(this.onGround){
this.body.setVelocityY(-500);
this.play('fall', true);
}
}
leftWalk(){
...
if(this.onGround)this.play('walk', true);
}
rightWalk(){
...
if(this.onGround)this.play('walk', true);
}
idle() {
...
if (this.onGround)this.play('idle', true);
}
}
Controles visuales – poniendo los botones en pantalla
static loadAssets(scene) {
...
scene.load.image('arrow', arrow);
}
visualControls(scene) {
const leftbtn = scene.add.sprite(50, gameHeight - 30, 'arrow').setInteractive();
leftbtn.setDepth(200);
leftbtn.setScrollFactor(0, 0); // este método hará que cuando en los siguientes pasos hagamos que la cámara siga al jugador, el botón de desplazamiento no se desplace. Si no utilizásemos este método, cuando hagamos el pointerup, como este no se hace en la misma posición en la que hicimos el pointerdown, no funcionaría
const rightbtn = scene.add.sprite(140, gameHeight - 30, 'arrow').setInteractive();
rightbtn.flipX = true;
rightbtn.setDepth(200);
rightbtn.setScrollFactor(0, 0);
const upbtn = scene.add.sprite(850, gameHeight - 30, 'arrow').setInteractive();
upbtn.rotation = Math.PI/2;
upbtn.setDepth(200);
upbtn.setScrollFactor(0, 0);
}
Controles visuales – dando funcionalidad
Habrá que llamar al siguiente método desde el create del player.
visualControls(scene) {
...
this.setData('horizontalDirection', 0);
this.setData('isJumping', false);
leftbtn.on('pointerdown', () => {
this.setData('horizontalDirection', Phaser.LEFT);
});
rightbtn.on('pointerdown', () => {
this.setData('horizontalDirection', Phaser.RIGHT);
});
upbtn.on('pointerdown', () => {
this.setData('isJumping', Phaser.UP);
});
leftbtn.on('pointerup', () => {
this.setData('horizontalDirection', Phaser.NONE);
});
rightbtn.on('pointerup', () => {
this.setData('horizontalDirection', Phaser.NONE);
});
upbtn.on('pointerup', () => {
this.setData('isJumping', Phaser.NONE);
});
}
update() {
this.onGround = this.body.onFloor();
if (this.cursors.left.isDown || this.getData('horizontalDirection') === Phaser.LEFT) {
this.leftWalk();
} else if (this.cursors.right.isDown || this.getData('horizontalDirection') === Phaser.RIGHT) {
this.rightWalk();
} else {
this.idle();
}
if (this.cursors.up.isDown || this.getData('isJumping') === Phaser.UP) {
this.jump();
}
}
La cámara sigue al prota
class Platform extends Phaser.Scene {
create() {
...
this.cameras.main.setSize(gameWidth, gameHeight);
this.cameras.main.startFollow(this.player);
}
La cámara sigue al prota horizontalmente
Eliminamos la línea:
// this.cameras.main.startFollow(this.player);
class Platform extends Phaser.Scene {
create(){
this.cameras.main.scrollY = 0;
}
update(){
...
this.cameras.main.scrollX = this.player.x - 400;
}
El fondo se repite
Sustituímos la línea que carga el fondo
this.bg = this.add.tileSprite(gameWidth/2, gameHeight/2, gameWidth, gameHeight, 'back').setScrollFactor(0);
En el método update, desplazamos el fondo acorde con la posición del jugador
class Platform extends Phaser.Scene {
update() {
this.bg.tilePositionX = this.player.x;
Método para la inserción de enemigos
class Platform extends Phaser.Scene {
create() {
...
const antsFromTiled = this.findObjectsByClassInObjectsLayer('antEnemy', map);
const ants = this.insertBadGuys(this, antsFromTiled, AntEnemy);
}
insertBadGuys(scene, arrayDeMalos, type) {
const enemies = scene.physics.add.group({classType: type, runChildUpdate: true, runChildCreate: true, gravityY:100}); // Cuando instanciamos un grupo, los parámetros de gravedad los ponemos en la creación del grupo en lugar de en el personaje individual
for (let i = 0; i < arrayDeMalos.length; i++) {
const malo = new type(scene, arrayDeMalos[i].x, arrayDeMalos[i].y);
enemies.add(malo);
}
return enemies;
}
}
class AntEnemy extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
super(scene, x, y, 'ant');
scene.physics.add.collider(this, scene.collisionLayer);
scene.add.existing(this);
}
static loadAssets(scene) {
scene.load.spritesheet('ant', ant, { frameWidth: 192, frameHeight: 96 });
}
}
export default AntEnemy;
Enemigo con animación
Las animaciones deben ser cargadas desde el create de la escena.
class AntEnemy extends Phaser.Physics.Arcade.Sprite {
constructor(x, y, scene) {
...
this.anims.create({
key: 'antWalk',
frames: this.anims.generateFrameNumbers('ant', { start: 0, end: 3 }),
frameRate: 7,
repeat: -1,
});
this.play('antWalk');
}
}
export default AntEnemy;
Enemigo se mueve
constructor(x, y, scene) {
...
this.speed = 100;
this.direction = 1;
}
update() {
this.body.setVelocityX(this.direction* this.speed);
}
Enemigo inteligente
class AntEnemy extends Phaser.Physics.Arcade.Sprite {
update(){
...
const nextX = Math.floor(this.x / 32) + this.direction;
let nextY = this.y + this.height / 2;
nextY = Math.round(nextY / 32);
const nextTile = this.scene.collisionLayer.hasTileAt(nextX, nextY);
if (!nextTile && this.body.blocked.down) {
this.direction *= -1;
}
if (this.direction > 0) {
this.flipX = false;
} else {
this.flipX = true;
}
}
}
Player muere, Enemigo muere
class Platform extends Phaser.Scene {
...
create() {
...
this.physics.add.overlap(this.player, ants, this.player.checkEnemy, null, this.player);
}
class Player extends Phaser.Physics.Arcade.Sprite {
checkEnemy(player, enemy) {
// El jugador está cayendo?
if (this.body.velocity.y > 0) {
enemy.die();
} else {
this.die();
}
}
die() {
this.disableBody();
}
class AntEnemy extends Phaser.Physics.Arcade.Sprite {
die() {
this.disableBody();
}
Enemigo explota
class AntEnemy extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y, sprite) {
...
this.anims.create({
key: 'crashAnim',
frames: this.anims.generateFrameNumbers('crash', {start: 0, end: 4}),
frameRate: 7
});
this.on('animationcomplete', this.animationComplete, this);
}
static loadAssets(scene) {
...
scene.load.spritesheet('crash', crashImg, { frameWidth: 199, frameHeight: 200 });
}
die() {
this.disableBody();
this.play('crashAnim');
}
animationComplete(animation, frame, sprite) {
if (animation.key === 'crashAnim') {
this.destroy();
}
}
}
Prota explota
Utilizaremos la variable estaVivo para detener todas las demás animaciones cuando el Player es colisionado.
class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
...
this.isAlive = true;
this.on('animationcomplete', this.animationComplete, this);
this.anims.create({
key: 'crashAnim',
frames: this.anims.generateFrameNumbers('crash', { start: 0, end: 4 }),
frameRate: 7
});
}
die() {
this.isAlive= false;
this.disableBody();
//Para que esta animación se ejecute es necesario haber puesto el código que evalúa this.estaVivo en el update, para que no se sigan ejecutando el resto de animaciones y comportamientos
this.play('crashAnim');
}
animationComplete(animation, frame, sprite) {
if (animation.key === 'crashAnim') {
this.destroy();
}
}
update() {
if (this.isAlive) {
this.onGround = this.body.onFloor();
if (this.cursors.left.isDown || this.getData('horizontalDirection') === Phaser.LEFT) {
this.leftWalk();
} else if (this.cursors.right.isDown || this.getData('horizontalDirection') === Phaser.RIGHT) {
this.rightWalk();
} else {
this.idle();
}
if (this.cursors.up.isDown || this.getData('isJumping') === Phaser.UP) {
this.jump();
}
}
}
}
Enemigo con Herencia
Creamos una nueva clase llamada Enemy de tal forma que AntEnemy herede de Enemy. Debemos las partes del código de AntEnemy que son comunes a todos los enemigos a la clase Enemy.
Aunque cambiemos la estructura del código interno, en este punto el juego debe seguir haciendo lo mismo.
class Enemy extends Phaser.Phantysics.Arcade.Sprite{
constructor(scene, x, y, sprite ) {
super(scene, x, y, sprite);
scene.physics.add.collider(this, scene.collisionLayer);
scene.add.existing(this);
this.speed = 100;
this.direction = 1;
this.anims.create({
key: 'crashAnim',
frames: this.anims.generateFrameNumbers('crash', { start: 0, end: 4 }),
frameRate: 7
});
this.on('animationcomplete', this.animationComplete, this);
}
animationComplete(animation, frame, sprite) {
if (animation.key === 'crashAnim') {
this.destroy();
}
}
static loadAssets(scene) {
scene.load.spritesheet('crash', crash, { frameWidth: 199, frameHeight: 200 });
}
update() {
this.body.setVelocityX(this.direction * this.speed);
const nextX = Math.floor(this.x / 32) + this.direction;
let nextY = this.y + this.height / 2;
nextY = Math.round(nextY / 32);
const nextTile = this.scene.collisionLayer.hasTileAt(nextX, nextY);
if (!nextTile && this.body.blocked.down) {
this.direction *= -1;
}
if (this.direction > 0) {
this.flipX = false;
} else {
this.flipX = true;
}
}
die() {
this.disableBody();
this.play('crashAnim');
}
}
export default Enemy;
class AntEnemy extends Enemy {
constructor(scene, x, y) {
super(scene, x, y, 'ant');
this.anims.create({
key: 'antWalk',
frames: this.anims.generateFrameNumbers('hormiga', { start: 0, end: 3 }),
frameRate: 7,
repeat: -1,
});
this.play('antWalk');
}
static loadAssets(scene) {
scene.load.spritesheet('ant', ant, { frameWidth: 192, frameHeight: 96 });
scene.load.spritesheet('crash', crash, { frameWidth: 199, frameHeight: 200 });
}
}
export default AntEnemy;
Insertar Oruga
class CaterpillarEnemy extends Enemy {
constructor(scene, x, y) {
super(scene, x, y, 'caterpillar');
this.anims.create({
key: 'caterpillarWalk',
frames: this.anims.generateFrameNumbers('caterpillar', { start: 0, end: 3 }),
frameRate: 7,
repeat: -1,
});
this.play('caterpillarWalk');
}
static loadAssets(scene) {
scene.load.spritesheet('caterpillar', caterpillar, { frameWidth: 96, frameHeight: 192 });
}
}
export default CaterpillarEnemy;
class Platform extends Phaser.Scene {
preload() {
...
CaterpillarEnemy.loadAssets(this);
}
create(){
...
const caterpillarsFromTiled = this.findObjectsByClassInObjectsLayer('caterpillarEnemy', map);
const caterpillars = this.insertBadGuys(this, caterpillarsFromTiled, CaterpillarEnemy);
this.physics.add.overlap(this.player, caterpillars, this.player.checkEnemy, null, this.player);
}
Insertar Avispa
class BeeEnemy extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
super(scene, x, y, 'bee');
scene.add.existing(this);
this.anims.create({
key: 'beeFly',
frames: this.anims.generateFrameNumbers('bee', { start: 0, end: 2 }),
frameRate: 10,
repeat: -1,
});
this.play('beeFly');
}
static loadAssets(scene) {
scene.load.spritesheet('bee', bee, { frameWidth: 128, frameHeight: 128 });
}
}
export default BeeEnemy;
Hacer que la avispa de vueltas
class BeeEnemy extends Phaser.Physics.Arcade.Sprite {
constructor(x, y, scene) {
...
this.flyPath = new Phaser.Curves.Ellipse(x, y, 100, 100);
// this.pathIndex es el grado de completitud de dicha trayetoria. 0 será el punto inicial de la trayectoria circular y 1 el punto final.
this.pathIndex = 0;
this.pathSpeed = 0.001;
this.currentPosition = new Phaser.Math.Vector2();
// La función getPoint aplicada sobre la trayectoria de vuelo (flyPath), modificara el valor de currentPosition para darle unas coordenadas en funcion del grado de completitud de la trayectoria (primer parametro) recibe dos parámetros
this.flyPath.getPoint(this.pathIndex, this.currentPosition);
this.setPosition(this.currentPosition.x, this.currentPosition.y);
}
update() {
// Incrementamos pathIndex, que es el coeficiente que indica el grado de completitud de la trayectoria.
this.pathIndex = Phaser.Math.Wrap(this.pathIndex + this.pathSpeed, 0, 1);
// Alimentamos la variable currentPosition, que estará en función del grado de completitud de la trayectoria.
this.flyPath.getPoint(this.pathIndex, this.currentPosition);
// Modificamos la posición de la avispa en función de las coordenadas x e y del vector.
this.setPosition(this.currentPosition.x, this.currentPosition.y);
}
}
Refactorizar el código para utilizar el estado FLYING y la función checkPlayer
Añadiremos los estados después de la clase Avispa.
class BeeEnemy extends Phaser.Physics.Arcade.Sprite {
// La avispa sólo volará en círculos en el estado volando, así que cambiamos un poco el código del update de la Avispa. De momento, el método checkPlayer se encargará de ejecutar este vuelo circular de la avispa:
update() {
if (this.state === BeeEnemy.FLYING) {
this.checkPlayer();
}
}
checkPlayer(){
this.pathIndex = Phaser.Math.Wrap(this.pathIndex + this.pathSpeed, 0, 1);
this.flyPath.getPoint(this.pathIndex, this.currentPosition);
this.setPosition(this.currentPosition.x, this.currentPosition.y);
}
}
BeeEnemy.FLYING = 0;
Avispa detecta a Player
class BeeEnemy extends Phaser.Physics.Arcade.Sprite {
create(){
...
this.patrolCircle = new Phaser.Geom.Circle(0, 0, 256);
}
checkPlayer(){
...
// Actualizamos la posición del círculo que patrulla la avispa para ver si el player se mete dentro
this.patrolCircle.x = this.x;
this.patrolCircle.y = this.y;
// El jugador ha entrado dentro del area de patrulla de la avispa?
const player = this.scene.player;
if (this.patrolCircle.contains(player.x, player.y)) {
alert('perseguir')
}
}
}
Avispa persigue a Player
class BeeEnemy extends Phaser.Physics.Arcade.Sprite {
constructor(x, y, scene) {
...
this.attackPath = new Phaser.Curves.Line([0, 0, 0, 0]);
}
update(time) {
if (this.state === BeeEnemy.FLYING) {
this.checkPlayer();
} else if (this.state === BeeEnemy.CHASING) {
this.chasePlayer();
}
}
checkPlayer(){
...
const player = this.scene.player;
if (this.patrolCircle.contains(player.x, player.y)) {
this.attackPath.p0.set(this.x, this.y);
this.attackPath.p1.set(player.x, player.y);
this.pathIndex = 0;
this.state = BeeEnemy.CHASING;
}
}
chasePlayer() {
const player = this.scene.player;
this.attackPath.p1.set(player.x, player.y);
this.pathIndex += this.pathSpeed * 2;
this.attackPath.getPoint(this.pathIndex, this.currentPosition);
this.setPosition(this.currentPosition.x, this.currentPosition.y);
if (this.scene.physics.overlap(this, player)) {
alert('ataca');
}
}
}
BeeEnemy.FLYING = 0;
BeeEnemy.CHASING = 1;
Avispa ataca a Player
class BeeEnemy extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
...
this.anims.create({
key: 'beeAttack',
frames: this.anims.generateFrameNumbers('bee', { frames: [ 3, 4, 5, 4, 3 ] }),
frameRate: 10
});
this.on('animationcomplete', this.animationComplete, this);
}
chasePlayer() {
...
if (this.scene.physics.overlap(this, player)) {
this.play('beeAttack', true);
}
}
animationComplete(animation, frame, sprite) {
if (animation.key === 'beeAttack') {
alert("termina ataque");
}
}
}
En vez de un alert, cuando la avispa colisione contra el Player, detonaremos la animación de ataque.
Avispa vuelve a casa
class BeeEnemy extends Phaser.Physics.Arcade.Sprite {
constructor(x, y, scene) {
...
this.startPlace = new Phaser.Math.Vector2(this.currentPosition.x, this.currentPosition.y);
}
checkPlayer(time) {
...
if (this.patrolCircle.contains(player.x, player.y)) {
...
this.timeFromStartingToChase = time;
}
}
update(time) {
...
}else if (this.state === BeeEnemy.RETURNING) {
this.pathIndex += this.pathSpeed * 2;
this.attackPath.getPoint(this.pathIndex, this.currentPosition);
this.setPosition(this.currentPosition.x, this.currentPosition.y);
if (this.pathIndex >= 1){
this.keepFlying();
}
}
}
animationComplete(animation, frame, sprite) {
if (animation.key === 'beeAttack') {
this.returnHome();
}
}
chasePlayer(time){
...
if (!this.patrolCircle.contains(player.x, player.y) && time - this.timeFromStartingToChase > 4000) {
this.returnHome();
}
}
returnHome() {
this.play('beeFly', true);
this.attackPath.p0.set(this.x, this.y);
this.attackPath.p1.set(this.startPlace.x, this.startPlace.y);
this.pathIndex = 0;
this.state = BeeEnemy.RETURNING;
}
keepFlying() {
this.state = BeeEnemy.FLYING;
this.pathIndex = 0;
}
}
BeeEnemy.FLYING= 0;
BeeEnemy.CHASING= 1;
BeeEnemy.RETURNING = 2;
La avispa siempre mira al protagonista
if(this.x < this.scene.player.x){
this.flipX = true;
}else{
this.flipX = false;
}
El heroe recibe daño
chasePlayer() {
...
if (this.scene.physics.overlap(this, player)){
...
player.playerHittedByBee();
}
}
playerHittedByBee(){
alert('golpeado');
}
El Player sale despedido por los aires
playerHittedByBee(){
this.isDazing = true;
this.body.setVelocity(-150,-150);
this.play('fall', true);
}
update() {
if(this.isAlive){
if(!this.isDazing){
//aquí dentro vendría todo el código que ya teníamos de la función update
}
}
}
Reactivar los controles del jugador
playerHittedByBee() {
...
this.scene.time.addEvent({delay: 1000, callback: this.dazeFinished, callbackScope: this});
}
dazeFinished() {
this.isDazing= false;
}
Añadir meta
preload(){
...
this.load.image('goal', goal);
}
create(){
...
const goalFromTiled = this.findObjectsByClassInObjectsLayer('goal', map)[0];
this.putCheckPoint(goalFromTiled.x, goalFromTiled.y, 'goal');
}
putCheckPoint(x, y, sprite) {
const goal = this.physics.add.sprite(x, y, sprite);
goal.body.immovable = true;
goal.body.moves = false;
goal.setSize(160, 160);
}
Fin del juego
putCheckPoint(x, y, sprite) {
...
this.physics.add.overlap(this.player, goal, this.playerReachGoal, null, this);
}
playerReachGoal(){
this.scene.start('Win');
}
Limitando la cámara
this.cameras.main.setBounds(0, 0, 3520, 640);
Escena de perder tras explotar
animationComplete(animation, frame, sprite) {
if (animation.key === 'crashAnim') {
this.scene.scene.start('Lose');
}
}
Escena de perder tras caer por precicipio
./src/scenes/Platform/index.js
this.physics.world.setBoundsCollision(false, false, false, true);
this.physics.world.on('worldbounds', () => {
this.scene.start('Lose');
});
./src/characters/Player/index.js
this.setCollideWorldBounds(true);
this.body.onWorldBounds = true;