Capítulo 06

Renderizado e integración

Objetivo: dibujar la escena completa, manejar pausa/reinicio y exponer métodos para activar o desactivar el juego desde el contenedor.

1. Fondo y línea central

El render comienza limpiando el canvas y dibujando elementos de fondo. La línea central se construye con segmentos verticales.

Fondo, estrellas y línea central
Fondo
function drawGame() {
    p.background(10, 10, 26);

    for (let s of stars) {
        p.fill(s.b);
        p.noStroke();
        p.ellipse(s.x, s.y, s.s);
    }

    p.stroke(30, 30, 55);
    p.strokeWeight(2);
    for (let y = 0; y < H; y += 16) {
        p.line(W / 2, y, W / 2, y + 8);
    }
    p.noStroke();
}

background() limpia el frame. El ciclo de estrellas dibuja puntos decorativos. La línea central usa p.line() con saltos de 16px para producir un patrón discontinuo.

2. Estela y pelota

La estela usa posiciones anteriores de la pelota. La pelota se dibuja encima para quedar visualmente como elemento principal.

Estela, pelota y halo
Estela y pelota
for (let i = 0; i < trail.length; i++) {
    let a = p.map(i, 0, trail.length, 10, 80);
    p.fill(255, 255, 120, a);
    p.noStroke();
    p.ellipse(trail[i].x, trail[i].y, ballR * 1.2);
}

p.fill(255, 255, 120);
p.ellipse(ball.x, ball.y, ballR * 2);
p.fill(255, 255, 120, 40);
p.ellipse(ball.x, ball.y, ballR * 3.5);

p.map() convierte el índice de la estela en opacidad. La pelota tiene dos círculos: uno sólido y otro semitransparente más grande para generar halo.

3. Paletas y marcador

Las paletas son rectángulos con bordes redondeados. El marcador se dibuja grande y semitransparente en la parte superior.

Paletas y marcador
Paletas y marcador
p.fill(80, 160, 255);
p.rect(player.x, player.y, paddleW, paddleH, 4);

p.fill(255, 80, 80);
p.rect(computer.x, computer.y, paddleW, paddleH, 4);

p.fill(80, 160, 255, 150);
p.textAlign(p.CENTER, p.TOP);
p.textSize(48);
p.text(player.score, W / 4, 20);

p.fill(255, 80, 80, 150);
p.text(computer.score, W * 3 / 4, 20);

El jugador usa azul y la CPU rojo. Los puntajes se ubican a un cuarto y tres cuartos del ancho del canvas.

4. Pausa y overlays

El overlay de pausa se dibuja encima de la escena actual. No reemplaza la escena, solo agrega una capa semitransparente.

Overlay de pausa
Pausa
function drawPaused() {
    p.fill(10, 10, 26, 170);
    p.noStroke();
    p.rect(0, 0, W, H);
    p.fill(167, 139, 250);
    p.textAlign(p.CENTER, p.CENTER);
    p.textSize(32);
    p.text('PAUSED', W / 2, H / 2 - 10);
}

El rectángulo con alfa 170 oscurece la escena. El texto se centra con textAlign(p.CENTER, p.CENTER).

5. Input de pausa y reinicio

P alterna active. R llama reset() y deja el juego activo.

Input global
p.keyPressed = function() {
    if (p.keyCode === p.ESCAPE) return false;

    if (p.key === 'p' || p.key === 'P') {
        active = !active;
        ctx.onStateChange(active, score);
        return false;
    }

    if (p.key === 'r' || p.key === 'R') {
        reset();
        active = true;
        ctx.onStateChange(true, score);
        return false;
    }
};

ctx.onStateChange() informa al contenedor si el juego está activo. return false evita propagación de teclas dentro de p5.js.

6. API de activación

El contenedor puede activar o desactivar el juego llamando métodos expuestos en la instancia p5.

API externa
p.__activateGame = function() {
    active = true;
};

p.__deactivateGame = function() {
    active = false;
};

Estas funciones modifican solo active. La lógica interna del juego sigue encapsulada en el módulo.

7. Loop final

El loop decide si actualizar o solo dibujar pausa. Esta es la estructura final del juego.

Loop final
return function() {
    if (!active) {
        drawGame();
        drawPaused();
        return;
    }

    update();
    drawGame();
};

Si active es falso, se dibuja la escena y encima el overlay de pausa. Si es verdadero, se actualiza la lógica y luego se dibuja el frame.