从零构建H5贪吃蛇游戏:HTML+CSS+JavaScript实战解析

张开发
2026/4/15 21:16:32 15 分钟阅读

分享文章

从零构建H5贪吃蛇游戏:HTML+CSS+JavaScript实战解析
1. 准备工作搭建基础HTML结构第一次接触前端开发时我最头疼的就是不知道从哪开始。后来发现就像盖房子要先打地基一样做网页游戏也得先搭建好HTML骨架。这个贪吃蛇游戏只需要最基本的HTML结构完全不用担心复杂。我们先创建一个标准的HTML5文档框架。建议直接用VS Code新建文件保存为snake.html。这里有个小技巧输入!然后按Tab键编辑器会自动生成HTML5基础模板省去手动输入的麻烦。我刚开始学的时候不知道这个功能每次都傻乎乎地手敲DOCTYPE声明。游戏界面我们使用canvas元素来实现这是HTML5专门为图形处理提供的画板。设置画布尺寸时要注意宽900px高600px是为了适配常见的显示器分辨率。记得给canvas加个边框这样游戏区域看起来更清晰。下面是我调整过多次后觉得最顺眼的样式!DOCTYPE html html head title贪吃蛇/title meta charsetUTF-8 style body { display: flex; justify-content: center; background: #f0f0f0; } .game-container { margin-top: 50px; border: 10px solid #333; border-radius: 5px; box-shadow: 0 0 20px rgba(0,0,0,0.2); } canvas { background: #aad751; display: block; } /style /head body div classgame-container canvas idgameCanvas width600 height600/canvas /div script srcgame.js/script /body /html这里有几个实用细节body使用flex布局让游戏区自动居中给容器加阴影增强立体感canvas背景用浅绿色模拟经典贪吃蛇游戏的草地效果。这些视觉优化虽然不影响功能但能让游戏看起来更专业。2. 游戏界面绘制Canvas基础运用刚开始用canvas时我总把它想象成一块真正的画布。ctx就像是手中的画笔fillRect就是画方块的命令。贪吃蛇游戏的核心就是不断地用方块拼出蛇身和食物。我们先获取canvas的绘图上下文。这里有个坑要注意必须在页面完全加载后才能获取canvas元素否则会报null错误。我当初就因为把script标签放在head里调试了半天。// 等页面加载完成再执行 window.onload function() { const canvas document.getElementById(gameCanvas); const ctx canvas.getContext(2d); // 网格背景绘制 function drawGrid() { ctx.strokeStyle #8aad5a; ctx.lineWidth 1; // 竖线 for(let i 0; i 600; i 20) { ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, 600); ctx.stroke(); } // 横线 for(let j 0; j 600; j 20) { ctx.beginPath(); ctx.moveTo(0, j); ctx.lineTo(600, j); ctx.stroke(); } } drawGrid(); };蛇身和食物都是用fillRect方法绘制的方块。我建议把每个方块设为20x20像素这样600x600的画布正好分成30x30的网格。这个尺寸计算起来方便玩起来也不会觉得太拥挤。绘制食物时可以用fillStyle设置不同颜色。我习惯用红色表示食物深绿色表示蛇身蛇头则用更亮的颜色区分。这样玩家一眼就能看清游戏元素// 绘制食物 function drawFood() { ctx.fillStyle red; ctx.fillRect(food.x * 20, food.y * 20, 20, 20); } // 绘制蛇 function drawSnake() { ctx.fillStyle green; snake.forEach((segment, index) { if(index snake.length - 1) { ctx.fillStyle lightgreen; // 蛇头特殊颜色 } ctx.fillRect(segment.x * 20, segment.y * 20, 20, 20); }); }3. 游戏逻辑实现蛇的移动与控制让蛇动起来是游戏最核心的部分。我最初实现时遇到了两个问题一是蛇身移动不连贯二是按键响应有延迟。后来发现是因为没处理好游戏循环和键盘事件的配合。蛇的移动原理其实很简单在移动方向添加一个新蛇头去掉尾部一节。用数组表示蛇身的话就是push新坐标后shift掉第一个元素let snake [ {x: 10, y: 10}, {x: 11, y: 10}, {x: 12, y: 10} ]; let direction right; function moveSnake() { const head {...snake[snake.length - 1]}; switch(direction) { case up: head.y - 1; break; case down: head.y 1; break; case left: head.x - 1; break; case right: head.x 1; break; } snake.push(head); snake.shift(); }控制蛇转向要监听键盘事件。这里有个重要细节要防止180度急转弯比如正在向右移动时不能直接转向左。我加了个方向锁解决了这个问题let nextDirection right; document.addEventListener(keydown, e { switch(e.key) { case ArrowUp: if(direction ! down) nextDirection up; break; case ArrowDown: if(direction ! up) nextDirection down; break; case ArrowLeft: if(direction ! right) nextDirection left; break; case ArrowRight: if(direction ! left) nextDirection right; break; } }); // 在游戏循环中更新方向 function gameLoop() { direction nextDirection; moveSnake(); // 其他逻辑... }游戏循环建议用requestAnimationFrame实现它比setInterval更流畅会自动匹配屏幕刷新率。我设置的150ms移动一次速度适中初学者可以调整这个值改变游戏难度let lastRenderTime 0; const SPEED 150; // 毫秒 function main(currentTime) { window.requestAnimationFrame(main); if(currentTime - lastRenderTime SPEED) return; lastRenderTime currentTime; updateGame(); drawGame(); } window.requestAnimationFrame(main);4. 游戏机制完善碰撞检测与得分完整的游戏需要能吃食物变长、撞墙/撞自己结束游戏等功能。这些都属于碰撞检测的范畴是游戏开发中最常见的需求之一。检测蛇是否吃到食物很简单只需判断蛇头坐标是否与食物坐标重合。我最初实现的食物生成没有考虑避开蛇身导致食物可能出现在蛇肚子里后来加了个校验逻辑let food {x: 5, y: 5}; function generateFood() { let newFood; do { newFood { x: Math.floor(Math.random() * 30), y: Math.floor(Math.random() * 30) }; } while(snake.some(segment segment.x newFood.x segment.y newFood.y )); food newFood; } function checkEatFood() { const head snake[snake.length - 1]; if(head.x food.x head.y food.y) { snake.unshift({...snake[0]}); // 在尾部添加一节 generateFood(); updateScore(); } }死亡检测包括撞墙和撞自己两种情况。撞墙检测就是判断蛇头是否超出画布边界撞自己则是检查蛇头坐标是否与任何一节蛇身重合function checkDeath() { const head snake[snake.length - 1]; // 撞墙检测 if(head.x 0 || head.x 30 || head.y 0 || head.y 30) { gameOver(); return; } // 撞自己检测跳过蛇头 for(let i 0; i snake.length - 1; i) { if(head.x snake[i].x head.y snake[i].y) { gameOver(); return; } } } function gameOver() { alert(游戏结束得分${snake.length - 3}); // 重置游戏状态 snake [ {x: 10, y: 10}, {x: 11, y: 10}, {x: 12, y: 10} ]; direction right; nextDirection right; generateFood(); }得分系统可以简单用蛇身长度表示。我建议初始长度为3所以实际得分是长度减3。如果想更专业可以添加localStorage存储最高分let highScore localStorage.getItem(snakeHighScore) || 0; function updateScore() { const currentScore snake.length - 3; if(currentScore highScore) { highScore currentScore; localStorage.setItem(snakeHighScore, highScore); } document.getElementById(score).innerText 当前得分${currentScore} | 最高分${highScore}; }5. 优化与扩展让游戏更专业基础功能完成后可以添加一些优化让游戏体验更好。我总结了几点最实用的改进方案首先是游戏暂停功能。通过添加一个状态变量可以在游戏循环中判断是否应该更新游戏状态let isPaused false; document.addEventListener(keydown, e { if(e.key ) isPaused !isPaused; // 空格键暂停/继续 }); function updateGame() { if(isPaused) return; // 正常游戏逻辑... }其次是游戏开始界面。很多初学者会直接开始游戏但添加一个开始界面能让体验更完整function drawStartScreen() { ctx.fillStyle rgba(0, 0, 0, 0.7); ctx.fillRect(0, 0, 600, 600); ctx.fillStyle white; ctx.font 40px Arial; ctx.textAlign center; ctx.fillText(贪吃蛇, 300, 200); ctx.font 20px Arial; ctx.fillText(按任意键开始游戏, 300, 300); ctx.fillText(最高分${highScore}, 300, 350); } let gameStarted false; document.addEventListener(keydown, () { if(!gameStarted) { gameStarted true; window.requestAnimationFrame(main); } });游戏难度可以随着分数增加而提高。我的做法是每得5分就加快游戏速度function updateGame() { const baseSpeed 150; const speedIncrease Math.floor((snake.length - 3) / 5) * 10; const currentSpeed Math.max(baseSpeed - speedIncrease, 50); if(currentTime - lastRenderTime currentSpeed) return; // ... }最后是移动端适配。虽然贪吃蛇更适合键盘操作但添加触摸控制可以让手机也能玩// 添加触摸控制按钮 div classcontrols button idup↑/button button idleft←/button button idright→/button button iddown↓/button /div // 按钮事件监听 document.getElementById(up).addEventListener(touchstart, () { if(direction ! down) nextDirection up; }); // 其他方向类似...这些优化不是必须的但能让你的贪吃蛇项目从能玩升级到好玩。我建议初学者先完成基础版本再逐步添加这些功能这样可以更好地理解每个改进的意义。

更多文章