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.
- 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
- 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.
- 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.
- 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.
- 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.
- 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.).
- 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
- ¿Qué es el Desarrollo de Juegos?
- Descripción general de Phaser
- Configurando tu Entorno de Desarrollo
- Tu Primer Proyecto con Phaser
Módulo 2: Fundamentos de Phaser
- Entendiendo el Bucle de Juego
- Configuración del Juego y Escenas
- Cargando y Mostrando Imágenes
- Trabajando con Texto
- Manejo de Entrada (Teclado y Ratón)
Módulo 3: Sprites y Animación
- ¿Qué son los Sprites?
- Añadiendo y Moviendo Sprites
- Fundamentos de Animación de Sprites
- Grupos de Sprites y Gestión
Módulo 4: Física e Interactividad en el Juego
- Introducción a la Física en Phaser
- Habilitando Física en Sprites
- Colisiones y Superposiciones
- Objetos Interactivos y Eventos
Módulo 5: Mundo del Juego y Cámara
- Creando Tilemaps y Mundos de Juego
- Desplazamiento y Control de Cámara
- Capas y Gestión de Profundidad
Módulo 6: Audio e Interfaz de Usuario
Módulo 7: Arquitectura del Juego y Gestión de Estados
- Organizando el Código del Juego
- Gestionando Múltiples Escenas
- Guardando y Cargando el Estado del Juego
Módulo 8: Características Avanzadas de Jugabilidad
- Efectos de Partículas
- Tweens y Animaciones
- Temporizadores y Eventos Retardados
- IA y Comportamiento de Enemigos