# ===== CodeLab: game-js-snake ===== # 以下代码片段按文章出现顺序拼接, 共 6 段 # ----- 片段 1 (html) -----

贪吃蛇

得分:0

# ----- 片段 2 (javascript) ----- const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); const SIZE = 20; const COLS = 20, ROWS = 20; let snake = [{ x: 10, y: 10 }]; let dir = { x: 1, y: 0 }; let nextDir = { x: 1, y: 0 }; let food = { x: 15, y: 10 }; let score = 0; function tick() { dir = nextDir; const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y }; snake.unshift(head); if (head.x === food.x && head.y === food.y) { score += 10; document.getElementById('score').textContent = '得分:' + score; spawnFood(); } else { snake.pop(); } draw(); } function draw() { ctx.fillStyle = '#1a1a2e'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#e94560'; ctx.fillRect(food.x * SIZE, food.y * SIZE, SIZE, SIZE); ctx.fillStyle = '#4ecca3'; snake.forEach(seg => { ctx.fillRect(seg.x * SIZE, seg.y * SIZE, SIZE - 1, SIZE - 1); }); } # ----- 片段 3 (javascript) ----- let running = false; let timer = null; function tick() { 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(); } if (snake.some(seg => seg.x === head.x && seg.y === head.y)) { return gameOver(); } snake.unshift(head); if (head.x === food.x && head.y === food.y) { score += 10; document.getElementById('score').textContent = '得分:' + score; spawnFood(); } else { snake.pop(); } draw(); } function gameOver() { clearInterval(timer); running = false; document.getElementById('score').textContent = '游戏结束!得分:' + score; } function spawnFood() { while (true) { const f = { x: Math.floor(Math.random() * COLS), y: Math.floor(Math.random() * ROWS) }; if (!snake.some(seg => seg.x === f.x && seg.y === f.y)) { food = f; return; } } } # ----- 片段 4 (javascript) ----- document.addEventListener('keydown', e => { if (e.key === 'ArrowUp' && dir.y !== 1) nextDir = { x: 0, y: -1 }; if (e.key === 'ArrowDown' && dir.y !== -1) nextDir = { x: 0, y: 1 }; if (e.key === 'ArrowLeft' && dir.x !== 1) nextDir = { x: -1, y: 0 }; if (e.key === 'ArrowRight' && dir.x !== -1) nextDir = { x: 1, y: 0 }; }); let touchStart = null; canvas.addEventListener('touchstart', e => { touchStart = { x: e.touches[0].clientX, y: e.touches[0].clientY }; }); canvas.addEventListener('touchmove', e => { e.preventDefault(); if (!touchStart) return; const dx = e.touches[0].clientX - touchStart.x; const dy = e.touches[0].clientY - touchStart.y; if (Math.abs(dx) < 20 && Math.abs(dy) < 20) return; if (Math.abs(dx) > Math.abs(dy)) { if (dx > 0 && dir.x !== -1) nextDir = { x: 1, y: 0 }; if (dx < 0 && dir.x !== 1) nextDir = { x: -1, y: 0 }; } else { if (dy > 0 && dir.y !== -1) nextDir = { x: 0, y: 1 }; if (dy < 0 && dir.y !== 1) nextDir = { x: 0, y: -1 }; } touchStart = { x: e.touches[0].clientX, y: e.touches[0].clientY }; }); # ----- 片段 5 (javascript) ----- document.addEventListener('keydown', e => { if (e.key === ' ' && running) { e.preventDefault(); if (timer) { clearInterval(timer); timer = null; } else { timer = setInterval(tick, 150); } } }); # ----- 片段 6 (javascript) ----- document.getElementById('start').addEventListener('click', () => { if (running) return; snake = [{ x: 10, y: 10 }]; dir = { x: 1, y: 0 }; nextDir = { x: 1, y: 0 }; score = 0; running = true; document.getElementById('score').textContent = '得分:0'; spawnFood(); timer = setInterval(tick, 150); });