feat: add Breakout game

This commit is contained in:
Ferdinand
2026-03-31 14:44:22 +02:00
parent 88ac5d8f52
commit a82e246464

146
breakout.html Normal file
View File

@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Breakout</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<a class="menu-link" href="index.html">← Menü</a>
<h1>BREAKOUT</h1>
<div class="hud">
<div>SCORE <span id="score-display">0</span></div>
<div>LIVES <span id="lives-display">3</span></div>
<div>BEST <span id="hs-display">0</span></div>
</div>
<canvas id="canvas" width="480" height="400"></canvas>
<div class="overlay" id="overlay">
<h2 id="overlay-title">GAME OVER</h2>
<p id="final-score"></p>
<button class="retro-btn" onclick="startGame()">NEU STARTEN</button>
<a class="retro-btn" href="index.html" style="text-decoration:none;text-align:center;">← MENÜ</a>
</div>
<script src="scores.js"></script>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('overlay');
const PADDLE_W = 80, PADDLE_H = 10, PADDLE_Y = 370;
const BALL_R = 8;
const BRICK_COLS = 10, BRICK_ROWS = 5, BRICK_W = 44, BRICK_H = 18, BRICK_PAD = 4, BRICK_TOP = 40;
const BRICK_COLORS = ['#ff6b6b','#ffd93d','#6bcb77','#4d96ff','#a29bfe'];
let paddle, ball, bricks, score, lives, animId, keys;
function createBricks() {
const b = [];
for (let r = 0; r < BRICK_ROWS; r++)
for (let c = 0; c < BRICK_COLS; c++)
b.push({ x: c * (BRICK_W + BRICK_PAD) + BRICK_PAD, y: r * (BRICK_H + BRICK_PAD) + BRICK_TOP, alive: true, color: BRICK_COLORS[r] });
return b;
}
function updateHUD() {
document.getElementById('score-display').textContent = score.toLocaleString('de-DE');
document.getElementById('lives-display').textContent = lives;
document.getElementById('hs-display').textContent = getHighscore('breakout').toLocaleString('de-DE');
}
function showOverlay(title) {
cancelAnimationFrame(animId);
setHighscore('breakout', score);
document.getElementById('overlay-title').textContent = title;
document.getElementById('final-score').textContent = 'SCORE: ' + score.toLocaleString('de-DE');
document.getElementById('hs-display').textContent = getHighscore('breakout').toLocaleString('de-DE');
overlay.style.display = 'flex';
}
function startGame() {
paddle = { x: 200, w: PADDLE_W };
ball = { x: 240, y: 340, vx: 3, vy: -4 };
bricks = createBricks();
score = 0; lives = 3;
keys = {};
overlay.style.display = 'none';
updateHUD();
animId = requestAnimationFrame(loop);
}
function loop() {
// Paddle bewegen
if (keys['ArrowLeft']) paddle.x = Math.max(0, paddle.x - 5);
if (keys['ArrowRight']) paddle.x = Math.min(canvas.width - paddle.w, paddle.x + 5);
// Ball bewegen
ball.x += ball.vx;
ball.y += ball.vy;
// Wände
if (ball.x - BALL_R < 0) { ball.x = BALL_R; ball.vx *= -1; }
if (ball.x + BALL_R > canvas.width) { ball.x = canvas.width - BALL_R; ball.vx *= -1; }
if (ball.y - BALL_R < 0) { ball.y = BALL_R; ball.vy *= -1; }
// Ball verloren
if (ball.y + BALL_R > canvas.height) {
lives--;
updateHUD();
if (lives <= 0) { showOverlay('GAME OVER'); return; }
ball = { x: paddle.x + paddle.w / 2, y: PADDLE_Y - BALL_R - 10, vx: 3 * (Math.random() > 0.5 ? 1 : -1), vy: -4 };
}
// Paddle-Kollision
if (ball.y + BALL_R >= PADDLE_Y && ball.y + BALL_R <= PADDLE_Y + PADDLE_H &&
ball.x >= paddle.x && ball.x <= paddle.x + paddle.w && ball.vy > 0) {
ball.vy *= -1;
const offset = (ball.x - (paddle.x + paddle.w / 2)) / (paddle.w / 2);
ball.vx = offset * 5;
}
// Brick-Kollision
let remaining = 0;
bricks.forEach(b => {
if (!b.alive) return;
remaining++;
if (ball.x + BALL_R > b.x && ball.x - BALL_R < b.x + BRICK_W &&
ball.y + BALL_R > b.y && ball.y - BALL_R < b.y + BRICK_H) {
b.alive = false;
remaining--;
ball.vy *= -1;
score += 10;
updateHUD();
}
});
if (remaining === 0) { showOverlay('GEWONNEN!'); return; }
// Zeichnen
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Paddle
ctx.fillStyle = '#4ecca3';
ctx.fillRect(paddle.x, PADDLE_Y, paddle.w, PADDLE_H);
// Ball
ctx.beginPath();
ctx.arc(ball.x, ball.y, BALL_R, 0, Math.PI * 2);
ctx.fillStyle = '#e0e0e0';
ctx.fill();
// Bricks
bricks.forEach(b => {
if (!b.alive) return;
ctx.fillStyle = b.color;
ctx.fillRect(b.x, b.y, BRICK_W, BRICK_H);
});
animId = requestAnimationFrame(loop);
}
document.addEventListener('keydown', e => { keys[e.key] = true; e.preventDefault(); });
document.addEventListener('keyup', e => { keys[e.key] = false; });
document.getElementById('hs-display').textContent = getHighscore('breakout').toLocaleString('de-DE');
startGame();
</script>
</body>
</html>