302 lines
11 KiB
HTML
302 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
<title>Snake - KinderWelt</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; touch-action: manipulation; }
|
|
body {
|
|
background: linear-gradient(135deg, #1a472a 0%, #2d5016 100%);
|
|
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
|
|
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 5px;
|
|
}
|
|
h1 { color: #4ade80; margin: 5px 0; font-size: 22px; }
|
|
.score-board { color: white; font-size: 16px; margin-bottom: 10px; text-align: center; }
|
|
#gameCanvas {
|
|
background: #0f291e; border: 3px solid #4ade80; border-radius: 8px;
|
|
box-shadow: 0 0 15px rgba(74, 222, 128, 0.3); display: block; margin: 0 auto;
|
|
}
|
|
.main-area { display: flex; flex-direction: column; align-items: center; gap: 10px; }
|
|
|
|
.controls {
|
|
display: flex; gap: 10px; margin: 10px 0; flex-wrap: wrap; justify-content: center;
|
|
}
|
|
.btn {
|
|
background: linear-gradient(135deg, #4ade80, #22c55e); border: none; border-radius: 8px;
|
|
padding: 12px 20px; font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
|
|
color: white; box-shadow: 0 3px 8px rgba(0,0,0,0.3);
|
|
}
|
|
.btn:active { transform: scale(0.95); }
|
|
.btn-back { background: #ff6b6b; text-decoration: none; }
|
|
|
|
/* Big Touch Controls */
|
|
.touch-controls-area {
|
|
display: flex; flex-direction: column; align-items: center;
|
|
gap: 5px; margin-top: 10px;
|
|
}
|
|
.d-pad {
|
|
display: grid;
|
|
grid-template-columns: 80px 80px 80px;
|
|
gap: 8px;
|
|
}
|
|
.d-btn {
|
|
width: 80px; height: 80px;
|
|
background: rgba(255,255,255,0.15);
|
|
border: 3px solid #4ade80;
|
|
border-radius: 15px;
|
|
font-size: 36px;
|
|
color: #4ade80;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
touch-action: manipulation;
|
|
}
|
|
.d-btn:active {
|
|
background: rgba(74, 222, 128, 0.4);
|
|
transform: scale(0.95);
|
|
}
|
|
.d-btn.center {
|
|
background: rgba(74, 222, 128, 0.25);
|
|
font-size: 24px;
|
|
}
|
|
|
|
@media (min-width: 700px) {
|
|
.main-area { flex-direction: row; align-items: flex-start; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>🐍 Snake</h1>
|
|
<div class="score-board">Punkte: <span id="score">0</span> | Highscore: <span id="highscore">0</span></div>
|
|
|
|
<div class="main-area">
|
|
<canvas id="gameCanvas" width="360" height="360"></canvas>
|
|
|
|
|
|
<div class="controls-panel">
|
|
<div class="controls">
|
|
<button class="btn" id="startBtn" onclick="startGame()">▶️ Start</button>
|
|
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
|
|
</div>
|
|
|
|
<!-- Touch Controls -->
|
|
<div class="touch-controls-area">
|
|
<div class="d-pad">
|
|
<div></div>
|
|
<button class="d-btn" ontouchstart="changeDirection('up'); return false;" onclick="changeDirection('up')">▲</button>
|
|
<div></div>
|
|
|
|
<button class="d-btn" ontouchstart="changeDirection('left'); return false;" onclick="changeDirection('left')">◀</button>
|
|
<button class="d-btn center" onclick="startGame()">●</button>
|
|
<button class="d-btn" ontouchstart="changeDirection('right'); return false;" onclick="changeDirection('right')">▶</button>
|
|
|
|
<div></div>
|
|
<button class="d-btn" ontouchstart="changeDirection('down'); return false;" onclick="changeDirection('down')">▼</button>
|
|
<div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const canvas = document.getElementById('gameCanvas');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
// Game variables
|
|
const gridSize = 18;
|
|
const tileCount = canvas.width / gridSize;
|
|
|
|
let snake = [];
|
|
let food = {};
|
|
let direction = 'right';
|
|
let nextDirection = 'right';
|
|
let score = 0;
|
|
let highscore = localStorage.getItem('snake_highscore') || 0;
|
|
let gameRunning = false;
|
|
let gameLoopId;
|
|
|
|
document.getElementById('highscore').textContent = highscore;
|
|
|
|
function initGame() {
|
|
snake = [
|
|
{x: 5, y: 10},
|
|
{x: 4, y: 10},
|
|
{x: 3, y: 10}
|
|
];
|
|
direction = 'right';
|
|
nextDirection = 'right';
|
|
score = 0;
|
|
document.getElementById('score').textContent = score;
|
|
spawnFood();
|
|
}
|
|
|
|
function spawnFood() {
|
|
food = {
|
|
x: Math.floor(Math.random() * tileCount),
|
|
y: Math.floor(Math.random() * tileCount)
|
|
};
|
|
for (let segment of snake) {
|
|
if (segment.x === food.x && segment.y === food.y) {
|
|
spawnFood();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function changeDirection(newDir) {
|
|
if (!gameRunning) {
|
|
startGame();
|
|
return;
|
|
}
|
|
|
|
const opposites = {up: 'down', down: 'up', left: 'right', right: 'left'};
|
|
if (opposites[newDir] !== direction) {
|
|
nextDirection = newDir;
|
|
}
|
|
}
|
|
|
|
function update() {
|
|
if (!gameRunning) return;
|
|
|
|
direction = nextDirection;
|
|
|
|
const head = {...snake[0]};
|
|
|
|
switch(direction) {
|
|
case 'up': head.y--; break;
|
|
case 'down': head.y++; break;
|
|
case 'left': head.x--; break;
|
|
case 'right': head.x++; break;
|
|
}
|
|
|
|
// Wall collision
|
|
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
|
|
gameOver();
|
|
return;
|
|
}
|
|
|
|
// Self collision
|
|
for (let segment of snake) {
|
|
if (head.x === segment.x && head.y === segment.y) {
|
|
gameOver();
|
|
return;
|
|
}
|
|
}
|
|
|
|
snake.unshift(head);
|
|
|
|
// Check food
|
|
if (head.x === food.x && head.y === food.y) {
|
|
score += 10;
|
|
document.getElementById('score').textContent = score;
|
|
spawnFood();
|
|
} else {
|
|
snake.pop();
|
|
}
|
|
}
|
|
|
|
function draw() {
|
|
// Background
|
|
ctx.fillStyle = '#0f291e';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// Food
|
|
ctx.fillStyle = '#ff6b6b';
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
food.x * gridSize + gridSize/2,
|
|
food.y * gridSize + gridSize/2,
|
|
gridSize/2 - 2,
|
|
0, Math.PI * 2
|
|
);
|
|
ctx.fill();
|
|
|
|
// Snake
|
|
snake.forEach((segment, index) => {
|
|
ctx.fillStyle = index === 0 ? '#4ade80' : '#22c55e';
|
|
ctx.fillRect(
|
|
segment.x * gridSize + 1,
|
|
segment.y * gridSize + 1,
|
|
gridSize - 2,
|
|
gridSize - 2
|
|
);
|
|
|
|
// Eyes for head
|
|
if (index === 0) {
|
|
ctx.fillStyle = '#0f291e';
|
|
let eyeX1 = segment.x * gridSize + 4;
|
|
let eyeY1 = segment.y * gridSize + 5;
|
|
let eyeX2 = segment.x * gridSize + 11;
|
|
let eyeY2 = segment.y * gridSize + 5;
|
|
|
|
if (direction === 'up') { eyeY1 -= 2; eyeY2 -= 2; }
|
|
if (direction === 'down') { eyeY1 += 2; eyeY2 += 2; }
|
|
if (direction === 'left') { eyeX1 -= 2; eyeX2 -= 2; }
|
|
if (direction === 'right') { eyeX1 += 2; eyeX2 += 2; }
|
|
|
|
ctx.fillRect(eyeX1, eyeY1, 3, 3);
|
|
ctx.fillRect(eyeX2, eyeY2, 3, 3);
|
|
}
|
|
});
|
|
}
|
|
|
|
function gameLoop() {
|
|
update();
|
|
draw();
|
|
gameLoopId = setTimeout(gameLoop, 100);
|
|
}
|
|
|
|
function startGame() {
|
|
if (gameRunning) {
|
|
// Neustart
|
|
clearTimeout(gameLoopId);
|
|
}
|
|
initGame();
|
|
gameRunning = true;
|
|
document.getElementById('startBtn').textContent = '🔄 Neustart';
|
|
gameLoop();
|
|
}
|
|
|
|
function gameOver() {
|
|
gameRunning = false;
|
|
clearTimeout(gameLoopId);
|
|
|
|
if (score > highscore) {
|
|
highscore = score;
|
|
localStorage.setItem('snake_highscore', highscore);
|
|
document.getElementById('highscore').textContent = highscore;
|
|
}
|
|
|
|
// Draw Game Over
|
|
ctx.fillStyle = 'rgba(0,0,0,0.7)';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
ctx.fillStyle = 'white';
|
|
ctx.font = 'bold 30px Comic Sans MS';
|
|
ctx.textAlign = 'center';
|
|
ctx.fillText('Game Over!', canvas.width/2, canvas.height/2 - 20);
|
|
ctx.font = '20px Comic Sans MS';
|
|
ctx.fillText('Punkte: ' + score, canvas.width/2, canvas.height/2 + 20);
|
|
|
|
document.getElementById('startBtn').textContent = '▶️ Nochmal';
|
|
}
|
|
|
|
// Keyboard controls
|
|
document.addEventListener('keydown', (e) => {
|
|
if (!gameRunning) return;
|
|
|
|
switch(e.key) {
|
|
case 'ArrowUp': case 'w': e.preventDefault(); changeDirection('up'); break;
|
|
case 'ArrowDown': case 's': e.preventDefault(); changeDirection('down'); break;
|
|
case 'ArrowLeft': case 'a': e.preventDefault(); changeDirection('left'); break;
|
|
case 'ArrowRight': case 'd': e.preventDefault(); changeDirection('right'); break;
|
|
}
|
|
});
|
|
|
|
// Initial draw
|
|
initGame();
|
|
draw();
|
|
</script>
|
|
</body>
|
|
</html> |