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.
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.
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.
1. Variables e instancia del juego
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
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.
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.
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.
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.
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
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();
}
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.
No comments:
Post a Comment