Todos los capítulos · Capítulo 8/9

Puntos y vidas

Arrancando

Ahora se puede perder. Y se puede ganar.

Texto es una imagen recortada

Un tercer fichero, hud.ts, y una fuente que no es una fuente del sistema:

const font = useLoadFont({
    json: '/hud_font.json',
    atlas: '/hud_font.png',
});

Son dos ficheros. El PNG es una imagen con todas las letras dibujadas en rejilla; el JSON dice en qué coordenadas está cada una. El motor escribe recortando trocitos de esa imagen y pegándolos uno detrás de otro.

Se llama fuente de mapa de bits y es como funcionaba absolutamente todo el texto de las recreativas y las consolas hasta bien entrados los 2000. No hay curvas ni suavizado: cada letra es un puñado de píxeles concretos, que es justo el aspecto que buscamos.

Esta en particular está dibujada píxel a píxel para el tutorial, así que puedes copiarla y usarla donde quieras.

const score = createText({
    text: 'SCORE 0',
    font,
    style: { fontSize: 16 },
    transform: { x: 16, y: 14 },
    tint: getColor('#ede6f5'),
});

fontSize: 16 la pinta a 16 píxeles de alto, el doble de los 8 con los que está dibujada. Como cada letra es un rectángulo de píxeles, se multiplica en enteros y no se emborrona.

Y para cambiar el texto luego, basta con escribir en él:

hud.score.text = `SCORE ${score}`;

Centrar sin adivinar

const message = createText({
    text: '',
    font,
    style: { fontSize: 24 },
    anchor: { x: 0.5, y: 0 },
    transform: { x: width / 2, y: 200 },
    tint: getColor('#ff6b1a'),
    zIndex: 10,
});

anchor dice qué punto del texto cae en x: 0.5 es la mitad de su ancho. Con x en la mitad de la pantalla, el texto queda centrado mida lo que mida, así que GAME OVER y YOU WIN salen centrados los dos aunque tengan distinta longitud, y cambiar el mensaje es solo cambiar su text. No hace falta medir nada. Con x: 1 es su borde derecho, y así es como LIVES se pega a la esquina derecha, sin adivinar cuánto mide.

Va en y: 200, en la franja vacía entre el muro y la pala, y con zIndex: 10, que lo dibuja por encima de todo lo demás. Hace falta: el HUD se crea antes que los ladrillos, y sin eso lo que se crea después se pintaría encima del mensaje.

Tres variables y se acabó

let score = 0;
let lives = 3;
let over = false;

No hace falta nada más elaborado. Viven dentro de la escena, y como el cuerpo de la escena se ejecuta una sola vez, useUpdate las ve siempre actualizadas: es la misma función, recordando su propio entorno.

Perder una vida:

if (ball.transform.y > HEIGHT) {
    lives -= 1;
    hud.lives.text = `LIVES ${Math.max(lives, 0)}`;
    if (lives <= 0) {
        over = true;
        showMessage(hud, 'GAME OVER');
    } else {
        serve();
    }
}

Y ganar, que es una línea:

if (bricks.length === 0) {
    over = true;
    showMessage(hud, 'YOU WIN');
}

Como cada ladrillo se saca de bricks al romperse, la lista vacía es la condición de victoria. No hay que contar nada.

Congelar el juego

useUpdate((dt) => {
    if (over) return;
    // ...
});

return sale de la función inmediatamente. El bucle sigue llamándose sesenta veces por segundo (eso no lo paras), pero no hace nada, así que la última imagen se queda ahí con el mensaje encima.

Es la forma más simple de terminar una partida, y es suficiente.

`let` y `const`: ¿cuál uso?

const es para lo que no cambia:

const PADDLE_W = 64;

let es para lo que sí:

let score = 0;
score += 100;   // esto con const daría error

Regla práctica: escribe const siempre, y cámbialo a let solo cuando el editor te diga que no puedes reasignarlo. Así, ver un let es una señal de «ojo, esto cambia», y no ruido de fondo.

Un detalle que confunde: const impide reasignar el nombre, no modificar lo que hay dentro. Por eso paddle.transform.x += 5 funciona aunque paddle sea const: no estás cambiando qué es paddle, estás cambiando algo suyo.

Ahora tú

Dale más puntos a las filas de arriba. Empieza con una vida. Cambia GAME OVER por algo más tuyo, pero recuerda que la fuente solo tiene mayúsculas, números y : - !.

Y una de verdad, que ya te sale sola: haz que ganar dé mil puntos de bonus.