本文详细介绍如何使用JavaScript从零构建贪吃蛇游戏。内容涵盖HTML结构搭建、CSS绝对定位样式设置,以及核心脚本逻辑:包括蛇身初始化、随机食物生成算法、定时器控制移动、边界穿越处理、方向键监听与防反向判定,以及碰撞检测机制。提供完整可运行代码,适合前端初学者理解游戏开发基础。
本文分享了一个使用JavaScript编写的贪吃蛇游戏完整示例,供读者参考学习。
页面结构与样式设计
1. 页面结构
首先构建HTML结构,创建一个id为box的容器作为蛇身的载体。初始状态下,box内仅包含一个子div,代表蛇的初始长度为1。同时,创建一个id为food的div元素,用于显示游戏目标食物。
<div id="box">
<div></div>
</div>
<div id="food"></div>
2. CSS样式设计
定义蛇身和食物的视觉样式。关键点在于将蛇身子元素和食物元素均设置为绝对定位(absolute),以便通过JavaScript动态控制其坐标位置。
<style>
*{
padding: 0px;
margin: 0px;
}
#box div{
width: 30px;
height: 30px;
box-sizing: border-box;
background: green;
border: 1px solid black;
position: absolute;
}
#food{
width: 30px;
height: 30px;
background: brown;
position: absolute;
}
</style>
3. 脚本逻辑实现
在JavaScript中,首先获取蛇身容器box及其内部所有子元素(代表蛇的每一节身体)。
var box = document.getElementById("box");
var boxs = document.getElementById("box").children;
接着,定义变量来记录蛇头当前的X和Y坐标位置。
var snackX = 0;
var snackY = 0;
获取浏览器窗口的可视区域宽度和高度,据此计算蛇身移动的最大边界值(minsnackX, maxsnackX, minsnackY, maxsnackY),从而限制蛇的活动范围。
var cw = document.documentElement.clientWidth;
var ch = document.documentElement.clientHeight;
var minsnackX = 0;
var maxsnackX = Math.floor(cw / boxs[0].offsetWidth)*boxs[0].offsetWidth;
var minsnackY = 0;
var maxsnackY = Math.floor(ch / boxs[0].offsetHeight)*boxs[0].offsetHeight;
初始化蛇的移动方向,默认设置为向右("right")。
var turn = "right";
获取食物DOM元素,并定义变量存储食物的坐标位置。
var foodele = document.getElementById("food");
var foodX,foodY;
执行蛇的初始化操作,通过循环向box容器中克隆添加子元素,使蛇的初始长度达到设定值(例如6节)。
for(var i = 0; i <6 ; i++){
box.appendChild(boxs[0].cloneNode(true));
}
实现食物刷新逻辑,确保每次生成新食物时,其位置不与蛇身任何一节重合。
function food(){
//此处的坐标要先获取页面最大支持的蛇身体的块数,然后在块数中随机,然后乘以块数的大小,
//因为蛇的移动每一步都是固定的,想要判定食物和蛇头重合就必须坐标是整块的倍数。
foodX = parseInt( Math.random()*Math.floor(cw / boxs[0].offsetWidth))*boxs[0].offsetWidth;
foodY = parseInt( Math.random()*Math.floor(ch / boxs[0].offsetHeight))*boxs[0].offsetHeight;
//判定当食物的产生位置和蛇的任何一个位置重合时就重新生成食物。
for(var i = 0;i<boxs.length;i++){
if(foodX + "px" === boxs[i].style.left && foodY + "px" === boxs[i].style.top){
food();
}
}
foodele.style.left = foodX + "px";
foodele.style.top = foodY + "px";
}
调用food()函数生成游戏开始时的第一个食物位置。
food();
使用setInterval设置定时器,每隔固定时间(如150毫秒)执行一次蛇的移动函数,控制游戏节奏。
var timer = setInterval(function(){
snackMOve();
},150)
核心脚本逻辑实现
封装核心移动函数snackMOve(),负责更新蛇头坐标、处理边界穿越、检测碰撞及更新DOM位置。
//移动和判定边界
function snackMOve(){
//此处为判定方向 根据判定的方向,向改方向前进一个方块
switch(turn){
case "right":snackX +=30;break;
case "left":snackX -=30;break;
case "bottom":snackY +=30;break;
case "top":snackY -=30;break;
}
//如果蛇越过了墙就从另一端出现
if(snackX > maxsnackX){
snackX = 0;
}
if(snackX < minsnackX){
snackX = maxsnackX;
}
if(snackY > maxsnackY){
snackY = 0;
}
if(snackY < minsnackY){
snackY = maxsnackY;
}
//从最后一个开始,每个元素跟随上一个元素的位置
for(var i = boxs.length-1; i >0 ; i--){
boxs[i].style.left = boxs[i-1].style.left;
boxs[i].style.top = boxs[i-1].style.top ;
}
//第一个也就是蛇头的位置,永远是根据方向获取的位置
boxs[0].style.left = snackX + "px";
boxs[0].style.top = snackY + "px" ;
//判定吃到食物 就长大和刷新
//当蛇头位置移动之后与食物重合 那么刷新食物,并且在蛇的身体中插入一个克隆的元素,相当于长度+1
if(snackX === foodX && snackY === foodY){
food();
box.appendChild(boxs[0].cloneNode(true));
}else{
//判定撞死
//当蛇头与身体中的任何一个元素重合,那么判定结束游戏,停止定时器
for(var i = 1;i<boxs.length;i++){
if(snackX + "px" === boxs[i].style.left && snackY + "px" === boxs[i].style.top){
clearInterval(timer);
alert("失败");
}
}
}
}
通过监听键盘事件(keydown)改变移动方向变量turn,实现玩家对蛇运动方向的控制。
document.onkeydown = function(eve){
var e = eve||event;
var keyCode = e.keyCode||e.which;
switch(keyCode){
case 37:if(turn === "right"){break;}turn = "left";break;
case 38:if(turn === "bottom"){break;}turn = "top";break;
case 39:if(turn === "left"){break;}turn = "right";break;
case 40:if(turn === "top"){break;}turn = "bottom";break;
}
}
以下是完整的HTML、CSS和JavaScript代码实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
*{
padding: 0px;
margin: 0px;
}
#box div{
width: 30px;
height: 30px;
box-sizing: border-box;
background: green;
border: 1px solid black;
position: absolute;
}
#food{
width: 30px;
height: 30px;
background: brown;
position: absolute;
}
</style>
</head>
<body>
<div id="box">
<div></div>
</div>
<div id="food"></div>
<script>
var box = document.getElementById("box");
var boxs = document.getElementById("box").children;
var snackX = 0;
var snackY = 0;
var cw = document.documentElement.clientWidth;
var ch = document.documentElement.clientHeight;
var minsnackX = 0;
var maxsnackX = Math.floor(cw / boxs[0].offsetWidth)*boxs[0].offsetWidth;
var minsnackY = 0;
var maxsnackY = Math.floor(ch / boxs[0].offsetHeight)*boxs[0].offsetHeight;
var turn = "right";
var foodele = document.getElementById("food");
var foodX,foodY;
for(var i = 0; i <6 ; i++){
box.appendChild(boxs[0].cloneNode(true));
}
//随机食物
function food(){
foodX = parseInt( Math.random()*Math.floor(cw / boxs[0].offsetWidth))*boxs[0].offsetWidth;
foodY = parseInt( Math.random()*Math.floor(ch / boxs[0].offsetHeight))*boxs[0].offsetHeight;
for(var i = 0;i<boxs.length;i++){
if(foodX + "px" === boxs[i].style.left && foodY + "px" === boxs[i].style.top){
food();
}
}
foodele.style.left = foodX + "px";
foodele.style.top = foodY + "px";
}
food();
//设置定时器 移动
var timer = setInterval(function(){
snackMOve();
},150)
//移动和判定边界
function snackMOve(){
switch(turn){
case "right":snackX +=30;break;
case "left":snackX -=30;break;
case "bottom":snackY +=30;break;
case "top":snackY -=30;break;
}
//根据边界归零
if(snackX > maxsnackX){
snackX = 0;
}
if(snackX < minsnackX){
snackX = maxsnackX;
}
if(snackY > maxsnackY){
snackY = 0;
}
if(snackY < minsnackY){
snackY = maxsnackY;
}
for(var i = boxs.length-1; i >0 ; i--){
boxs[i].style.left = boxs[i-1].style.left;
boxs[i].style.top = boxs[i-1].style.top ;
}
boxs[0].style.left = snackX + "px";
boxs[0].style.top = snackY + "px" ;
//判定吃到食物 就长大和刷新
if(snackX === foodX && snackY === foodY){
food();
box.appendChild(boxs[0].cloneNode(true));
}else{
//判定撞死 暂停计时器 刷新
for(var i = 1;i<boxs.length;i++){
// console.log(boxs[i].style.left);
if(snackX + "px" === boxs[i].style.left && snackY + "px" === boxs[i].style.top){
clearInterval(timer);
alert("失败");
// console.log(1)
}
}
}
}
//方向
document.onkeydown = function(eve){
var e = eve||event;
var keyCode = e.keyCode||e.which;
switch(keyCode){
case 37:if(turn === "right"){break;}turn = "left";break;
case 38:if(turn === "bottom"){break;}turn = "top";break;
case 39:if(turn === "left"){break;}turn = "right";break;
case 40:if(turn === "top"){break;}turn = "bottom";break;
}
}
</script>
</body>
</html>
游戏逻辑总结
总结
贪吃蛇游戏的核心开发思路主要包含以下关键环节:
1. 食物随机生成逻辑(需确保不生成在蛇身坐标上); 2. 利用定时器(setInterval)控制蛇的移动频率; 3. 边界碰撞判定(穿越墙壁后从另一侧出现); 4. 蛇身运动逻辑(头部移动,身体跟随); 5. 运动方向判定与键盘事件监听; 6. 吃到食物的判定(头部坐标与食物坐标重合); 7. 蛇头与身体碰撞判定(即游戏结束条件)。
此外,还推荐参考JavaScript经典小游戏汇总专题,获取更多游戏开发案例。
以上就是JavaScript实现贪吃蛇游戏的完整教程,希望能为读者的前端学习提供有益帮助。