En esta sección, aplicaremos todo lo aprendido a lo largo del curso para construir un juego completo con Phaser. El objetivo es que puedas ver cómo se integran los distintos conceptos y técnicas en un flujo de trabajo real, desde la estructura básica hasta la jugabilidad y la interfaz de usuario.


  1. Estructura Inicial del Proyecto

Conceptos Clave

  • Organización de carpetas y archivos
  • Separación de escenas
  • Carga de recursos

Estructura Recomendada

Carpeta/Archivo Descripción
index.html Archivo principal HTML
src/ Código fuente del juego
src/scenes/ Escenas del juego (Menu, Juego, GameOver)
assets/ Imágenes, sonidos y otros recursos
main.js Punto de entrada y configuración de Phaser

Ejemplo de Estructura

/mi-juego/
│
├── index.html
├── main.js
├── /src/
│   └── /scenes/
│       ├── MenuScene.js
│       ├── GameScene.js
│       └── GameOverScene.js
└── /assets/
    ├── player.png
    ├── enemy.png
    ├── background.png
    └── jump.wav

  1. Configuración de Phaser y Escenas

Paso 1: Configuración Básica

// main.js
import MenuScene from './src/scenes/MenuScene.js';
import GameScene from './src/scenes/GameScene.js';
import GameOverScene from './src/scenes/GameOverScene.js';

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    scene: [MenuScene, GameScene, GameOverScene],
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    }
};

const game = new Phaser.Game(config);

Explicación:

  • Definimos el tamaño del juego, las escenas y la configuración de física.
  • Importamos las escenas que crearemos a continuación.

  1. Creando la Escena de Menú

Paso 2: Menú Principal

// src/scenes/MenuScene.js
export default class MenuScene extends Phaser.Scene {
    constructor() {
        super({ key: 'MenuScene' });
    }

    create() {
        this.add.text(300, 200, 'Mi Juego', { fontSize: '48px', fill: '#fff' });
        const startText = this.add.text(320, 300, 'Presiona ENTER para Jugar', { fontSize: '24px', fill: '#fff' });

        this.input.keyboard.on('keydown-ENTER', () => {
            this.scene.start('GameScene');
        });
    }
}

Explicación:

  • Mostramos el título y una instrucción.
  • Escuchamos la tecla ENTER para iniciar el juego.

  1. Construyendo la Escena Principal del Juego

Paso 3: Lógica Básica del Juego

// src/scenes/GameScene.js
export default class GameScene extends Phaser.Scene {
    constructor() {
        super({ key: 'GameScene' });
    }

    preload() {
        this.load.image('player', 'assets/player.png');
        this.load.image('enemy', 'assets/enemy.png');
        this.load.image('background', 'assets/background.png');
        this.load.audio('jump', 'assets/jump.wav');
    }

    create() {
        this.add.image(400, 300, 'background');
        this.player = this.physics.add.sprite(100, 450, 'player');
        this.player.setCollideWorldBounds(true);

        this.cursors = this.input.keyboard.createCursorKeys();

        this.enemies = this.physics.add.group();
        this.spawnEnemy();

        this.physics.add.overlap(this.player, this.enemies, this.hitEnemy, null, this);

        this.score = 0;
        this.scoreText = this.add.text(16, 16, 'Puntuación: 0', { fontSize: '32px', fill: '#fff' });
    }

    update() {
        if (this.cursors.left.isDown) {
            this.player.setVelocityX(-160);
        } else if (this.cursors.right.isDown) {
            this.player.setVelocityX(160);
        } else {
            this.player.setVelocityX(0);
        }

        if (this.cursors.up.isDown && this.player.body.touching.down) {
            this.player.setVelocityY(-330);
            this.sound.play('jump');
        }
    }

    spawnEnemy() {
        const x = Phaser.Math.Between(800, 1200);
        const enemy = this.enemies.create(x, 500, 'enemy');
        enemy.setVelocityX(-100);
        enemy.setCollideWorldBounds(false);

        // Reaparecer enemigos periódicamente
        this.time.addEvent({
            delay: 2000,
            callback: this.spawnEnemy,
            callbackScope: this
        });
    }

    hitEnemy(player, enemy) {
        this.physics.pause();
        player.setTint(0xff0000);
        this.scene.start('GameOverScene', { score: this.score });
    }
}

Explicación:

  • Cargamos recursos en preload.
  • Creamos el fondo, el jugador y los enemigos.
  • Controlamos el movimiento del jugador con el teclado.
  • Los enemigos aparecen periódicamente y se mueven hacia el jugador.
  • Si el jugador colisiona con un enemigo, el juego termina.

  1. Escena de Game Over

Paso 4: Pantalla de Fin de Juego

// src/scenes/GameOverScene.js
export default class GameOverScene extends Phaser.Scene {
    constructor() {
        super({ key: 'GameOverScene' });
    }

    init(data) {
        this.finalScore = data.score || 0;
    }

    create() {
        this.add.text(300, 200, '¡Game Over!', { fontSize: '48px', fill: '#fff' });
        this.add.text(320, 300, `Puntuación: ${this.finalScore}`, { fontSize: '32px', fill: '#fff' });
        this.add.text(320, 400, 'Presiona ENTER para volver al menú', { fontSize: '24px', fill: '#fff' });

        this.input.keyboard.on('keydown-ENTER', () => {
            this.scene.start('MenuScene');
        });
    }
}

Explicación:

  • Mostramos el mensaje de fin de juego y la puntuación.
  • Permitimos reiniciar el juego presionando ENTER.

  1. Mejoras y Pulido

Ideas para Mejorar

  • Agregar animaciones al jugador y enemigos.
  • Incluir más niveles o dificultad progresiva.
  • Implementar power-ups o vidas extra.
  • Añadir efectos de sonido y música de fondo.
  • Mostrar un HUD más completo (vidas, tiempo, etc.).

  1. Ejercicio Práctico

Ejercicio:
Agrega un sistema de puntuación que aumente cada vez que el jugador esquive un enemigo (por ejemplo, cada vez que un enemigo sale de la pantalla sin colisionar con el jugador).

Pista:
Puedes detectar cuándo un enemigo sale de la pantalla y aumentar la puntuación en ese momento.

Solución Sugerida

// Dentro de GameScene.js, en create():
this.enemies = this.physics.add.group({
    runChildUpdate: true
});

// Modifica spawnEnemy para asignar una función de actualización a cada enemigo:
spawnEnemy() {
    const x = Phaser.Math.Between(800, 1200);
    const enemy = this.enemies.create(x, 500, 'enemy');
    enemy.setVelocityX(-100);
    enemy.setCollideWorldBounds(false);
    enemy.update = () => {
        if (enemy.x < -enemy.width) {
            enemy.destroy();
            this.score += 10;
            this.scoreText.setText('Puntuación: ' + this.score);
        }
    };

    this.time.addEvent({
        delay: 2000,
        callback: this.spawnEnemy,
        callbackScope: this
    });
}

Errores Comunes:

  • Olvidar actualizar el texto de la puntuación.
  • No destruir el enemigo, lo que puede causar pérdida de rendimiento.

Consejo:
Asegúrate de que cada enemigo tenga su propia función de actualización y que el grupo de enemigos tenga runChildUpdate: true.


Conclusión

En esta sección, has aprendido a construir un juego completo paso a paso utilizando Phaser, integrando escenas, sprites, física, entrada del usuario y lógica de juego. Has visto cómo organizar el proyecto, estructurar el código y añadir mejoras. Ahora estás listo para personalizar y expandir tu propio juego, aplicando todo lo aprendido en el curso. ¡En la siguiente sección, aprenderás a pulir y finalizar tu proyecto para compartirlo con el mundo!

Phaser - Desarrollo de Juegos con JavaScript

Módulo 1: Introducción al Desarrollo de Juegos y Phaser

Módulo 2: Fundamentos de Phaser

Módulo 3: Sprites y Animación

Módulo 4: Física e Interactividad en el Juego

Módulo 5: Mundo del Juego y Cámara

Módulo 6: Audio e Interfaz de Usuario

Módulo 7: Arquitectura del Juego y Gestión de Estados

Módulo 8: Características Avanzadas de Jugabilidad

Módulo 9: Despliegue y Optimización

Módulo 10: Proyecto Final

© Copyright 2024. Todos los derechos reservados