Neu: Breakout
This commit is contained in:
parent
dc2b59c282
commit
a54e9cdd60
828
breakout/index.html
Normal file
828
breakout/index.html
Normal file
@ -0,0 +1,828 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Breakout</title>
|
||||
<meta property="og:title" content="Breakout">
|
||||
<meta property="og:description" content="Ein klassisches Breakout-Spiel in HTML">
|
||||
<meta property="og:type" content="game">
|
||||
<meta property="og:url" content="https://tools.ponywave.de/breakout">
|
||||
|
||||
<!-- Umami Tracking -->
|
||||
<script defer src="https://stats.ponywave.de/script" data-website-id="9ef713d2-adb9-4906-9df5-708d8a8b9131" data-tag="breakout"></script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #f0f0f0;
|
||||
--text-color: #333;
|
||||
--primary-color: #7F006E;
|
||||
--secondary-color: #FF7FED;
|
||||
--accent-color: #5a189a;
|
||||
--border-color: #ddd;
|
||||
--paddle-color: #2c3e50;
|
||||
--ball-color: #e74c3c;
|
||||
--modal-bg: rgba(255, 255, 255, 0.95);
|
||||
--modal-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||
--btn-primary: #7F006E;
|
||||
--btn-hover: #FF7FED;
|
||||
--btn-text: #fff;
|
||||
}
|
||||
|
||||
.dark-mode {
|
||||
--bg-color: #222;
|
||||
--text-color: #f0f0f0;
|
||||
--primary-color: #9d4edd;
|
||||
--secondary-color: #c77dff;
|
||||
--accent-color: #7b2cbf;
|
||||
--border-color: #444;
|
||||
--paddle-color: #7b2cbf;
|
||||
--ball-color: #ff5e78;
|
||||
--modal-bg: rgba(30, 30, 30, 0.95);
|
||||
--modal-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||
--btn-primary: #9d4edd;
|
||||
--btn-hover: #c77dff;
|
||||
--btn-text: #fff;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.game-container {
|
||||
margin: 20px auto;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
canvas {
|
||||
background-color: var(--bg-color);
|
||||
border: 2px solid var(--border-color);
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 10px 0 20px;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: var(--btn-primary);
|
||||
color: var(--btn-text);
|
||||
border: none;
|
||||
padding: 8px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: var(--btn-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.modal-overlay.active {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background-color: var(--modal-bg);
|
||||
padding: 2rem;
|
||||
border-radius: 10px;
|
||||
box-shadow: var(--modal-shadow);
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
transform: translateY(-20px);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.modal-overlay.active .modal {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
margin-top: 0;
|
||||
color: var(--primary-color);
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.modal p {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.leaderboard {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: 20px auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.leaderboard th, .leaderboard td {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.leaderboard th {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.leaderboard tr:nth-child(even) {
|
||||
background-color: rgba(128, 128, 128, 0.1);
|
||||
}
|
||||
|
||||
.lives {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.score {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: auto;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info {
|
||||
width: 100%;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Breakout</h1>
|
||||
<button id="themeToggle" class="theme-toggle">🌙</button>
|
||||
</header>
|
||||
|
||||
<div class="controls">
|
||||
<div class="info">
|
||||
<div class="lives">Leben: <span id="lives">3</span></div>
|
||||
<div class="score">Punkte: <span id="score">0</span></div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button id="startBtn" class="btn">Start</button>
|
||||
<button id="leaderboardBtn" class="btn">Bestenliste</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="game-container">
|
||||
<canvas id="gameCanvas" width="600" height="450"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Anleitungs-Modal -->
|
||||
<div id="instructionModal" class="modal-overlay">
|
||||
<div class="modal">
|
||||
<button class="close-modal">×</button>
|
||||
<h2>Spielanleitung</h2>
|
||||
<p>Willkommen bei Breakout! Hier ist, wie man spielt:</p>
|
||||
<ul>
|
||||
<li>Benutze die <strong>Pfeiltasten (Links/Rechts)</strong> um die Plattform zu steuern.</li>
|
||||
<li>Klicke auf <strong>Start</strong> oder drücke die <strong>Leertaste</strong>, um das Spiel zu beginnen.</li>
|
||||
<li>Drücke die <strong>Leertaste</strong> ein zweites Mal, um den Ball zu starten.</li>
|
||||
<li>Zerstöre alle Blöcke, um das Level zu gewinnen.</li>
|
||||
<li>Farbige Blöcke benötigen unterschiedliche Anzahl von Treffern:</li>
|
||||
<ul>
|
||||
<li>Grüne Blöcke: 1 Treffer</li>
|
||||
<li>Blaue Blöcke: 2 Treffer</li>
|
||||
<li>Rote Blöcke: 3 Treffer</li>
|
||||
</ul>
|
||||
<li>Du hast 3 Leben. Wenn der Ball den unteren Bildschirmrand berührt, verlierst du ein Leben.</li>
|
||||
<li>Das Spiel endet, wenn du keine Leben mehr hast.</li>
|
||||
</ul>
|
||||
<p>Viel Spaß beim Spielen!</p>
|
||||
<button id="startGameBtn" class="btn">Verstanden, Los geht's!</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Over Modal -->
|
||||
<div id="gameOverModal" class="modal-overlay">
|
||||
<div class="modal">
|
||||
<h2>Game Over</h2>
|
||||
<p>Dein Punktestand: <span id="finalScore">0</span></p>
|
||||
<button id="saveScoreBtn" class="btn">Zur Bestenliste hinzufügen</button>
|
||||
<button id="restartBtn" class="btn">Nochmal spielen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leaderboard Modal -->
|
||||
<div id="leaderboardModal" class="modal-overlay">
|
||||
<div class="modal">
|
||||
<button class="close-modal">×</button>
|
||||
<h2>Bestenliste</h2>
|
||||
<table class="leaderboard">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rang</th>
|
||||
<th>Punkte</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="leaderboardBody">
|
||||
<!-- Wird durch JavaScript gefüllt -->
|
||||
</tbody>
|
||||
</table>
|
||||
<button id="closeLeaderboardBtn" class="btn">Schließen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<a href="/">Zurück zur Startseite</a> | © <span id="currentYear"></span> Akamaru
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Basis-Spiel-Variablen
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const startBtn = document.getElementById('startBtn');
|
||||
const scoreDisplay = document.getElementById('score');
|
||||
const livesDisplay = document.getElementById('lives');
|
||||
const themeToggle = document.getElementById('themeToggle');
|
||||
const instructionModal = document.getElementById('instructionModal');
|
||||
const gameOverModal = document.getElementById('gameOverModal');
|
||||
const leaderboardModal = document.getElementById('leaderboardModal');
|
||||
const finalScoreDisplay = document.getElementById('finalScore');
|
||||
const playerNameInput = document.getElementById('playerName');
|
||||
const saveScoreBtn = document.getElementById('saveScoreBtn');
|
||||
const restartBtn = document.getElementById('restartBtn');
|
||||
const leaderboardBtn = document.getElementById('leaderboardBtn');
|
||||
const closeLeaderboardBtn = document.getElementById('closeLeaderboardBtn');
|
||||
const leaderboardBody = document.getElementById('leaderboardBody');
|
||||
const startGameBtn = document.getElementById('startGameBtn');
|
||||
const closeModalButtons = document.querySelectorAll('.close-modal');
|
||||
|
||||
// Spielzustand
|
||||
let ball = {
|
||||
x: canvas.width / 2,
|
||||
y: canvas.height - 30,
|
||||
dx: 4,
|
||||
dy: -4,
|
||||
radius: 8,
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--ball-color')
|
||||
};
|
||||
|
||||
let paddle = {
|
||||
width: 100,
|
||||
height: 12,
|
||||
x: (canvas.width - 100) / 2,
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--paddle-color')
|
||||
};
|
||||
|
||||
// Farben und Treffer für jeden Block-Typ
|
||||
const blockTypes = [
|
||||
{ color: '#4CAF50', hits: 1 }, // Grün - 1 Treffer
|
||||
{ color: '#2196F3', hits: 2 }, // Blau - 2 Treffer
|
||||
{ color: '#F44336', hits: 3 } // Rot - 3 Treffer
|
||||
];
|
||||
|
||||
// Blöcke konfiguration
|
||||
const blockRowCount = 5;
|
||||
const blockColumnCount = 8;
|
||||
const blockWidth = 60;
|
||||
const blockHeight = 20;
|
||||
const blockPadding = 10;
|
||||
const blockOffsetTop = 40;
|
||||
const blockOffsetLeft = 30;
|
||||
|
||||
let blocks = [];
|
||||
|
||||
// Spielwerte
|
||||
let score = 0;
|
||||
let lives = 3;
|
||||
let gameStarted = false;
|
||||
let ballReleased = false;
|
||||
let gameOver = false;
|
||||
let highScores = JSON.parse(localStorage.getItem('breakoutHighScores')) || [];
|
||||
|
||||
// Event-Listener für Tastaturbedienung
|
||||
let rightPressed = false;
|
||||
let leftPressed = false;
|
||||
let spacePressed = false;
|
||||
|
||||
// Blöcke initialisieren
|
||||
function initBlocks() {
|
||||
blocks = [];
|
||||
for (let c = 0; c < blockColumnCount; c++) {
|
||||
blocks[c] = [];
|
||||
for (let r = 0; r < blockRowCount; r++) {
|
||||
// Zufälligen Block-Typ auswählen
|
||||
const typeIndex = Math.floor(Math.random() * blockTypes.length);
|
||||
const type = blockTypes[typeIndex];
|
||||
|
||||
blocks[c][r] = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
status: type.hits, // Status = Anzahl der Treffer, die benötigt werden
|
||||
maxHits: type.hits,
|
||||
color: type.color
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spiel initialisieren
|
||||
function init() {
|
||||
initBlocks();
|
||||
resetPaddle();
|
||||
resetBall();
|
||||
score = 0;
|
||||
lives = 3;
|
||||
gameOver = false;
|
||||
ballReleased = false;
|
||||
updateScoreDisplay();
|
||||
updateLivesDisplay();
|
||||
}
|
||||
|
||||
function resetBall() {
|
||||
ball.x = canvas.width / 2;
|
||||
ball.y = canvas.height - paddle.height - ball.radius;
|
||||
// Ball-Geschwindigkeit wird erst beim Loslassen bestimmt
|
||||
ball.dx = 0;
|
||||
ball.dy = 0;
|
||||
ballReleased = false; // Ball ist noch nicht losgelassen
|
||||
}
|
||||
|
||||
function resetPaddle() {
|
||||
paddle.x = (canvas.width - paddle.width) / 2;
|
||||
}
|
||||
|
||||
// Ball zeichnen
|
||||
function drawBall() {
|
||||
ctx.beginPath();
|
||||
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = ball.color;
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
// Paddle zeichnen
|
||||
function drawPaddle() {
|
||||
ctx.beginPath();
|
||||
ctx.rect(paddle.x, canvas.height - paddle.height, paddle.width, paddle.height);
|
||||
ctx.fillStyle = paddle.color;
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
// Blöcke zeichnen
|
||||
function drawBlocks() {
|
||||
for (let c = 0; c < blockColumnCount; c++) {
|
||||
for (let r = 0; r < blockRowCount; r++) {
|
||||
if (blocks[c][r].status > 0) {
|
||||
const blockX = c * (blockWidth + blockPadding) + blockOffsetLeft;
|
||||
const blockY = r * (blockHeight + blockPadding) + blockOffsetTop;
|
||||
|
||||
blocks[c][r].x = blockX;
|
||||
blocks[c][r].y = blockY;
|
||||
|
||||
// Helligkeit abhängig von verbleibenden Treffern anpassen
|
||||
const hitRatio = blocks[c][r].status / blocks[c][r].maxHits;
|
||||
const baseColor = blocks[c][r].color;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.rect(blockX, blockY, blockWidth, blockHeight);
|
||||
ctx.fillStyle = adjustColorBrightness(baseColor, hitRatio);
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = '#333';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Farbe je nach Zustand anpassen
|
||||
function adjustColorBrightness(hexColor, ratio) {
|
||||
// Je niedriger das Verhältnis, desto heller der Block (wurde öfter getroffen)
|
||||
const r = parseInt(hexColor.slice(1, 3), 16);
|
||||
const g = parseInt(hexColor.slice(3, 5), 16);
|
||||
const b = parseInt(hexColor.slice(5, 7), 16);
|
||||
|
||||
// Luminanz anpassen
|
||||
const brightnessAdjust = 0.7 + (0.3 * ratio);
|
||||
|
||||
const adjustedR = Math.floor(r * brightnessAdjust);
|
||||
const adjustedG = Math.floor(g * brightnessAdjust);
|
||||
const adjustedB = Math.floor(b * brightnessAdjust);
|
||||
|
||||
return `rgb(${adjustedR}, ${adjustedG}, ${adjustedB})`;
|
||||
}
|
||||
|
||||
// Kollisionserkennung
|
||||
function collisionDetection() {
|
||||
for (let c = 0; c < blockColumnCount; c++) {
|
||||
for (let r = 0; r < blockRowCount; r++) {
|
||||
const b = blocks[c][r];
|
||||
if (b.status > 0) {
|
||||
if (ball.x > b.x && ball.x < b.x + blockWidth && ball.y > b.y && ball.y < b.y + blockHeight) {
|
||||
ball.dy = -ball.dy;
|
||||
b.status -= 1;
|
||||
if (b.status === 0) {
|
||||
score += b.maxHits * 10; // Punkte basierend auf der Blockstärke
|
||||
updateScoreDisplay();
|
||||
|
||||
// Überprüfen, ob alle Blöcke zerstört wurden
|
||||
if (checkLevelComplete()) {
|
||||
alert("Glückwunsch! Du hast alle Blöcke zerstört!");
|
||||
resetBall();
|
||||
resetPaddle();
|
||||
initBlocks();
|
||||
}
|
||||
} else {
|
||||
// Teilpunkte für Treffer, die den Block nicht komplett zerstören
|
||||
score += 5;
|
||||
updateScoreDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Überprüfen, ob alle Blöcke zerstört wurden
|
||||
function checkLevelComplete() {
|
||||
for (let c = 0; c < blockColumnCount; c++) {
|
||||
for (let r = 0; r < blockRowCount; r++) {
|
||||
if (blocks[c][r].status > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Punkteanzeige aktualisieren
|
||||
function updateScoreDisplay() {
|
||||
scoreDisplay.textContent = score;
|
||||
}
|
||||
|
||||
// Lebenanzeige aktualisieren
|
||||
function updateLivesDisplay() {
|
||||
livesDisplay.textContent = lives;
|
||||
}
|
||||
|
||||
// Ball bewegen
|
||||
function moveBall() {
|
||||
// Wenn der Ball noch nicht losgelassen wurde, folgt er dem Paddle
|
||||
if (!ballReleased) {
|
||||
ball.x = paddle.x + paddle.width / 2;
|
||||
ball.y = canvas.height - paddle.height - ball.radius;
|
||||
return;
|
||||
}
|
||||
|
||||
ball.x += ball.dx;
|
||||
ball.y += ball.dy;
|
||||
|
||||
// Kollision mit Wänden
|
||||
if (ball.x + ball.dx > canvas.width - ball.radius || ball.x + ball.dx < ball.radius) {
|
||||
ball.dx = -ball.dx;
|
||||
}
|
||||
|
||||
// Kollision mit oberem Rand
|
||||
if (ball.y + ball.dy < ball.radius) {
|
||||
ball.dy = -ball.dy;
|
||||
}
|
||||
// Kollision mit unterem Rand (Leben verlieren)
|
||||
else if (ball.y + ball.dy > canvas.height - ball.radius) {
|
||||
// Kollision mit Paddle
|
||||
if (ball.x > paddle.x && ball.x < paddle.x + paddle.width) {
|
||||
// Ball-Richtung abhängig vom Treffpunkt auf dem Paddle beeinflussen
|
||||
const hitPosition = (ball.x - paddle.x) / paddle.width; // 0 bis 1
|
||||
const angle = hitPosition * Math.PI - Math.PI/2; // -90 bis +90 Grad
|
||||
const speed = Math.sqrt(ball.dx * ball.dx + ball.dy * ball.dy);
|
||||
|
||||
ball.dx = Math.cos(angle) * speed;
|
||||
ball.dy = -Math.sin(angle) * speed;
|
||||
|
||||
// Leicht beschleunigen, aber nicht zu stark
|
||||
ball.dx *= 1.002; // Von 1.005 auf 1.002 reduziert
|
||||
ball.dy *= 1.002; // Von 1.005 auf 1.002 reduziert
|
||||
} else {
|
||||
// Leben verlieren
|
||||
lives--;
|
||||
updateLivesDisplay();
|
||||
|
||||
if (lives <= 0) {
|
||||
endGame();
|
||||
} else {
|
||||
resetBall();
|
||||
resetPaddle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Paddle bewegen
|
||||
function movePaddle() {
|
||||
if (rightPressed && paddle.x < canvas.width - paddle.width) {
|
||||
paddle.x += 5;
|
||||
} else if (leftPressed && paddle.x > 0) {
|
||||
paddle.x -= 5;
|
||||
}
|
||||
}
|
||||
|
||||
// Spielschleife
|
||||
function draw() {
|
||||
// Canvas löschen
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Spielelemente zeichnen
|
||||
drawBlocks();
|
||||
drawBall();
|
||||
drawPaddle();
|
||||
|
||||
// Kollisionserkennung
|
||||
collisionDetection();
|
||||
|
||||
if (gameStarted && !gameOver) {
|
||||
// Ball und Paddle bewegen
|
||||
moveBall();
|
||||
movePaddle();
|
||||
}
|
||||
|
||||
// Nächsten Frame anfordern
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
// Spiel starten
|
||||
function startGame() {
|
||||
if (!gameStarted) {
|
||||
gameStarted = true;
|
||||
startBtn.textContent = 'Pause';
|
||||
resetBall(); // Ball zurücksetzen beim Spielstart
|
||||
} else {
|
||||
gameStarted = false;
|
||||
startBtn.textContent = 'Fortsetzen';
|
||||
}
|
||||
}
|
||||
|
||||
// Spiel beenden
|
||||
function endGame() {
|
||||
gameOver = true;
|
||||
gameStarted = false;
|
||||
finalScoreDisplay.textContent = score;
|
||||
showModal(gameOverModal);
|
||||
}
|
||||
|
||||
// Spiel neustarten
|
||||
function restartGame() {
|
||||
hideModal(gameOverModal);
|
||||
init();
|
||||
gameStarted = true;
|
||||
startBtn.textContent = 'Pause';
|
||||
}
|
||||
|
||||
// Punktzahl speichern
|
||||
function saveScore() {
|
||||
highScores.push({ name: "Spieler", score: score });
|
||||
highScores.sort((a, b) => b.score - a.score);
|
||||
highScores = highScores.slice(0, 10); // Nur Top 10 behalten
|
||||
|
||||
localStorage.setItem('breakoutHighScores', JSON.stringify(highScores));
|
||||
|
||||
hideModal(gameOverModal);
|
||||
showLeaderboard();
|
||||
}
|
||||
|
||||
// Bestenliste anzeigen
|
||||
function showLeaderboard() {
|
||||
leaderboardBody.innerHTML = '';
|
||||
|
||||
highScores.forEach((entry, index) => {
|
||||
const row = document.createElement('tr');
|
||||
|
||||
const rankCell = document.createElement('td');
|
||||
rankCell.textContent = index + 1;
|
||||
|
||||
const scoreCell = document.createElement('td');
|
||||
scoreCell.textContent = entry.score;
|
||||
|
||||
row.appendChild(rankCell);
|
||||
row.appendChild(scoreCell);
|
||||
|
||||
leaderboardBody.appendChild(row);
|
||||
});
|
||||
|
||||
showModal(leaderboardModal);
|
||||
}
|
||||
|
||||
// Modal anzeigen
|
||||
function showModal(modal) {
|
||||
modal.classList.add('active');
|
||||
}
|
||||
|
||||
// Modal schließen
|
||||
function hideModal(modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
|
||||
// Theme initialisieren und auf Benutzersystemeinstellungen prüfen
|
||||
function initTheme() {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const savedTheme = localStorage.getItem('breakoutTheme');
|
||||
|
||||
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
|
||||
document.body.classList.add('dark-mode');
|
||||
themeToggle.textContent = '☀️';
|
||||
ball.color = getComputedStyle(document.documentElement).getPropertyValue('--ball-color');
|
||||
paddle.color = getComputedStyle(document.documentElement).getPropertyValue('--paddle-color');
|
||||
}
|
||||
}
|
||||
|
||||
// Theme umschalten
|
||||
function toggleTheme() {
|
||||
document.body.classList.toggle('dark-mode');
|
||||
ball.color = getComputedStyle(document.documentElement).getPropertyValue('--ball-color');
|
||||
paddle.color = getComputedStyle(document.documentElement).getPropertyValue('--paddle-color');
|
||||
|
||||
themeToggle.textContent = document.body.classList.contains('dark-mode') ? '☀️' : '🌙';
|
||||
|
||||
// Speichern der Präferenz
|
||||
const isDark = document.body.classList.contains('dark-mode');
|
||||
localStorage.setItem('breakoutTheme', isDark ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
// Ball loslassen
|
||||
function releaseBall() {
|
||||
if (gameStarted && !ballReleased && !gameOver) {
|
||||
ballReleased = true;
|
||||
// Zufällige Richtung, aber nicht zu steil
|
||||
let angle = Math.random() * Math.PI / 3 - Math.PI / 6; // zwischen -30 und +30 Grad
|
||||
let speed = 2; // Von 3 auf 2 reduziert für langsameren Ball
|
||||
ball.dx = Math.sin(angle) * speed;
|
||||
ball.dy = -Math.cos(angle) * speed;
|
||||
}
|
||||
}
|
||||
|
||||
// Event Listener
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Right' || e.key === 'ArrowRight') {
|
||||
rightPressed = true;
|
||||
} else if (e.key === 'Left' || e.key === 'ArrowLeft') {
|
||||
leftPressed = true;
|
||||
} else if (e.key === ' ') {
|
||||
if (!gameStarted && !gameOver) {
|
||||
startGame();
|
||||
} else if (gameStarted && !ballReleased && !gameOver) {
|
||||
releaseBall();
|
||||
}
|
||||
e.preventDefault(); // Verhindert Seiten-Scrollen bei Leertaste
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', (e) => {
|
||||
if (e.key === 'Right' || e.key === 'ArrowRight') {
|
||||
rightPressed = false;
|
||||
} else if (e.key === 'Left' || e.key === 'ArrowLeft') {
|
||||
leftPressed = false;
|
||||
}
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', startGame);
|
||||
themeToggle.addEventListener('click', toggleTheme);
|
||||
restartBtn.addEventListener('click', restartGame);
|
||||
saveScoreBtn.addEventListener('click', saveScore);
|
||||
leaderboardBtn.addEventListener('click', showLeaderboard);
|
||||
closeLeaderboardBtn.addEventListener('click', () => hideModal(leaderboardModal));
|
||||
startGameBtn.addEventListener('click', () => {
|
||||
hideModal(instructionModal);
|
||||
startGame();
|
||||
});
|
||||
|
||||
closeModalButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const modal = button.closest('.modal-overlay');
|
||||
hideModal(modal);
|
||||
});
|
||||
});
|
||||
|
||||
// Aktuelles Jahr im Footer anzeigen
|
||||
document.getElementById('currentYear').textContent = new Date().getFullYear();
|
||||
|
||||
// Spiel initialisieren und starten
|
||||
init();
|
||||
initTheme(); // Theme automatisch entsprechend den Systemeinstellungen initialisieren
|
||||
showModal(instructionModal); // Anleitung beim Start anzeigen
|
||||
draw(); // Zeichenschleife starten
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -211,6 +211,10 @@
|
||||
<div class="category-section">
|
||||
<h2 class="category-title">🎮 Spiele</h2>
|
||||
<div class="tools-grid">
|
||||
<a href="https://tools.ponywave.de/breakout" class="tool-bubble">
|
||||
<h2 class="tool-title">Breakout</h2>
|
||||
<p class="tool-description">Ein klassisches Breakout-Spiel mit farbigen Blöcken und Highscore-System</p>
|
||||
</a>
|
||||
<a href="https://tools.ponywave.de/emoji_jump" class="tool-bubble">
|
||||
<h2 class="tool-title">Emoji Doodle Jump</h2>
|
||||
<p class="tool-description">Ein Doodle Jump Klon mit Emojis als Charaktere</p>
|
||||
|
@ -5,6 +5,7 @@ https://tools.ponywave.de/text_sorter
|
||||
https://tools.ponywave.de/yt_thumb
|
||||
https://tools.ponywave.de/flash_dl
|
||||
https://tools.ponywave.de/kemonogen
|
||||
https://tools.ponywave.de/breakout
|
||||
https://tools.ponywave.de/emoji_jump
|
||||
https://tools.ponywave.de/2048
|
||||
https://tools.ponywave.de/solitaire
|
||||
|
Loading…
x
Reference in New Issue
Block a user