Files
turtle-river-crossing/index.html

584 lines
21 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Schildkröte überquert den Fluss</title>
<style>
body {
margin: 0;
padding: 0;
background: linear-gradient(to bottom, #87CEEB, #E0F7FA);
font-family: 'Arial', sans-serif;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
user-select: none;
}
#game-container {
position: relative;
margin-top: 20px;
}
canvas {
border: 4px solid #333;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
background: linear-gradient(to bottom, #4EC0CA 0%, #87CEEB 100%);
}
#ui {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
color: #333;
font-weight: bold;
font-size: 18px;
text-shadow: 1px 1px 2px white;
}
#game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
display: none;
}
#game-over h2 {
font-size: 48px;
color: #D32F2F;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
#game-over button {
font-size: 24px;
padding: 12px 32px;
border: 3px solid #333;
border-radius: 8px;
background: #4CAF50;
color: white;
cursor: pointer;
transition: all 0.3s;
}
#game-over button:hover {
background: #45a049;
transform: scale(1.05);
}
#instructions {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(255,255,255,0.9);
padding: 12px 24px;
border-radius: 8px;
text-align: center;
border: 2px solid #333;
}
</style>
</head>
<body>
<h1 style="color: #1565C0; margin: 20px 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">
🐢 Schildkröte überquert den Fluss 🐊
</h1>
<div id="game-container">
<canvas id="gameCanvas" width="800" height="600"></canvas>
<div id="ui">
<div>Level: <span id="level">1</span></div>
<div>Punkte: <span id="score">0</span></div>
<div>Leben: <span id="lives">3</span></div>
</div>
<div id="game-over">
<h2 id="game-over-title">Spiel vorbei!</h2>
<p style="font-size: 24px; color: #333;">Dein Ergebnis: <span id="final-score">0</span> Punkte</p>
<button onclick="restartGame()">Nochmal spielen</button>
</div>
</div>
<div id="instructions">
Benutze die Pfeiltasten ⬅️⬆️➡️ zum Bewegen
</div>
<div id="touch-controls" style="display:none; position:fixed; bottom:20px; width:100%; text-align:center; gap:20px;">
<button id="btn-left" style="font-size:32px; padding:20px; background:#333; color:white; border-radius:50%; border:none; width:60px; height:60px;">⬅️</button>
<button id="btn-up" style="font-size:32px; padding:20px; background:#333; color:white; border-radius:50%; border:none; width:60px; height:60px;">⬆️</button>
<button id="btn-down" style="font-size:32px; padding:20px; background:#333; color:white; border-radius:50%; border:none; width:60px; height:60px;">⬇️</button>
<button id="btn-right" style="font-size:32px; padding:20px; background:#333; color:white; border-radius:50%; border:none; width:60px; height:60px;">➡️</button>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Spielzustand
let gameRunning = true;
let score = 0;
let level = 1;
let lives = 3;
// Schildkröte (Spieler)
const player = {
x: canvas.width / 2 - 20,
y: canvas.height - 60,
width: 40,
height: 40,
speed: 5,
onLog: false
};
// Flusslinien (Bäume zum draufspringen)
const logs = [];
const numLogs = 5;
// Bäume (Hindernisse)
const trees = [];
const numTrees = 10;
// Aligatoren (Gegner)
const alligators = [];
const numAlligators = 8;
// Initialisierung
function initGame() {
// Logs (Baumstämme im Wasser)
logs.length = 0;
for (let i = 0; i < numLogs; i++) {
logs.push({
y: canvas.height - 150 - (i * 60),
x: Math.random() * (canvas.width - 100),
width: 80 + Math.random() * 60,
speed: 1 + Math.random() * 1.5,
direction: i % 2 === 0 ? 1 : -1
});
}
// Bäume (Hindernisse im Gras)
trees.length = 0;
for (let i = 0; i < numTrees; i++) {
trees.push({
x: Math.random() * (canvas.width - 50),
y: canvas.height - 120 - Math.random() * 300,
width: 30,
height: 30
});
}
// Aligatoren (Gegner)
alligators.length = 0;
for (let i = 0; i < numAlligators; i++) {
alligators.push({
x: Math.random() * canvas.width,
y: 150 + Math.random() * 250,
width: 60,
height: 30,
speed: 1.5 + Math.random(),
direction: i % 2 === 0 ? 1 : -1
});
}
}
// Tastensteuerung für Desktop
const keys = {};
window.addEventListener('keydown', e => keys[e.key] = true);
window.addEventListener('keyup', e => keys[e.key] = false);
// Touch-Steuerung für Handy
let touchStartX = 0;
let touchStartY = 0;
let touchMoving = false;
canvas.addEventListener('touchstart', e => {
touchMoving = false;
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
e.preventDefault();
}, {passive: false});
canvas.addEventListener('touchmove', e => {
if (!gameRunning) return;
touchMoving = true;
const touchX = e.touches[0].clientX;
const touchY = e.touches[0].clientY;
const deltaX = touchX - touchStartX;
const deltaY = touchY - touchStartY;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
if (deltaX > 10) player.x += player.speed;
if (deltaX < -10) player.x -= player.speed;
} else {
if (deltaY > 10) player.y += player.speed;
if (deltaY < -10) player.y -= player.speed;
}
touchStartX = touchX;
touchStartY = touchY;
e.preventDefault();
}, {passive: false});
canvas.addEventListener('touchend', e => {
touchMoving = false;
e.preventDefault();
}, {passive: false});
setupTouchButtons();
// Touch-Button-Funktionen
function setupTouchButtons() {
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
if (isTouchDevice) {
document.getElementById('touch-controls').style.display = 'flex';
document.getElementById('instructions').style.display = 'none';
}
const btns = {
'btn-left': 'ArrowLeft',
'btn-right': 'ArrowRight',
'btn-up': 'ArrowUp',
'btn-down': 'ArrowDown'
};
Object.keys(btns).forEach(id => {
const btn = document.getElementById(id);
const key = btns[id];
btn.addEventListener('touchstart', (e) => {
e.preventDefault();
keys[key] = true;
});
btn.addEventListener('touchend', (e) => {
e.preventDefault();
keys[key] = false;
});
});
}
// Eingabeverarbeitung
function handleInput() {
if (!gameRunning) return;
// Touch-Steuerung (Vorrang)
if (!touchMoving) {
if (keys['ArrowLeft']) player.x -= player.speed;
if (keys['ArrowRight']) player.x += player.speed;
if (keys['ArrowUp']) player.y -= player.speed;
if (keys['ArrowDown']) player.y += player.speed;
}
// Grenzen
if (player.x < 0) player.x = 0;
if (player.x > canvas.width - player.width) player.x = canvas.width - player.width;
if (player.y < 0) player.y = 0;
if (player.y > canvas.height - player.height) player.y = canvas.height - player.height;
}
// Bäume bewegen
function moveLogs() {
logs.forEach(log => {
log.x += log.speed * log.direction;
if (log.x < -200) log.x = canvas.width;
if (log.x > canvas.width) log.x = -200;
});
}
// Bäume bewegen
function moveLogs() {
logs.forEach(log => {
log.x += log.speed * log.direction;
if (log.x < -200) log.x = canvas.width;
if (log.x > canvas.width) log.x = -200;
});
}
// Aligatoren bewegen
function moveAlligators() {
alligators.forEach(alligator => {
alligator.x += alligator.speed * alligator.direction;
// zurückspringen am Rand
if (alligator.x < -50) alligator.x = canvas.width;
if (alligator.x > canvas.width) alligator.x = -50;
});
}
// Kollisionserkennung
function checkCollisions() {
// Aligatoren
alligators.forEach(alligator => {
if (player.x < alligator.x + alligator.width &&
player.x + player.width > alligator.x &&
player.y < alligator.y + alligator.height &&
player.y + player.height > alligator.y) {
loseLife();
}
});
// Bäume (Hindernisse im Gras)
trees.forEach(tree => {
if (player.x < tree.x + tree.width &&
player.x + player.width > tree.x &&
player.y < tree.y + tree.height &&
player.y + player.height > tree.y) {
loseLife();
}
});
// Bäume im Wasser (Logs) - Spieler bewegt sich mit
player.onLog = false;
logs.forEach(log => {
if (player.y > 100 && player.y < 450) {
if (player.x < log.x + log.width &&
player.x + player.width > log.x &&
player.y + player.height > log.y - 20 &&
player.y < log.y) {
player.x += log.speed * log.direction;
player.onLog = true;
}
}
});
// Ziel erreicht?
if (player.y < 60) {
levelUp();
}
// Ins Wasser fallen?
if (player.y > 100 && player.y < 450 && !player.onLog) {
loseLife();
}
}
function loseLife() {
lives--;
document.getElementById('lives').textContent = lives;
player.x = canvas.width / 2 - 25;
player.y = canvas.height - 70;
if (lives <= 0) {
gameOver();
}
}
function levelUp() {
level++;
score += 100;
document.getElementById('level').textContent = level;
document.getElementById('score').textContent = score;
// Schwierigkeit erhöhen
player.speed += 0.5;
//Reset Player
player.x = canvas.width / 2 - 25;
player.y = canvas.height - 70;
// Aligatoren beschleunigen
alligators.forEach(a => a.speed += 0.3);
}
// Zeichnen
function draw() {
// Hintergrund
ctx.fillStyle = '#4EC0CA';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Startzone (Gras)
ctx.fillStyle = '#4CAF50';
ctx.fillRect(0, canvas.height - 100, canvas.width, 50);
// Grasdetails
ctx.fillStyle = '#388E3C';
for (let i = 0; i < canvas.width; i += 30) {
ctx.fillRect(i, canvas.height - 90, 5, 5);
ctx.fillRect(i + 15, canvas.height - 95, 5, 5);
}
// Zielzone (Gras oben)
ctx.fillStyle = '#4CAF50';
ctx.fillRect(0, 0, canvas.width, 50);
ctx.fillStyle = '#2E7D32';
ctx.fillText('🏆 ZIEL 🏆', canvas.width/2 - 50, 30);
// Grasdetails
ctx.fillStyle = '#388E3C';
for (let i = 0; i < canvas.width; i += 30) {
ctx.fillRect(i, 10, 5, 5);
ctx.fillRect(i + 15, 5, 5, 5);
}
// Flüsse
ctx.fillStyle = '#2196F3';
logs.forEach(log => {
ctx.fillRect(0, log.y - 40, canvas.width, 40);
// Welleneffekt
ctx.fillStyle = '#42A5F5';
ctx.beginPath();
ctx.moveTo(0, log.y - 20);
for (let x = 0; x < canvas.width; x += 20) {
ctx.lineTo(x, log.y - 20 + Math.sin(x + Date.now()/200 + log.y)*5);
}
ctx.lineTo(canvas.width, log.y - 10);
ctx.lineTo(0, log.y - 10);
ctx.fill();
ctx.fillStyle = '#2196F3';
});
// Logs (Baumstämme im Wasser)
logs.forEach(log => {
// Baumstamm
ctx.fillStyle = '#795548';
ctx.fillRect(log.x, log.y - 20, log.width, 20);
// Details
ctx.fillStyle = '#5D4037';
ctx.fillRect(log.x + 5, log.y - 18, 5, 16);
ctx.fillRect(log.x + log.width - 10, log.y - 18, 5, 16);
});
// Aligatoren
alligators.forEach(alligator => {
// Körper
ctx.fillStyle = '#1B5E20';
ctx.beginPath();
ctx.ellipse(alligator.x + alligator.width/2, alligator.y + alligator.height/2, alligator.width/2, alligator.height/2, 0, 0, Math.PI * 2);
ctx.fill();
// Kopf
ctx.beginPath();
ctx.arc(alligator.x, alligator.y + alligator.height/2, 12, 0, Math.PI * 2);
ctx.fill();
// Augen
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(alligator.x + 5, alligator.y + alligator.height/2 - 5, 3, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(alligator.x + 5, alligator.y + alligator.height/2 - 5, 1, 0, Math.PI * 2);
ctx.fill();
// Schuppen
ctx.fillStyle = '#2E7D32';
ctx.beginPath();
ctx.arc(alligator.x + alligator.width/2 - 10, alligator.y + alligator.height/2 + 5, 5, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(alligator.x + alligator.width/2 + 10, alligator.y + alligator.height/2 + 5, 5, 0, Math.PI * 2);
ctx.fill();
});
// Schildkröte
// Panzer
ctx.fillStyle = '#333';
ctx.beginPath();
ctx.ellipse(player.x + player.width/2, player.y + player.height/2, player.width/2, player.height/2, 0, 0, Math.PI * 2);
ctx.fill();
// Muster auf Panzer
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(player.x + player.width/2 - 10, player.y + player.height/2 - 10);
ctx.lineTo(player.x + player.width/2 + 10, player.y + player.height/2 + 10);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(player.x + player.width/2 + 10, player.y + player.height/2 - 10);
ctx.lineTo(player.x + player.width/2 - 10, player.y + player.height/2 + 10);
ctx.stroke();
// Kopf
ctx.fillStyle = '#689F38';
ctx.beginPath();
ctx.arc(player.x + player.width/2, player.y + player.height - 8, 10, 0, Math.PI * 2);
ctx.fill();
// Augen
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(player.x + player.width/2 - 4, player.y + player.height - 10, 3, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(player.x + player.width/2 + 4, player.y + player.height - 10, 3, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(player.x + player.width/2 - 4, player.y + player.height - 10, 1, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(player.x + player.width/2 + 4, player.y + player.height - 10, 1, 0, Math.PI * 2);
ctx.fill();
// Beine
ctx.fillStyle = '#689F38';
ctx.beginPath();
ctx.arc(player.x + 8, player.y + 28, 8, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(player.x + player.width - 8, player.y + 28, 8, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(player.x + 8, player.y + player.height - 12, 8, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(player.x + player.width - 8, player.y + player.height - 12, 8, 0, Math.PI * 2);
ctx.fill();
// Wolkeneffekt (Hintergrund)
ctx.fillStyle = 'rgba(255,255,255,0.6)';
ctx.beginPath();
ctx.arc(100, 100, 30, 0, Math.PI * 2);
ctx.arc(140, 100, 40, 0, Math.PI * 2);
ctx.arc(180, 100, 30, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(canvas.width - 150, 150, 35, 0, Math.PI * 2);
ctx.arc(canvas.width - 100, 150, 45, 0, Math.PI * 2);
ctx.arc(canvas.width - 50, 150, 35, 0, Math.PI * 2);
ctx.fill();
}
// Spiel-Loop
function gameLoop() {
if (!gameRunning) return;
handleInput();
moveAlligators();
moveLogs();
checkCollisions();
draw();
requestAnimationFrame(gameLoop);
}
function gameOver() {
gameRunning = false;
document.getElementById('game-over').style.display = 'block';
document.getElementById('final-score').textContent = score;
}
function restartGame() {
score = 0;
level = 1;
lives = 3;
player.speed = 5;
document.getElementById('level').textContent = level;
document.getElementById('score').textContent = score;
document.getElementById('lives').textContent = lives;
document.getElementById('game-over').style.display = 'none';
// Reset
player.x = canvas.width / 2 - 25;
player.y = canvas.height - 70;
alligators.forEach(a => {
a.speed = 1.5 + Math.random();
a.x = Math.random() * canvas.width;
});
gameRunning = true;
initGame();
gameLoop();
}
// Start
initGame();
gameLoop();
</script>
</body>
</html>