CREA TU PROPIO VIDEOJUEGO PONG DESDE CERO CON JAVASCRIPT
Introducción al desarrollo de un videojuego clásico con tecnologías web
El desarrollo de videojuegos en el navegador ha evolucionado significativamente gracias a las capacidades de HTML5 y JavaScript. En este tutorial, exploraremos cómo crear un videojuego completo desde cero, específicamente una versión del clásico Pong. Este juego consiste en dos palas que rebotan una pelota, con el objetivo de evitar que pase al lado opuesto. Utilizaremos el elemento canvas de HTML5 para renderizar los gráficos y JavaScript para manejar la lógica del juego.
Esta aproximación permite entender los fundamentos del desarrollo de juegos en el entorno web, incluyendo el bucle de animación, el manejo de eventos y la física básica. Pong representa un excelente punto de partida porque involucra conceptos esenciales como el movimiento continuo, las colisiones y la gestión de puntuación, todo sin depender de bibliotecas externas.
A lo largo de este guía, configuraremos el proyecto inicial, definiremos los elementos visuales, implementaremos el movimiento de las palas y la pelota, detectaremos colisiones y agregaremos funcionalidades para determinar el ganador. El enfoque se mantiene en prácticas actuales de JavaScript, utilizando requestAnimationFrame para un rendimiento óptimo en lugar de métodos obsoletos.
Configuración inicial del proyecto y estructura HTML
Para comenzar, creamos un archivo HTML básico que servirá como base del juego. Este archivo incluye el elemento canvas donde se dibujará todo el contenido y un script para la lógica en JavaScript.
Aquí el código HTML completo:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pong desde Cero</title>
<style>
body {
margin: 0;
background: #000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: Arial, sans-serif;
}
canvas {
background: #111;
display: block;
}
#score {
position: absolute;
top: 20px;
width: 100%;
text-align: center;
color: white;
font-size: 32px;
}
</style>
</head>
<body>
<div id="score">0 - 0</div>
<canvas id="canvas" width="800" height="400"></canvas>
<script>
// Código JavaScript aquí
</script>
</body>
</html>
Este estructura centra el canvas en la pantalla y agrega un div para mostrar la puntuación. El canvas tiene dimensiones fijas de 800x400 píxeles, lo que proporciona un área de juego adecuada.
Obtención del contexto y definición de variables iniciales
En el script de JavaScript, obtenemos el contexto 2D del canvas y definimos las variables necesarias para el juego.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const scoreElement = document.getElementById("score");
const width = canvas.width;
const height = canvas.height;
const paddleWidth = 15;
const paddleHeight = 100;
const ballSize = 15;
let playerY = (height - paddleHeight) / 2;
let aiY = (height - paddleHeight) / 2;
let ballX = width / 2;
let ballY = height / 2;
let ballSpeedX = 5;
let ballSpeedY = 3;
let playerScore = 0;
let aiScore = 0;
let upPressed = false;
let downPressed = false;
Estas variables controlan las posiciones de las palas, la pelota y las velocidades iniciales. La pala del jugador se controla con teclas, mientras que la IA sigue la pelota.
Manejo de entrada del teclado para el jugador
Implementamos eventos para capturar el movimiento de la pala izquierda utilizando las flechas del teclado o teclas W y S para mayor accesibilidad.
document.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp" || e.key === "w") upPressed = true;
if (e.key === "ArrowDown" || e.key === "s") downPressed = true;
});
document.addEventListener("keyup", (e) => {
if (e.key === "ArrowUp" || e.key === "w") upPressed = false;
if (e.key === "ArrowDown" || e.key === "s") downPressed = false;
});
Esto permite un movimiento fluido cuando las teclas se mantienen presionadas.
Implementación del bucle de juego con requestAnimationFrame
El corazón del juego es el bucle que actualiza y dibuja constantemente. Usamos requestAnimationFrame para sincronizar con la tasa de refresco del monitor.
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
function update() {
// Movimiento del jugador
if (upPressed && playerY > 0) playerY -= 8;
if (downPressed && playerY < height - paddleHeight) playerY += 8;
// Movimiento de la IA
if (aiY + paddleHeight / 2 < ballY) aiY += 4;
if (aiY + paddleHeight / 2 > ballY) aiY -= 4;
// Movimiento de la pelota
ballX += ballSpeedX;
ballY += ballSpeedY;
// Colisiones con paredes superior e inferior
if (ballY <= ballSize / 2 || ballY >= height - ballSize / 2) {
ballSpeedY = -ballSpeedY;
}
// Colisiones con palas
if (
ballX <= paddleWidth + ballSize / 2 &&
ballY >= playerY &&
ballY <= playerY + paddleHeight
) {
if (ballSpeedX < 0) {
ballSpeedX = -ballSpeedX * 1.05; // Aumento leve de velocidad
}
}
if (
ballX >= width - paddleWidth - ballSize / 2 &&
ballY >= aiY &&
ballY <= aiY + paddleHeight
) {
if (ballSpeedX > 0) {
ballSpeedX = -ballSpeedX * 1.05;
}
}
// Puntuación y reinicio
if (ballX < 0) {
aiScore++;
resetBall();
}
if (ballX > width) {
playerScore++;
resetBall();
}
scoreElement.textContent = `${playerScore} - ${aiScore}`;
}
function resetBall() {
ballX = width / 2;
ballY = height / 2;
ballSpeedX = -ballSpeedX;
ballSpeedY = Math.random() * 6 - 3;
}
La función update maneja toda la lógica: movimiento, colisiones y puntuación. Al anotar un punto, se reinicia la pelota con una dirección ligeramente aleatoria.
Dibujo de elementos en el canvas
La función draw limpia el canvas y renderiza todos los elementos visuales.
function draw() {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, width, height);
// Línea central
ctx.strokeStyle = "#fff";
ctx.setLineDash([5, 15]);
ctx.beginPath();
ctx.moveTo(width / 2, 0);
ctx.lineTo(width / 2, height);
ctx.stroke();
ctx.setLineDash([]);
// Palas
ctx.fillStyle = "#fff";
ctx.fillRect(10, playerY, paddleWidth, paddleHeight);
ctx.fillRect(width - 10 - paddleWidth, aiY, paddleWidth, paddleHeight);
// Pelota
ctx.beginPath();
ctx.arc(ballX, ballY, ballSize, 0, Math.PI * 2);
ctx.fill();
}
Este dibujo simple mantiene el estilo retro del Pong original. La línea central se crea con guiones para un efecto clásico.
Mejora de la inteligencia artificial y dificultad progresiva
Para hacer el juego más desafiante, la IA ajusta su velocidad según el nivel. Además, aumentamos gradualmente la velocidad de la pelota.
En la función update, modificamos el movimiento de la IA para que sea más precisa en niveles avanzados. La detección de colisiones precisa permite variar el ángulo de rebote dependiendo de dónde impacta la pelota en la pala.
Ejemplo de cálculo de rebote avanzado:
// Dentro de colisión con pala izquierda
let relativeIntersectY = playerY + paddleHeight / 2 - ballY;
let normalizedIntersectY = relativeIntersectY / (paddleHeight / 2);
ballSpeedY = -normalizedIntersectY * 8; // Máxima velocidad vertical
ballSpeedX = Math.abs(ballSpeedX) * 1.05;
Esto hace que el rebote dependa de la posición de impacto, agregando estrategia al juego.
Agregado de efectos visuales y sonoros básicos
Aunque mantenemos simplicidad, incorporamos efectos básicos. Para sonido, usamos el API Audio.
const hitSound = new Audio(
"data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0Y..."
); // Base64 simple o URL
// En colisión
hitSound.currentTime = 0;
hitSound.play();
Para efectos visuales, agregamos un rastro a la pelota modificando el composite operation.
ctx.globalCompositeOperation = "source-over";
// Para rastro
ctx.fillStyle = "rgba(255,255,255,0.1)";
ctx.fillRect(0, 0, width, height);
Estos detalles mejoran la inmersión sin complicar el código.
Optimizaciones y consideraciones de rendimiento actuales
En 2026, las mejores prácticas recomiendan usar requestAnimationFrame exclusivamente para animaciones fluidas. Evitamos setInterval por su falta de sincronización. Además, limitamos las operaciones de dibujo y usamos offscreen canvas si el juego crece en complejidad.
El rendimiento en dispositivos móviles se beneficia de dimensiones responsivas, aunque aquí mantenemos fijas para simplicidad.
Extensión del juego con modos adicionales
Podemos agregar un modo dos jugadores cambiando el control de la pala derecha a teclas como I y K.
También implementamos un límite de puntos para victoria, por ejemplo 10.
const winningScore = 10;
if (playerScore >= winningScore || aiScore >= winningScore) {
alert(`¡${playerScore >= winningScore ? "Jugador" : "IA"} gana!`);
playerScore = 0;
aiScore = 0;
}
Esto proporciona cierre al juego.
Conclusiones
Crear un videojuego como Pong desde cero con HTML5 Canvas y JavaScript demuestra los fundamentos sólidos del desarrollo web interactivo. Hemos cubierto desde la configuración inicial hasta la lógica completa, incluyendo movimiento fluido de elementos, detección de colisiones avanzada, puntuación y mejoras progresivas.
Este proyecto sirve como base para explorar conceptos más complejos, como física detallada o multiplayer vía WebSockets. La accesibilidad del canvas permite ejecutar el juego en cualquier navegador moderno sin instalaciones adicionales.
Al dominar estos principios, se abre la puerta a crear experiencias interactivas más ambiciosas en el ecosistema web. Experimenta modificando velocidades, agregando power-ups o implementando gráficos más elaborados para personalizar tu versión.