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.
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.
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.
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.
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.
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.
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.
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.