Tu primer juego con Phaser: atrapa las estrellas (código completo)

Crea un juego 2D completo con Phaser en unas 90 líneas: cesta, estrellas, puntuación y temporizador. Sin imágenes externas.

Juego Atrapa las estrellas hecho con Phaser: cesta y estrellas cayendo

Phaser es una librería de JavaScript para hacer juegos 2D que se ejecutan en el navegador. En esta guía construyes un juego completo, «Atrapa las estrellas»: mueves una cesta con las flechas del teclado, caen estrellas y tienes 30 segundos para atrapar todas las que puedas. Son unas 90 líneas, sin imágenes ni sonidos externos, y cabe en dos archivos.

Cómo se hizo esta guía: el código se escribió con ayuda de IA y se probó en Chrome con Phaser 4.2.1: el movimiento, la puntuación, el final de partida a los 30 segundos y el reinicio con la barra espaciadora funcionan sin errores en la consola. Más sobre nuestro método en cómo usamos la IA.

Qué necesitas

  • Un navegador moderno (Chrome, Firefox, Edge).
  • Un editor de texto cualquiera (Bloc de notas sirve; VS Code es mejor).
  • Conexión a internet para que la página cargue Phaser desde una CDN.
  • No necesitas instalar nada más ni saber de motores de juego.

Paso 1: la página HTML

Crea una carpeta con dos archivos. El primero, index.html, solo carga Phaser y tu juego. Phaser se descarga desde la CDN jsDelivr, fijando la versión con la que probamos este código para que no cambie por sorpresa.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <title>Atrapa las estrellas</title>
  <style>body{margin:0;background:#0d0f24;display:flex;justify-content:center}</style>
</head>
<body>
  <script src="https://cdn.jsdelivr.net/npm/phaser@4.2.1/dist/phaser.min.js"></script>
  <script src="juego.js"></script>
</body>
</html>

Paso 2: la escena y los objetos del juego

El segundo archivo se llama juego.js. En Phaser, un juego se organiza en escenas. Una escena tiene un método create() que se ejecuta una vez al empezar y un método update() que se ejecuta en cada fotograma. Empezamos por create(): dibuja las texturas por código, crea la cesta, el grupo de estrellas, los textos y dos temporizadores.

const DURACION = 30; // segundos de partida

class Juego extends Phaser.Scene {
  constructor() {
    super("Juego");
  }

  create() {
    this.puntos = 0;
    this.tiempo = DURACION;
    this.terminado = false;

    // Texturas dibujadas por código: el juego no necesita archivos de imagen
    const g = this.add.graphics();
    g.fillStyle(0x4fc3f7, 1).fillRect(0, 0, 80, 20);
    g.generateTexture("cesta", 80, 20);
    g.clear();
    g.fillStyle(0xffd54f, 1).fillCircle(12, 12, 12);
    g.generateTexture("estrella", 24, 24);
    g.destroy();

    this.cesta = this.physics.add.image(400, 560, "cesta");
    this.cesta.setCollideWorldBounds(true);
    this.cesta.body.allowGravity = false;
    this.cesta.setImmovable(true);

    this.estrellas = this.physics.add.group();
    this.physics.add.overlap(this.cesta, this.estrellas, this.atrapar, null, this);

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

    this.textoPuntos = this.add.text(16, 16, "Puntos: 0", { fontSize: "24px", color: "#ffffff" });
    this.textoTiempo = this.add.text(784, 16, "Tiempo: " + DURACION, { fontSize: "24px", color: "#ffffff" }).setOrigin(1, 0);

    // Una estrella nueva cada 600 ms
    this.time.addEvent({ delay: 600, loop: true, callback: this.nuevaEstrella, callbackScope: this });
    // El reloj baja cada segundo
    this.time.addEvent({ delay: 1000, loop: true, callback: this.bajarReloj, callbackScope: this });
  }

Qué hace cada bloque:

  • generateTexture convierte un dibujo en una textura reutilizable. Así el juego no depende de archivos de imagen.
  • physics.add.image crea un objeto con física «Arcade», el sistema de colisiones sencillo de Phaser. La cesta no se ve afectada por la gravedad (allowGravity = false) y no puede salir de la pantalla.
  • physics.add.overlap avisa cuando la cesta se solapa con una estrella y llama a atrapar.
  • time.addEvent programa tareas repetidas: crear estrellas y bajar el reloj.

Paso 3: las reglas del juego

Dentro de la misma clase, sigue con los métodos que crean estrellas, suman puntos, gestionan el tiempo y terminan la partida.

nuevaEstrella() {
    if (this.terminado) return;
    const x = Phaser.Math.Between(20, 780);
    const e = this.estrellas.create(x, -20, "estrella");
    e.setVelocityY(Phaser.Math.Between(120, 260));
  }

  atrapar(cesta, estrella) {
    estrella.destroy();
    this.puntos += 1;
    this.textoPuntos.setText("Puntos: " + this.puntos);
  }

  bajarReloj() {
    if (this.terminado) return;
    this.tiempo -= 1;
    this.textoTiempo.setText("Tiempo: " + this.tiempo);
    if (this.tiempo <= 0) this.terminar();
  }

  terminar() {
    this.terminado = true;
    this.estrellas.clear(true, true);
    this.add.text(400, 280, "Fin. Puntos: " + this.puntos, { fontSize: "40px", color: "#ffffff" }).setOrigin(0.5);
    this.add.text(400, 340, "Pulsa ESPACIO para jugar otra vez", { fontSize: "22px", color: "#cccccc" }).setOrigin(0.5);
    this.input.keyboard.once("keydown-SPACE", () => this.scene.restart());
  }

Cada estrella aparece en una posición horizontal al azar y cae a una velocidad también aleatoria. Al terminar, scene.restart() vuelve a ejecutar create() y deja todo a cero.

Paso 4: el movimiento y el arranque

Por último, update() lee el teclado en cada fotograma y mueve la cesta. También elimina las estrellas que se salen por abajo para no acumular objetos. Después de cerrar la clase, creamos el juego con su configuración.

update() {
    if (this.terminado) return;
    const v = 420;
    if (this.teclas.left.isDown) this.cesta.setVelocityX(-v);
    else if (this.teclas.right.isDown) this.cesta.setVelocityX(v);
    else this.cesta.setVelocityX(0);

    // Las estrellas que salen por abajo se eliminan
    this.estrellas.getChildren().forEach((e) => {
      if (e.y > 620) e.destroy();
    });
  }
}

new Phaser.Game({
  type: Phaser.AUTO,
  width: 800,
  height: 600,
  backgroundColor: "#1b1f3b",
  physics: { default: "arcade", arcade: { gravity: { y: 0 } } },
  scene: Juego,
});

Cómo ejecutarlo

Guarda los dos archivos en la misma carpeta y abre index.html con el navegador. Debería aparecer un fondo azul oscuro con estrellas cayendo. Muévete con las flechas izquierda y derecha. Si ves la pantalla en blanco, abre las herramientas del navegador (tecla F12) y mira la pestaña Consola: casi siempre el problema es un nombre de archivo mal escrito.

Ideas para mejorarlo

  • Subir la velocidad de caída a medida que aumentan los puntos.
  • Añadir estrellas rojas que restan puntos.
  • Guardar la mejor puntuación en localStorage.
  • Controles táctiles para móvil.
  • Sustituir los dibujos por imágenes propias con this.load.image.

Cómo usar un asistente de IA para ampliarlo

Si trabajas con un asistente de código, pásale el contenido de juego.js y pide un cambio concreto y pequeño, por ejemplo: «añade estrellas rojas que restan un punto». Prueba el resultado en el navegador antes de pedir lo siguiente. Los cambios pequeños son más fáciles de revisar, y así sabes qué línea rompió el juego si algo falla. Para dejar instrucciones fijas al asistente, mira la guía de CLAUDE.md.

Preguntas frecuentes

¿Necesito saber JavaScript?

Con lo básico (variables, funciones, if y clases) puedes seguir esta guía. Si vienes de Python, la lógica es la misma; cambia la sintaxis.

¿Phaser sirve para juegos grandes?

Sí, se usa para juegos 2D de navegador de distinto tamaño. Para un primer proyecto lo importante es terminar algo pequeño y jugable.

¿Por qué fijar la versión de Phaser en la URL?

Para que el juego siga funcionando aunque salgan versiones nuevas con cambios. Esta guía se probó con la 4.2.1.

¿Puedo publicar mi juego?

Sí: son archivos estáticos. Puedes subirlos a cualquier hosting o a servicios de alojamiento gratuito de páginas.

Siguientes pasos

Si JavaScript es nuevo para ti, repasa antes las bases de programación en la introducción a Python: los conceptos de variables, condiciones y funciones son los mismos.