mirror of
https://github.com/thorsten-gehrig/alexa-remote-control.git
synced 2026-07-21 14:58:15 +02:00
Add Snake web app optimized for iOS
Touch-friendly Snake game with swipe controls, d-pad button alternative, pause/resume, high score tracking, and iOS home screen support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AV1WVGPNjtacTHkKWnobPY
This commit is contained in:
@@ -0,0 +1,517 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Snake">
|
||||||
|
<title>Snake</title>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg: #0f172a;
|
||||||
|
--surface: #1e293b;
|
||||||
|
--border: #334155;
|
||||||
|
--text: #f1f5f9;
|
||||||
|
--muted: #94a3b8;
|
||||||
|
--snake: #22c55e;
|
||||||
|
--snake-head: #4ade80;
|
||||||
|
--food: #ef4444;
|
||||||
|
--accent: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro', system-ui, sans-serif;
|
||||||
|
touch-action: none;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
padding: env(safe-area-inset-top) 16px env(safe-area-inset-bottom);
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.high-score {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-wrap {
|
||||||
|
position: relative;
|
||||||
|
background: var(--surface);
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(15, 23, 42, 0.85);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
-webkit-backdrop-filter: blur(6px);
|
||||||
|
gap: 16px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay.hidden { display: none; }
|
||||||
|
|
||||||
|
.overlay h1 {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay .subtitle {
|
||||||
|
font-size: 15px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay .final-score {
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--snake);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
padding: 14px 40px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
transition: transform 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:active { transform: scale(0.95); }
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
". up ."
|
||||||
|
"left down right";
|
||||||
|
grid-template-columns: repeat(3, 64px);
|
||||||
|
grid-template-rows: repeat(2, 64px);
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-btn {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: 14px;
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
background: var(--surface);
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
transition: background 0.1s, transform 0.08s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-btn:active {
|
||||||
|
background: var(--border);
|
||||||
|
transform: scale(0.92);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-up { grid-area: up; }
|
||||||
|
.d-left { grid-area: left; }
|
||||||
|
.d-down { grid-area: down; }
|
||||||
|
.d-right { grid-area: right; }
|
||||||
|
|
||||||
|
.mode-toggle {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-toggle label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-btn {
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-btn.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pause-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<span class="score" id="score">0</span>
|
||||||
|
<span class="high-score" id="highScore">Best: 0</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="canvas-wrap" id="canvasWrap">
|
||||||
|
<canvas id="game"></canvas>
|
||||||
|
|
||||||
|
<div class="overlay" id="startScreen">
|
||||||
|
<h1>Snake</h1>
|
||||||
|
<p class="subtitle">Swipe or use buttons to play</p>
|
||||||
|
<button class="btn" id="startBtn">Play</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overlay hidden" id="gameOverScreen">
|
||||||
|
<h1>Game Over</h1>
|
||||||
|
<div class="final-score" id="finalScore">0</div>
|
||||||
|
<p class="subtitle" id="newRecord" style="display:none;color:var(--snake)">New Record!</p>
|
||||||
|
<button class="btn" id="restartBtn">Play Again</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overlay hidden" id="pauseScreen">
|
||||||
|
<h1>Paused</h1>
|
||||||
|
<button class="btn" id="resumeBtn">Resume</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mode-toggle">
|
||||||
|
<label>Control:</label>
|
||||||
|
<button class="toggle-btn active" id="swipeMode">Swipe</button>
|
||||||
|
<button class="toggle-btn" id="btnMode">Buttons</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls" id="dpad" style="display:none">
|
||||||
|
<button class="d-btn d-up" data-dir="up">▲</button>
|
||||||
|
<button class="d-btn d-left" data-dir="left">◀</button>
|
||||||
|
<button class="d-btn d-down" data-dir="down">▼</button>
|
||||||
|
<button class="d-btn d-right" data-dir="right">▶</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="pause-hint" id="pauseHint" style="display:none">Tap canvas to pause</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const canvas = document.getElementById('game');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const scoreEl = document.getElementById('score');
|
||||||
|
const highScoreEl = document.getElementById('highScore');
|
||||||
|
const startScreen = document.getElementById('startScreen');
|
||||||
|
const gameOverScreen = document.getElementById('gameOverScreen');
|
||||||
|
const pauseScreen = document.getElementById('pauseScreen');
|
||||||
|
const finalScoreEl = document.getElementById('finalScore');
|
||||||
|
const newRecordEl = document.getElementById('newRecord');
|
||||||
|
const dpad = document.getElementById('dpad');
|
||||||
|
const pauseHint = document.getElementById('pauseHint');
|
||||||
|
|
||||||
|
const GRID = 20;
|
||||||
|
let cols, rows, cellSize;
|
||||||
|
let snake, dir, nextDir, food, score, highScore, gameState, speed, lastTick;
|
||||||
|
let useSwipe = true;
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
const maxW = Math.min(window.innerWidth - 32, 400);
|
||||||
|
const maxH = window.innerHeight * (useSwipe ? 0.65 : 0.48);
|
||||||
|
cellSize = Math.floor(Math.min(maxW, maxH) / GRID);
|
||||||
|
cols = GRID;
|
||||||
|
rows = GRID;
|
||||||
|
canvas.width = cols * cellSize;
|
||||||
|
canvas.height = rows * cellSize;
|
||||||
|
document.getElementById('canvasWrap').style.width = canvas.width + 'px';
|
||||||
|
document.getElementById('canvasWrap').style.height = canvas.height + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
highScore = parseInt(localStorage.getItem('snakeHigh') || '0');
|
||||||
|
highScoreEl.textContent = 'Best: ' + highScore;
|
||||||
|
resize();
|
||||||
|
gameState = 'start';
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startGame() {
|
||||||
|
const mid = Math.floor(GRID / 2);
|
||||||
|
snake = [{x: mid, y: mid}, {x: mid - 1, y: mid}, {x: mid - 2, y: mid}];
|
||||||
|
dir = {x: 1, y: 0};
|
||||||
|
nextDir = {x: 1, y: 0};
|
||||||
|
score = 0;
|
||||||
|
speed = 150;
|
||||||
|
scoreEl.textContent = '0';
|
||||||
|
placeFood();
|
||||||
|
gameState = 'playing';
|
||||||
|
startScreen.classList.add('hidden');
|
||||||
|
gameOverScreen.classList.add('hidden');
|
||||||
|
pauseScreen.classList.add('hidden');
|
||||||
|
pauseHint.style.display = 'block';
|
||||||
|
lastTick = performance.now();
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
function placeFood() {
|
||||||
|
const occupied = new Set(snake.map(s => s.x + ',' + s.y));
|
||||||
|
let pos;
|
||||||
|
do {
|
||||||
|
pos = {x: Math.floor(Math.random() * cols), y: Math.floor(Math.random() * rows)};
|
||||||
|
} while (occupied.has(pos.x + ',' + pos.y));
|
||||||
|
food = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loop(now) {
|
||||||
|
if (gameState !== 'playing') return;
|
||||||
|
if (now - lastTick >= speed) {
|
||||||
|
lastTick = now;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
draw();
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
dir = nextDir;
|
||||||
|
const head = {x: snake[0].x + dir.x, y: snake[0].y + dir.y};
|
||||||
|
|
||||||
|
if (head.x < 0 || head.x >= cols || head.y < 0 || head.y >= rows) return gameOver();
|
||||||
|
for (const s of snake) if (s.x === head.x && s.y === head.y) return gameOver();
|
||||||
|
|
||||||
|
snake.unshift(head);
|
||||||
|
|
||||||
|
if (head.x === food.x && head.y === food.y) {
|
||||||
|
score++;
|
||||||
|
scoreEl.textContent = score;
|
||||||
|
if (speed > 70) speed -= 2;
|
||||||
|
placeFood();
|
||||||
|
} else {
|
||||||
|
snake.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
ctx.fillStyle = '#1e293b';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
ctx.strokeStyle = '#263248';
|
||||||
|
ctx.lineWidth = 0.5;
|
||||||
|
for (let i = 1; i < cols; i++) {
|
||||||
|
ctx.beginPath(); ctx.moveTo(i * cellSize, 0); ctx.lineTo(i * cellSize, canvas.height); ctx.stroke();
|
||||||
|
}
|
||||||
|
for (let i = 1; i < rows; i++) {
|
||||||
|
ctx.beginPath(); ctx.moveTo(0, i * cellSize); ctx.lineTo(canvas.width, i * cellSize); ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
const r = cellSize * 0.42;
|
||||||
|
ctx.fillStyle = '#ef4444';
|
||||||
|
ctx.shadowColor = '#ef4444';
|
||||||
|
ctx.shadowBlur = 10;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(food.x * cellSize + cellSize / 2, food.y * cellSize + cellSize / 2, r, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
|
||||||
|
for (let i = snake.length - 1; i >= 0; i--) {
|
||||||
|
const s = snake[i];
|
||||||
|
const pad = 1;
|
||||||
|
const sz = cellSize - pad * 2;
|
||||||
|
const radius = i === 0 ? sz * 0.35 : sz * 0.25;
|
||||||
|
const x = s.x * cellSize + pad;
|
||||||
|
const y = s.y * cellSize + pad;
|
||||||
|
|
||||||
|
ctx.fillStyle = i === 0 ? '#4ade80' : '#22c55e';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + radius, y);
|
||||||
|
ctx.arcTo(x + sz, y, x + sz, y + sz, radius);
|
||||||
|
ctx.arcTo(x + sz, y + sz, x, y + sz, radius);
|
||||||
|
ctx.arcTo(x, y + sz, x, y, radius);
|
||||||
|
ctx.arcTo(x, y, x + sz, y, radius);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
if (i === 0) {
|
||||||
|
ctx.fillStyle = '#0f172a';
|
||||||
|
const eyeR = cellSize * 0.1;
|
||||||
|
const eyeOff = cellSize * 0.22;
|
||||||
|
let e1x = s.x * cellSize + cellSize / 2;
|
||||||
|
let e1y = s.y * cellSize + cellSize / 2;
|
||||||
|
let e2x = e1x, e2y = e1y;
|
||||||
|
if (dir.x === 1) { e1x += eyeOff; e2x += eyeOff; e1y -= eyeOff; e2y += eyeOff; }
|
||||||
|
else if (dir.x === -1) { e1x -= eyeOff; e2x -= eyeOff; e1y -= eyeOff; e2y += eyeOff; }
|
||||||
|
else if (dir.y === -1) { e1x -= eyeOff; e2x += eyeOff; e1y -= eyeOff; e2y -= eyeOff; }
|
||||||
|
else { e1x -= eyeOff; e2x += eyeOff; e1y += eyeOff; e2y += eyeOff; }
|
||||||
|
ctx.beginPath(); ctx.arc(e1x, e1y, eyeR, 0, Math.PI * 2); ctx.fill();
|
||||||
|
ctx.beginPath(); ctx.arc(e2x, e2y, eyeR, 0, Math.PI * 2); ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameOver() {
|
||||||
|
gameState = 'over';
|
||||||
|
pauseHint.style.display = 'none';
|
||||||
|
finalScoreEl.textContent = score;
|
||||||
|
if (score > highScore) {
|
||||||
|
highScore = score;
|
||||||
|
localStorage.setItem('snakeHigh', highScore);
|
||||||
|
highScoreEl.textContent = 'Best: ' + highScore;
|
||||||
|
newRecordEl.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
newRecordEl.style.display = 'none';
|
||||||
|
}
|
||||||
|
gameOverScreen.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDirection(d) {
|
||||||
|
if (gameState !== 'playing') return;
|
||||||
|
const map = {
|
||||||
|
up: {x: 0, y: -1},
|
||||||
|
down: {x: 0, y: 1},
|
||||||
|
left: {x: -1, y: 0},
|
||||||
|
right: {x: 1, y: 0}
|
||||||
|
};
|
||||||
|
const nd = map[d];
|
||||||
|
if (!nd) return;
|
||||||
|
if (nd.x + dir.x === 0 && nd.y + dir.y === 0) return;
|
||||||
|
nextDir = nd;
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePause() {
|
||||||
|
if (gameState === 'playing') {
|
||||||
|
gameState = 'paused';
|
||||||
|
pauseScreen.classList.remove('hidden');
|
||||||
|
} else if (gameState === 'paused') {
|
||||||
|
gameState = 'playing';
|
||||||
|
pauseScreen.classList.add('hidden');
|
||||||
|
lastTick = performance.now();
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swipe detection
|
||||||
|
let touchStart = null;
|
||||||
|
canvas.addEventListener('touchstart', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!useSwipe && (gameState === 'playing' || gameState === 'paused')) {
|
||||||
|
togglePause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
touchStart = {x: e.touches[0].clientX, y: e.touches[0].clientY};
|
||||||
|
}, {passive: false});
|
||||||
|
|
||||||
|
canvas.addEventListener('touchmove', e => e.preventDefault(), {passive: false});
|
||||||
|
|
||||||
|
canvas.addEventListener('touchend', e => {
|
||||||
|
if (!useSwipe || !touchStart) return;
|
||||||
|
const dx = e.changedTouches[0].clientX - touchStart.x;
|
||||||
|
const dy = e.changedTouches[0].clientY - touchStart.y;
|
||||||
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
if (dist < 20) {
|
||||||
|
togglePause();
|
||||||
|
touchStart = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Math.abs(dx) > Math.abs(dy)) {
|
||||||
|
setDirection(dx > 0 ? 'right' : 'left');
|
||||||
|
} else {
|
||||||
|
setDirection(dy > 0 ? 'down' : 'up');
|
||||||
|
}
|
||||||
|
touchStart = null;
|
||||||
|
}, {passive: false});
|
||||||
|
|
||||||
|
// Keyboard
|
||||||
|
document.addEventListener('keydown', e => {
|
||||||
|
const keyMap = {ArrowUp: 'up', ArrowDown: 'down', ArrowLeft: 'left', ArrowRight: 'right',
|
||||||
|
w: 'up', s: 'down', a: 'left', d: 'right'};
|
||||||
|
if (keyMap[e.key]) { e.preventDefault(); setDirection(keyMap[e.key]); }
|
||||||
|
if (e.key === ' ' || e.key === 'Escape') { e.preventDefault(); togglePause(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// D-pad buttons
|
||||||
|
document.querySelectorAll('.d-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('touchstart', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setDirection(btn.dataset.dir);
|
||||||
|
}, {passive: false});
|
||||||
|
btn.addEventListener('click', e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setDirection(btn.dataset.dir);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Control mode toggle
|
||||||
|
document.getElementById('swipeMode').addEventListener('click', () => {
|
||||||
|
useSwipe = true;
|
||||||
|
document.getElementById('swipeMode').classList.add('active');
|
||||||
|
document.getElementById('btnMode').classList.remove('active');
|
||||||
|
dpad.style.display = 'none';
|
||||||
|
resize(); draw();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('btnMode').addEventListener('click', () => {
|
||||||
|
useSwipe = false;
|
||||||
|
document.getElementById('btnMode').classList.add('active');
|
||||||
|
document.getElementById('swipeMode').classList.remove('active');
|
||||||
|
dpad.style.display = 'grid';
|
||||||
|
resize(); draw();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('startBtn').addEventListener('click', startGame);
|
||||||
|
document.getElementById('restartBtn').addEventListener('click', startGame);
|
||||||
|
document.getElementById('resumeBtn').addEventListener('click', togglePause);
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => { resize(); if (gameState !== 'start') draw(); });
|
||||||
|
|
||||||
|
food = {x: 5, y: 5};
|
||||||
|
snake = [{x: 10, y: 10}];
|
||||||
|
dir = {x: 1, y: 0};
|
||||||
|
init();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user