Maquetación Web Dinámica / Canvas y videojuegos 2D
Proyecto: Pong completo desde cero
Este es el momento: juntar TODO el módulo — game loop, física, input, colisiones, dibujo — en un videojuego completo y jugable. Pong es el primer videojuego de la historia (1972) y sigue siendo el mejor primer juego que puedes programar: reglas simples, pero cada sistema que aprendiste tiene que funcionar a la vez. Al terminar tendrás un juego de verdad, hecho por ti, línea por línea.
Reto
Pong: tu primer videojuego completo
Qué debes construir
Pong clásico: tu paleta contra una CPU, bola que rebota y acelera, marcador y pantalla de victoria. Así se ve la meta (modo demostración — las dos paletas juegan solas):
<canvas id="attract" width="420" height="280"></canvas>body { font-family: system-ui; padding: 12px; background: #f8fafc; display: grid; place-items: center; }
canvas { border-radius: 12px; border: 1px solid #e2e8f0; max-width: 100%; }const canvas = document.getElementById("attract");
const ctx = canvas.getContext("2d");
const quieto = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const W = canvas.width, H = canvas.height;
const pw = 12, ph = 64;
let py = H/2 - ph/2, cy = H/2 - ph/2;
let bx = W/2, by = H/2, bvx = 3.4, bvy = 2.1, r = 8;
function seguir(actual, objetivo) {
const centro = actual + ph/2;
if (centro < objetivo - 6) return actual + 3;
if (centro > objetivo + 6) return actual - 6 + 6 - 3;
return actual;
}
function draw() {
ctx.fillStyle = "#0b1120"; ctx.fillRect(0, 0, W, H);
ctx.strokeStyle = "#334155"; ctx.lineWidth = 2; ctx.setLineDash([6, 10]);
ctx.beginPath(); ctx.moveTo(W/2, 0); ctx.lineTo(W/2, H); ctx.stroke(); ctx.setLineDash([]);
ctx.fillStyle = "#a78bfa"; ctx.fillRect(16, py, pw, ph);
ctx.fillStyle = "#f1f5f9"; ctx.fillRect(W - 16 - pw, cy, pw, ph);
ctx.fillStyle = "#22c55e"; ctx.beginPath(); ctx.arc(bx, by, r, 0, Math.PI*2); ctx.fill();
ctx.fillStyle = "#64748b"; ctx.font = "600 12px system-ui"; ctx.textAlign = "center";
ctx.fillText("DEMO", W/2, H - 14);
}
function frame() {
bx += bvx; by += bvy;
if (by - r < 0) { by = r; bvy *= -1; }
if (by + r > H) { by = H - r; bvy *= -1; }
const cly = (t, o) => { const c = t + ph/2; return c < o ? Math.min(t + 3.2, H - ph) : Math.max(t - 3.2, 0); };
py = cly(py, by); cy = cly(cy, by);
if (bvx < 0 && bx - r < 16 + pw && by > py && by < py + ph) { bx = 16 + pw + r; bvx *= -1; }
if (bvx > 0 && bx + r > W - 16 - pw && by > cy && by < cy + ph) { bx = W - 16 - pw - r; bvx *= -1; }
if (bx < -r) { bx = W/2; by = H/2; bvx = 3.4; }
if (bx > W + r) { bx = W/2; by = H/2; bvx = -3.4; }
draw();
requestAnimationFrame(frame);
}
if (quieto) draw(); else requestAnimationFrame(frame);Requisitos:
- Dos paletas y una bola: tu paleta (izquierda) sigue el mouse; la CPU (derecha) sigue la bola con velocidad limitada (para que sea ganable).
- Física de la bola: rebota en el techo y el suelo, y en las paletas. Al golpear una paleta, ACELERA un poco y cambia de ángulo según DÓNDE la golpeó (el borde da efecto).
- Marcador: punto cuando la bola sale por un lado; se dibuja arriba.
- Pantalla de victoria: el primero en llegar a 5 gana, con mensaje y opción de reiniciar.
- Sin arranque automático: la bola no se mueve hasta que el jugador interactúa (cortesía de movimiento — nada se anima solo al cargar).
Reglas
- Todo en canvas + JavaScript. Sin librerías.
- Reutiliza los patrones del módulo: el loop
limpiar → actualizar → dibujar, la física de rebote de la lección 4, el input de la 5, la colisión de la 7. - Control por mouse para tu paleta (funciona en cualquier dispositivo con cursor y evita problemas de foco del teclado).
120:00
Cuando estés listo, arranca el reloj.
Qué sigue
Tienes un juego completo bajo el cinturón. El proyecto FINAL del módulo sube la apuesta: tu propio juego, de principio a fin, con menú de inicio, puntaje y game over. Snake, Flappy Bird o Breakout — tú eliges — pero esta vez la arquitectura de estados (menú → jugando → fin) la diseñas tú. El examen del que sale un juego que puedes enseñar con orgullo.