GAMES IN HTML5

Phaser 2014: minijuego de fuerza y lanzamiento

Esta entrada cerró el primer año del blog repasando el código del minijuego de lanzamiento que habíamos construido con Phaser. Además de ser un tutorial, es una fotografía bastante clara de cómo escribíamos juegos HTML5 con Phaser 2 en 2014.

Archivo técnico de 2014 · reorganizado en 2026

Conservo las ideas y el código original, pero los presento como documentación de Phaser 2. No deben confundirse con la API actual de Phaser.

Phaser 2014, tutorial histórico de juegos HTML5
Imagen original de la entrada con la que cerramos 2014.

Un año aprendiendo Phaser

Cuando publiqué esta entrada había pasado menos de un año desde el inicio del blog. Habíamos experimentado con física, sprites, input, grupos, partículas y pequeños prototipos. El objetivo de este último artículo era repasar el funcionamiento del minijuego de lanzamiento y explicar sus piezas con más calma.

Phaser 2 · diciembre de 2014

1. Variables e instancia del juego

JavaScriptPhaser 2
var estrellas, balon, stars, flecha;

var game = new Phaser.Game(
  400,
  400,
  Phaser.AUTO,
  "ejemplo",
  {
    preload: preload,
    create: create,
    update: update
  }
);

2. Cargar los recursos

preload()Assets
function preload() {
  game.load.image("star", "S.png");
  game.load.image("ball", "b.png");
  game.load.image("a", "arrow.png");
}

3. Crear física, objetivos e input

La escena inicializaba Arcade Physics, creaba un grupo para las estrellas, añadía la esfera y registraba dos eventos: pulsar y soltar. Las variables presionado, tiempoSostenido y estado controlaban la pequeña máquina de estados del lanzamiento.

create()Fragmento reorganizado
function create() {
  game.stage.setBackgroundColor(0x22aaff);
  game.physics.startSystem(Phaser.Physics.ARCADE);

  stars = game.add.group();
  stars.enableBody = true;
  stars.physicsBodyType = Phaser.Physics.ARCADE;

  balon = game.add.sprite(200, 350, "ball");
  game.physics.arcade.enable(balon);

  balon.body.collideWorldBounds = true;
  balon.body.bounce.setTo(1, 1);

  game.input.onDown.add(presiona, this);
  game.input.onUp.add(suelta, this);

  presionado = false;
  tiempoSostenido = 0;
  estado = null;

  comenzar(0);
}

4. Medir la fuerza de lanzamiento

El prototipo convertía el tiempo que manteníamos presionado el mouse en un valor de fuerza que subía y bajaba dentro de un rango. Era una forma compacta de crear un medidor sin necesitar una interfaz compleja.

InputPresionar, soltar y fuerza
function presiona(pointer) {
  presionado = true;
}

function suelta(pointer) {
  presionado = false;
}

function fuerza() {
  return Math.abs(
    ((game.time.now - tiempoSostenido + 600) % 1000) - 600
  );
}

5. La flecha como hijo de la esfera

La flecha se añadía como child de la esfera para mantener una posición relativa al proyectil. El artículo original ya señalaba un defecto importante: el cuerpo físico de la esfera no coincidía perfectamente con su apariencia, por lo que la rotación visual podía resultar extraña.

JerarquíaPhaser 2 / PIXI
function crearFlecha() {
  flecha = game.add.sprite(0, 0, "a");

  flecha.anchor.setTo(0.5, 0.5);
  flecha.pivot.x = -18;
  flecha.pivot.y = 30;

  balon.addChild(flecha);
}

function removerFlecha() {
  balon.removeChild(flecha);
  flecha.destroy();
}

6. Estados del lanzamiento

El juego alternaba entre dos estados simples: preparar la flecha y dejar que la esfera se moviera. Cuando el proyectil volvía prácticamente a detenerse, se preparaba el siguiente lanzamiento.

EstadoPhaser 2
function comenzar(nuevoEstado) {
  if (estado !== nuevoEstado) {
    if (estado === 1) {
      removerFlecha();
    } else if (estado === 0) {
      balon.body.velocity.setTo(0, 0);
      crearFlecha();
      tiempoSostenido = 0;
    }

    estado = nuevoEstado;
  }
}

7. Update: apuntar, lanzar y recoger estrellas

update()Lógica central
function update() {
  game.physics.arcade.overlap(
    balon,
    stars,
    destruye,
    null,
    this
  );

  if (estado === 0) {
    if (
      Math.abs(balon.body.velocity.x) < 10 &&
      Math.abs(balon.body.velocity.y) < 10
    ) {
      comenzar(1);
    }
  } else if (estado === 1) {
    flecha.rotation =
      game.math.angleBetween(
        balon.body.x,
        balon.body.y,
        game.input.x,
        game.input.y
      ) + Math.PI / 2;

    if (presionado) {
      if (tiempoSostenido === 0) {
        tiempoSostenido = game.time.now;
      } else {
        flecha.scale.y =
          1.0 + fuerza() / 500 * 2;
      }
    } else if (tiempoSostenido !== 0) {
      balon.body.velocity =
        game.physics.arcade.accelerationFromRotation(
          flecha.rotation - Math.PI / 2,
          fuerza() + 50
        );

      comenzar(0);
    }
  }
}

function destruye(a, b) {
  b.kill();
}
Qué sigue siendo útil hoy

Las APIs concretas han cambiado, pero las ideas siguen siendo muy actuales: separar estados, medir input, convertirlo en fuerza, calcular un ángulo, aplicar una velocidad y comprobar colisiones. Esa es justamente la parte que vale la pena conservar de este tutorial.

Phaser hoy

El Phaser que utilizábamos en este código pertenece a otra generación del framework. Phaser continúa activo más de una década después, con Phaser 3.90 disponible y una línea estable de Phaser 4. Si reconstruimos este ejemplo, debería hacerse como un tutorial nuevo en lugar de intentar adaptar línea por línea la API de 2014.

Esta entrada queda como archivo del primer año del blog y como punto de partida para una futura reconstrucción moderna del mismo minijuego.

ADVERTISEMENT
ADVERTISEMENT

No comments:

Post a Comment

Search the blog

Blog archive

Latest in English