feat: add Breakout game
This commit is contained in:
146
breakout.html
Normal file
146
breakout.html
Normal 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>
|
||||||
Reference in New Issue
Block a user