<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> body {
background-color: #111;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
font-family: 'Courier New', Courier, monospace;
color: white;
}
canvas {
background-color: #000;
border: 2px solid #fff;
}
<canvas id="gameCanvas" width="480" height="320"></canvas>
// --- CONFIGURACIÓN INICIAL ---
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// --- CONSTANTES DEL JUEGO ---
const PADDLE_WIDTH = 75;
const PADDLE_HEIGHT = 10;
const BALL_RADIUS = 8;
const BRICK_WIDTH = 75;
const BRICK_HEIGHT = 20;
const BRICK_PADDING = 10;
const BRICK_OFFSET_TOP = 30;
const BRICK_OFFSET_LEFT = 30;
const BRICK_ROWS = 3;
const BRICK_COLS = 5;
// --- VARIABLES DEL JUEGO ---
let score = 0;
let lives = 3;
// --- OBJETO PALETA ---
const paddle = {
x: (canvas.width - PADDLE_WIDTH) / 2,
y: canvas.height - PADDLE_HEIGHT - 10,
width: PADDLE_WIDTH,
height: PADDLE_HEIGHT,
speed: 7
};
// --- OBJETO PELOTA ---
const ball = {
x: canvas.width / 2,
y: canvas.height - 30,
dx: 3, // Velocidad en el eje X
dy: -3, // Velocidad en el eje Y
radius: BALL_RADIUS
};
// --- CONTROL DE TECLAS ---
let rightPressed = false;
let leftPressed = false;
// --- INICIALIZACIÓN DE LOS BLOQUES ---
// Creamos una matriz 2D para los bloques
const bricks = [];
for (let r = 0; r < BRICK_ROWS; r++) {
bricks[r] = [];
for (let c = 0; c < BRICK_COLS; c++) {
// status: 1 = visible, 0 = destruido
bricks[r][c] = { x: 0, y: 0, status: 1 };
}
}
// --- EVENT LISTENERS (para el control de la paleta) ---
document.addEventListener("keydown", keyDownHandler);
document.addEventListener("keyup", keyUpHandler);
document.addEventListener("mousemove", mouseMoveHandler); // Opcional: control con ratón
function keyDownHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
rightPressed = true;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
rightPressed = false;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
leftPressed = false;
}
}
// Opcional: control con el ratón
function mouseMoveHandler(e) {
const relativeX = e.clientX - canvas.offsetLeft;
if(relativeX > 0 && relativeX < canvas.width) {
paddle.x = relativeX - paddle.width / 2;
}
}
// --- FUNCIONES DE DIBUJO ---
function drawBall() {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddle.x, paddle.y, paddle.width, paddle.height);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for (let r = 0; r < BRICK_ROWS; r++) {
for (let c = 0; c < BRICK_COLS; c++) {
if (bricks[r][c].status === 1) {
const brickX = c * (BRICK_WIDTH + BRICK_PADDING) + BRICK_OFFSET_LEFT;
const brickY = r * (BRICK_HEIGHT + BRICK_PADDING) + BRICK_OFFSET_TOP;
bricks[r][c].x = brickX;
bricks[r][c].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, BRICK_WIDTH, BRICK_HEIGHT);
// Damos un color diferente a cada fila
ctx.fillStyle = `hsl(${r * 60}, 100%, 50%)`;
ctx.fill();
ctx.closePath();
}
}
}
}
function drawScore() {
ctx.font = "16px Arial";
ctx.fillStyle = "#FFF";
ctx.fillText("Puntuación: " + score, 8, 20);
}
function drawLives() {
ctx.font = "16px Arial";
ctx.fillStyle = "#FFF";
ctx.fillText("Vidas: " + lives, canvas.width - 65, 20);
}
// --- DETECCIÓN DE COLISIONES ---
function collisionDetection() {
for (let r = 0; r < BRICK_ROWS; r++) {
for (let c = 0; c < BRICK_COLS; c++) {
const b = bricks[r][c];
if (b.status === 1) {
if (ball.x > b.x && ball.x < b.x + BRICK_WIDTH && ball.y > b.y && ball.y < b.y + BRICK_HEIGHT) {
ball.dy = -ball.dy; // Invertimos la dirección de la pelota
b.status = 0; // Marcamos el bloque como destruido
score++;
if (score === BRICK_ROWS * BRICK_COLS) {
alert("¡GANASTE! 🎉");
document.location.reload(); // Recargamos la página para empezar de nuevo
}
}
}
}
}
}
// --- BUCLE PRINCIPAL DEL JUEGO ---
function draw() {
// Limpiamos el canvas en cada frame
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Dibujamos todos los elementos
drawBricks();
drawBall();
drawPaddle();
drawScore();
drawLives();
collisionDetection();
// --- LÓGICA DE REBOTE DE LA PELOTA ---
// Rebote en las paredes laterales
if (ball.x + ball.dx > canvas.width - ball.radius || ball.x + ball.dx < ball.radius) {
ball.dx = -ball.dx;
}
// Rebote en la pared superior
if (ball.y + ball.dy < ball.radius) {
ball.dy = -ball.dy;
}
// Rebote en la paleta
else if (ball.y + ball.dy > paddle.y - ball.radius) {
// Comprobamos si la pelota está dentro de los límites horizontales de la paleta
if (ball.x > paddle.x && ball.x < paddle.x + paddle.width) {
// Un toque extra: cambiamos la dirección de la pelota según dónde golpee la paleta
const hitPos = (ball.x - (paddle.x + paddle.width / 2)) / (paddle.width / 2);
ball.dx = hitPos * 5; // Ajusta este valor para cambiar el ángulo
ball.dy = -ball.dy;
} else {
// Si no toca la paleta, pierde una vida
lives--;
if (!lives) {
alert("GAME OVER 😢");
document.location.reload();
} else {
// Reseteamos la posición de la pelota y la paleta
ball.x = canvas.width / 2;
ball.y = canvas.height - 30;
ball.dx = 3;
ball.dy = -3;
paddle.x = (canvas.width - paddle.width) / 2;
}
}
}
// --- MOVIMIENTO DE LA PELOTA Y LA PALETA ---
ball.x += ball.dx;
ball.y += ball.dy;
if (rightPressed && paddle.x < canvas.width - paddle.width) {
paddle.x += paddle.speed;
} else if (leftPressed && paddle.x > 0) {
paddle.x -= paddle.speed;
}
// Llamamos a la función draw() de nuevo para crear el bucle de animación
requestAnimationFrame(draw);
}
// Iniciamos el juego
draw();