1.1界面布局设计
本节主要介绍游戏界面布局的设计,由两个部分组成,即信息展示区(包含了历史最高分和当前分数)和主游戏显示区域。
1.1.1整体界面设计
首先使用一个<div>元素在页面背景上创建游戏整体界面,在其内部添加标题、水平线并预留信息展示区和主游戏显示区域(游戏画布)。相关HTML5代码片段如下:
<body>
<div id="container">
<h3>基于HTML5的贪吃蛇小游戏</h3>
<hr>
<!--状态信息栏-->
<!--设置游戏画布-->
</div>
</body>
这段代码为<div>元素定义了id="container",以便可以使用CSS的ID选择器进行样式设置。
本例使用CSS外部样式表规定页面样式。在本地css文件夹中创建 snake.css文件,并在<head>首尾标签中声明对CSS文件的引用。相关HTML5代码片段如下:
<head>
<meta charset="utf-8">
<title>贪吃蛇游戏的设计与实现</title>
<link rel="stylesheet" href="css/snake.css">
</head>
在CSS文件中使用ID选择器为 id="container"的<div>标签设置样式,具体样式要求如下。
文本:居中显示;
尺寸: 宽度为600像素;
边距:各边的外边距定义为 auto, 以便可以居中显示;各边的内边距为10像素;
颜色:背景颜色为白色;
特殊:使用了CSS3 技术为其定义边框阴影效果,在其右下角有灰色投影。
相关CSS代码片段如下:
/*游戏主界面的总体样式*/
#container {
text-align: center; width: 600px; margin: auto; padding:10px;
background-color:white;
box-shadow: 10px 10px 15px gray;
}
其中,box-shadow 属性可以实现边框投影效果,4个参数分别代表水平方向的偏移(向右偏移10像素)、垂直方向的偏移(向下偏移10像素)、阴影宽度(15像素)和阴影颜色 (灰色),均可自定义成其他值。
由于网页背景颜色默认为白色,与<div>元素设置的背景颜色相同。为了区分,将网页的背景颜色设置为银色(silver)。相关CSS代码片段如下:
body{
background-color:silver;/*设置页面的背景颜色为银色*/
}
此时页面效果如1.1.1 1所示:
图1.1.1 1游戏整体界面样式效果图
由上图可知,关于游戏整体界面的样式要求已初步实现。目前尚未在<div>首尾标签之间填充信息展示栏与游戏画布的相关内容,因此在网页上浏览没有完整效果,需等待后续补充。接下来将介绍如何添加信息展示区。
1.1.2信息展示区设计
信息展示区位于主游戏区域的上方,在游戏主界面的预留区域创建一个id="status"的<div>元素表示,并在其中包含了两个<div>元素分别用于显示历史最高分与当前游戏分数。相关HTML5代码片段如下:
<div id="container">
<h3>基于HTML5的贪吃蛇小游戏</h3>
<hr>
<!--状态信息栏-->
<div id="status">
<!--历史最高分-->
<div class="box">
历史最高分: <span id="bestScore">0</span>
</div>
<!--当前分数-->
<div class="box">
当前分数:<span id="currentScore">0</span>
</div></div>
<!--设置游戏画布-->
</div>
为显示分数的两个<div>元素添加class="box",以便在CSS样式表中为其设置统一样式。其中历史最高分与当前游戏分数均初始化为0,并将分数各自嵌套在<span>容器中。为这两个<span>元素分别设置id="bestScore"和id="currentScore",以方便后面使用jQuery语句实时更新信息展示区中的分数。
在CSS样式表中使用ID选择器为信息展示区(状态栏)设置整体样式。
边距:各边的内边距均为10像素;各边的外边距为auto, 以便可以居中显示。
尺寸:宽度为400像素,高度为20像素。
相关CSS代码片段如下:
#status{
padding:10px;
width:400px;
height:0px;
margin: auto;}
然后使用类选择器为包含class="box"的<div>进行样式设置。
浮动:向左浮动;
尺寸:宽度为200像素。
相关CSS代码片段如下:
/*状态栏中栏目的盒子样式*/
box {
float: left;
width: 200px;
}
此时信息展示区已完成,运行效果如图1.1.2 1所示:
图1.1.2 1信息展示区完成效果图
1.1.3主游戏界面设计
主游戏界面区域包含两个部分,即游戏画面与“重新开始”按钮。本项目的游戏画面是基于HTML5画布API实现的,所有的游戏内容将在<canvas >元素内部呈现。
相关 HTML5代码片段如下:
<div id="container">
<h3>基于HTML5的贪吃蛇小游戏</h3>
<hr>
<!--状态信息栏-->
<!--设置游戏画布-->
<canvas id="myCanvas"width="400"height="400" style="border:lpx solid">
</canvas>
</div>
为画布标签 <canvas 设置id="myCanvas",以便后续使用JavaScript进行绘制工作。设置<canvas 元素的宽度和高度均为400 像素,并使用行内样式表设置该画布带有1像素宽的实线边框效果。
最后使用<button>标签制作“重新开始”按钮。相关HTML5代码片段如下:
<div>
<button onclick="window.location.reload()">重新开始</button>
</div>
当前该按钮仅用于布局设计,单击后暂无响应事件。后续会在JavaScript 中为其增加回调函数。
在CSS文件中为按钮标签<button>进行样式设置。
尺寸:宽度为200像素、高度为50像素;
边距:上、下外边距为10像素,左、右外边距为O像素;
边框:无边框效果;
字体:字体大小为25像素,加粗显示;
颜色:字体颜色为白色,背景颜色为浅珊瑚红色(lightcoral)。
相关CSS代码片段如下:
/*设置游戏按钮样式*/
button {
width: 200px;
height: 50px;
margin: 10px 0;
border: 0;
outline: none;
font-size: 25px;
font-weight: bold;
color: white;
background-color: lightcoral;
}
用户还可以为<button>标签设置鼠标悬浮时的样式效果,在CSS 样式表中用 button:hover 表示。本例将该效果设置为按钮背景颜色的改变,换成颜色加深的珊瑚红色(coral)。
相关CSS 代码片段如下:
/*设置鼠标悬浮时的按钮样式*/
button:hover {
background-color:coral;
}
此时整个样式设计就全部完成了,其页面效果如图1.1.3 1所示:
图1.1.3 1信息展示区完成效果图
1.2数据模型设计
本项目将游戏画布分割成40行、40列,即分割成长宽均为10像素的1600个网格。在画布上的蛇身就是由一系列连续的网格填充颜色组成的,而食物是由单个网格填充颜色而成的,因此只要知道了这些需要填色的网格坐标即可在画布上绘制出蛇身与食物。
1.2.1创建贪吃蛇模型
项目设置贪吃蛇的初始身长为3格,所有坐标数据对应的都是网格的左上角,坐标位置使用浅蓝色填充、边长为10像素的网格来表示蛇身。由于目前规定为向右移动,因此随着每次游戏内容刷新都追加填充右侧一个空白网格作为蛇身,直到完整蛇身在网格中全部显现。
可以使用一个数组来记录组成蛇身的每一个网格坐标,并依次在画布的指定位置填充颜色,这样即可形成贪吃蛇从出现到移动直到展现完整身体的过程。
向右移动的贪吃蛇的初始状态的坐标可以记录为:
var snakeMap=[{'x³:0,'y':10}];
随着蛇头向右前进,该数组增加第2组坐标:
snakeMap=[{'x³:0,'y':10},{'x³:10,'y':10}];
如果继续向右前进,该数组增加第3组坐标:
snakeMap=[{'x³:0,'y':10},{'x³:10,'y³:10},{'x³:20,'y':10}];
此时蛇身已经完整显示出来。
1.2.2蛇身移动模型
当蛇身已经完全显示在游戏画面中时,如果蛇继续前进,则需要清除蛇尾的网格颜色,以表现出蛇的移动效果。
在吃到食物之前,蛇的身长将保持不变。此时除了需要填充右侧一个新的空白网格外,还需要清除最早的一个蛇身网格颜色,以实现蛇在移动的动画效果。
因为该数组中的坐标只用于显示当前的蛇身数据,因此需要去掉曾经路过的轨迹。这种绘制方式可以展现贪吃蛇的动态移动效果。
故只要每次在游戏界面刷新时保持更新 snakeMap 数组的记录即可获得贪吃蛇的当前位置。
1.2.3蛇吃食物模型
在贪吃蛇的移动过程中,如果蛇头碰撞到食物则认为蛇将食物吃掉了。此时蛇的身长增加一格,并且食物消失然后在随机位置重新出现。
因此每当蛇吃到食物时需要将表示蛇身的变量t自增1,然后判断用于记录蛇身坐标的数组snakeMap的长度,如果与当前蛇身长度t的值相同,则不必删除最前面的数据。
此时的snakeMap数组坐标为:
snakeMap=[{'x³:10,'y³:10},{'x³:20,'y':10},{'x³:20,'y³:20},{'x':20,y':30}];
1.3游戏逻辑的实现
1.3.1游戏准备
首先使用一系列变量设置贪吃蛇的初始状态,包括蛇身长度、首次出现的位置和移动方向等。相关JavaScript代码如下:
//游戏参数设置
//蛇的身长
var t=3;
//记录蛇的运行轨迹,用数组记录每一个坐标点
var snakeMap=[];
//蛇身单元大小
var=10;
//方向代码:左37,上38,右39,下40
var direction 39;
//蛇的初始坐标
var=0;
var y=0;
//画布的宽和高
var width=400;
var height=400;
//根据id找到指定的画布
var C=document.getElementById("myCanvas");
//创建2D的context对象
var ctx=c.getContext("2d");
上述代码设置了贪吃蛇的初始状态为身长3格,初始出现位置为(0,0)点,并且向右移动。其中用于记录移动方向的变量direction可以根据实际需要自定义方向对应的数字,这里为了方便按键监听效果,选择了相应的按键code表示方向。
这里声明了蛇身单元格的边长(w)以及画布的宽(width)和高(height)是为了方便开发者的后续修改,如果想改变游戏界面或者贪吃蛇的大小只需要更改对应的这一处变量值,无须修改其他逻辑代码。最后声明的ctx对象是为了后续用于游戏画布的绘制工作。
由于当前蛇的初始位置与方向为固定值,这样会降低游戏难度与可玩度,因此在游戏启动方法GameStartQ中可以使用随机数重新定义蛇初始出现的位置和移动方向,以便每次刷新页面都可以获得不同的游戏效果。
JavaScript中GameStart0方法的初始代码如下:
//启动游戏
function GameStart(){
//随机生成贪吃蛇的蛇头坐标
Math.floor(Math.random() * width /w) w;
Y Math.floor (Math.random()*height / w) *w;
//随机生成蛇的前进方向
Direction =37+ Math.floor(Math.random()*4);
}
首先需要为贪吃蛇随机产生一个初始位置坐标(x,y),并且其中x和y的值必须是游戏画布中任意网格的左上角点,这样才能保证蛇身的位置不发生偏移。
由于游戏画布的长和宽均为400像素,因此画布中任意网格的坐标规律为:(row*10, col*10)其中,row 指的是当前网格的行数,col指的是列数。需要注意的是,这里的行数与列数均为从O开始计数。例如游戏画布中第2行、第3列的网格坐标为(2*10,3*10),即(20,30)。因此只需要随机产生0~39的行数和列数即可乘以网格边长换算出坐标位置。
在 JavaScript 中,Math.randomQ可以产生一个[0,1)区间的随机数,因此在这里使用Math.random(*width/w 表示用随机数乘以画布的宽度再除以网格边长,可获得一个[0,40)区间的随机数。由于本项目中的画布宽与高相等,因此Math.randomQ 米height/w同样可以产生一个[0,40)区间的随机数。使用 Math.floor(函数是为了去掉小数点后面的数字,这样才能确保最后的随机数为指定范围内的整数。最后再乘以网格边长w即可符合画布中任意网格的坐标规律。
而表示蛇前进方向的变量direction 为了和键盘上的方向键对应的代码保持一致,只能是[37,40]区间的一个数字。因此同样先使用Math.randomQ *4来获取[0,4)区间的数字,然后用Math.floorQ函数去掉小数点后面的内容变成整数,最后加上37即可获得[37,41)区间的整数,由于该随机数不包括上限,也就是[37,40]区间的整数。
此时游戏的初始化工作基本完成,接下来将介绍如何动态地绘制蛇身的移动过程。
1.3.2绘制蛇身
每次游戏画面刷新,蛇只需要往指定方向前进一格。如果没有吃到新的食物,则还需要清除原先蛇尾最后一个位置的颜色,以表现出贪吃蛇动态地前进了一格的效果。
在JavaScript 中声明drawSnake0方法专门用于绘制贪吃蛇。
//绘制贪吃蛇函数
function drawSnake(){
//设置蛇身内部的填充颜色
ctx.fillStyle=lightblue";
//绘制最新位置的蛇身矩形
ctx.fillRect(x, Y, w,w);
//数组只保留蛇身长度的数据,如果蛇前进了,则删除最旧的坐标数据
if (snakeMap.length > t){
//删除数组的第一项,即蛇的尾部最后一个位置的坐标记录
var lastBox=snakeMap.shift();
//清除蛇的尾部的最后一个位置,从而实现移动效果
ctx.clearRect(lastBox['x'j,lastBox['y'l,w,w);
}
}
由于要通过游戏画面刷新才能实现动画效果,因此在JavaScript中声明gameRefreshQ方法专门用于刷新画布,并在该方法中调用drawSnake(方法绘制贪吃蛇的蛇身变化过程。
JavaScript中 gameRefresh(方法的初始代码如下:
//游戏画面刷新函数
function gameRefresh() {
//将当前坐标数据添加到贪吃蛇的运动轨迹坐标数组中
snakeMap.push({
‘x’:x;
‘y’:y;
});
//绘制贪吃蛇
drawSnake();
//根据方向移动蛇头的下一个位置
switch(direction) {
//左37
case 37:
x-=w;
break;
//上38
case 38:
y-=w;
break;
//右39
case 39:
x+= w;
break;
//下40
case 40:
y += w;
break;
}
}
最后在GameStart(函数中设置在间隔规定的时间后重复调用 gameRefresh(已达到游戏画面刷新的效果。修改后的GameStartQ函数如下:
//启动游戏
//
function GameStart() {
//随机生成贪吃蛇的蛇头坐标 (代码略)
//随机生成蛇的前进方向
(代码略)
//每隔time毫秒刷新一次游戏内容
timer = setInterval("gameRefresh()", time);}
其中200 指的是游戏画面每隔200毫秒(即0.2秒)刷新一次,该数值可自定义。为方便开发者后续修改,也可以将这里的200改为变量time,并在游戏初始化的参数中对变量time进行初始化声明。
相关JavaScript代码如下:
//游戏参数设置
//游戏界面刷新的间隔时间(数字越大,蛇的速度越慢)
var time=200;
运行结果如图1.3.2 1和图1.3.2 2所示:
1.3.3处理蛇头的移动
贪吃蛇是依靠玩家按下键盘上的方向键进行上、下、左、右方向切换的,因此首先在JavaScript中使用document对象的onkeydown 方法监听并获取用户按键。
JavaScript中按键监听的完整代码如下:
//改变蛇方向的按键监听
document.onkeydown function(e){
//根据按键更新前进方向code: 左37,上38,右39,下40
if(e.keyCode==37||e.keyCode==38||e.keyCode==39||e.keyCode==40)
direction e.keyCode;
}
由于只需要监听键盘上的方向键,并且这些方向键顺时针旋转左、上、右、下对应的数字代码是37~40,因此只考虑这4种情况,并且直接将按键的代码赋值给表示方向的变量direction, 以便后续判断。
1.3.4绘制随机位置的食物
接下来需要在页面上为贪吃蛇绘制食物。食物每次将在随机网格位置出现,占一格位置。食物每次只在画面中呈现一个,直到被蛇头触碰表示吃掉才可在原先的位置清除并在下一个随机位置重新产生。
在JavaScript中声明drawFood0方法用于在游戏画布的随机位置绘制食物,代码如下:
//绘制食物函数
function drawFood() {
//随机生成食物坐标
foodX=Math.floor(Math.random () *width /w) *w;
foodY=Math.floor(Math.random () *height /w) *w;
//内部填充颜色
ctx.fillStyle=#FF0000";
//绘制矩形
ctx.fillRect(foodX, foodY, w, w);
}
这里随机产生的食物坐标(x,y)与贪吃蛇的初始位置坐标要求一样,必须是游戏画布中任意网格的左上角点,因此同样使用了Math.randomQ获取随机数来制作。由于代码完全相同,这里不再赘述,读者可以查看上面绘制蛇身时的解释。然后设置食物为红色,并使用 fillRect(函数进行颜色的填充。
修改JavaScript中的GameStart0方法,在绘制贪吃蛇之前添加drawFoodQ函数来绘制食物。修改后的相关代码如下:
// 启动游戏
function GameStart() {
// 调用drawFood()函数,在随机位置绘制第一个食物
drawFood();
drawFood2();
//随机生成贪吃蛇的蛇头坐标
x = Math.floor((Math.random() * width) / w) * w;
y = Math.floor((Math.random() * height) / w) * w;
//随机生成蛇的前进方向
direction = 37 + Math.floor(Math.random() * 4);
// 每隔time毫秒,则刷新一次游戏内容
timer = setInterval("gameRefresh()", time);
}
添加食物绘制后的运行效果如图1.3.4 1所示:
图1.3.4 1在随机位置生成食物
1.3.5吃到食物的判定
当蛇头与食物出现在同一格中时判定蛇吃到了食物,此时食物消失、当前分数增加10分、蛇身增加一格,并且重新在随机位置生成下一个食物。
在JavaScript中修改后的gameRefresh0方法如下:
//游戏画面刷新函数
function gameRefresh() {
//将当前坐标数据添加到贪吃蛇的运动轨迹坐标数组中
… (代码略)
//绘制贪吃蛇
(代码略)
//根据方向移动蛇头的下一个位置 (代码略)
//碰撞检测,返回值0表示没有撞到障碍物
(代码略)
//吃到食物的判定
if(foodX && foodY==y){
//吃到一次食物加10分
score+= 10;
//更新状态栏中的当前分数
var currentScore document.getElementById("currentScore");
currentScore.innerHTML=score;
//在随机位置绘制下一个食物
drawFood();
//蛇身的长度加1
t++;
}}
这里使用了document.getElementById(方法找到状态栏中用于显示当前分数的元素对象,并将其内容更新为最新的分值。
运行效果如图1.3.5 1和图1.3.5.2
图1.3.5 1贪吃蛇向食物前进
图1.3.5 2贪吃蛇吃到第一个食物
1.3.6碰撞检测
如果蛇头撞到了游戏画布的任意一边或者蛇自己的身体均判定为游戏失败,此时弹出提示对话框告知玩家游戏失败的原因,并显示当前总分。如果本局得分打破了历史纪录,则使用HTML5 Web 存储API中的localStorage对象更新存储的数据。玩家单击对话框上的确定”按钮则可以开始下一局游戏。
在JavaScript中首先创建detectCollision(函数用于进行蛇与障碍物的碰撞检测。碰撞检测需要检测两种可能性,一是蛇头撞到了四周的墙壁,二是蛇头撞到了蛇身。无论哪一种情况发生都判定为游戏失败。
detectCollision()方法的完整代码如下:
function detectCollision() {
//蛇头碰撞到了四周的墙壁,游戏失败
if (x > width || y > height || x < 0 || y < 0) {
return 1;
}
//蛇头碰撞到了蛇身,游戏失败
for (var i = 0; i < snakeMap.length; i++) {
if (snakeMap[i].x == x && snakeMap[i].y == y) {
return 2;
} }
return 0;}
该函数具有3种返回值,分别表示不同的含义。
(1)返回值为0:表示本次没有碰撞到障碍物,游戏继续;
(2)返回值为1:表示蛇头碰撞到了游戏画布任意一边的墙壁,游戏失败;
(3)返回值为2:表示蛇头碰撞到了蛇身,游戏失败。
在JavaScript中修改gameRefreshQ方法,在根据方向移动蛇头位置的switch语句后面添加游戏失败判定的相关代码,通过判断detectCollision(函数的返回值确定当前的游戏状态。修改后的gameRefreshQ方法如下:
//游戏画面刷新函数
function gameRefresh(){
//碰撞检测,返回值o表示没有撞到障碍物
var code=detectCollision();
//如果返回值不为0,表示游戏失败
if(code != 0) {
//如果当前得分高于历史最高分,则更新历史最高分纪录
if(score>bestScore)
localStorage.setItem(“bestScore”, score);
//返回值1表示撞到墙壁
if (code==1){
alert("撞到了墙壁,游戏失败!当前得分: score);
}
//返回值2表示撞到蛇身
else if (code==2){
alert("撞到了蛇身,游戏失败!当前得分:”+score);
}
//重新加载页面
window.location.reload();
}
}
加上碰撞检测后的游戏运行效果如图1.3.6 1和图1.3.6 2所示:
图1.3.6 1贪吃蛇碰撞到墙壁
图1.3.6 2贪吃蛇碰撞到蛇身
1.3.7显示历史最高分
这里将介绍如何在状态栏中显示历史最高分纪录。本项目使用HTML5 Web存储API中的localStorage进行历史最高分记录的读取。在JavaScript中声明showBestScoreQ方法用于获取并在状态栏中展示历史最高分。
JavaScript中showBestScore0方法的完整代码如下:
//显示历史最高分纪录
function showBestScore() {
//从本地存储数据中读取历史最高分
bestScore localStorage.getItem("bestScore");
//如果尚未记录最高分,则重置为O
if (bestScore null)
bestScore 0;
//将历史最高分更新到状态栏中
var best document.getElementById("bestScore");
best.innerHTML bestScore;
}
首先从localStorage 中根据键名称bestScore 查找历史最高分纪录,如果为空值则表示尚未存储最高分,因此将空值重置为0,最后将获取到的最高分更新到信息展示栏中。相关代码修改后如下:
//获得历史最高分纪录
showBestScore();
//开始游戏
GameStart();
});
运行效果如图1.3.7 1所示:
图1.3.7 1贪吃蛇吃食物的过程
上图显示的是历史最高分为30分的情况,当本次游戏分数超过历史最高分时该数字将会被更新。
1.3.8游戏重新开始
玩家重新开始游戏有两种方式,一是当蛇碰撞到墙壁或者自身导致游戏失败时会自动重新开始游戏,二是单击“重新开始”按钮强制重新开始游戏。
重新加载游戏可以直接使用window.location.reload(方法,已达到刷新页面的作用。为“重新开始”按钮添加按键监听,修改后的代码如下:
<button οnclick="window.location.reload()">重新开始</button>
在游戏刷新函数gameRefresh(中找到碰撞检测的相关代码,在判断碰撞到物体时同样添加window.location.reload(方法以达到游戏刷新的效果。
gameRefresh(方法修改后的代码如下:
//游戏画面刷新函数
function gameRefresh() {
//碰撞检测,返回值0表示没有撞到障碍物
var code detectCollision();
//如果返回值不为0,表示游戏失败
if (code!= 0) {
(代码略)
//重新加载页面
window.location.reload();
}
}
1.4增加一个蓝色的食物
(1)增加一个绘制食物函数drwofood2(),并将他的颜色修改为蓝色:
function drawFood2() {
//随机生成食物坐标
foodM = Math.floor((Math.random() * width) / w) * w;
foodN = Math.floor((Math.random() * height) / w) * w;
//内部填充颜色
ctx.fillStyle = "blue";
//绘制矩形
ctx.fillRect(foodM, foodN, w, w);
}
(2)在启动游戏函数GameStart()函数中再次调用drawFood2函数:
//启动游戏
function GameStart() {
//调用drawFood()函数,在随机位置给一个食物
drawFood1();
drawFood2();
//随机生成贪吃蛇的蛇头坐标
x = Math.floor((Math.random() * width) / w) * w;
y = Math.floor((Math.random() * height) / w) * w;
direction = 37 + Math.floor(Math.random() * 4);
timer = setInterval("gameRefresh()", time);
}
- 同时在对吃到食物的判定函数中加上对第2个食物的判定:
//吃到食物的判定
if (foodX == x && foodY == y) {
//吃到食物加10分
score += 10;
//更新状态栏中当前得分
var currentScore = document.getElementById("currentScore");
currentScore.innerHTML = score;
//在随机位置绘制下一个食物
drawFood1();
//蛇身长度加1
t++;
}
else if (foodM == x && foodN == y) {
//吃到食物加10分
score += 10;
//更新状态栏中当前得分
var currentScore = document.getElementById("currentScore");
currentScore.innerHTML = score;
drawFood2();
//蛇身长度加1
t++;
}
具体效果如图1.4 1所示:
图1.4 1增加一个食物实现效果
1.5吃到食物时播放音效
(1)在js中载入音效文件:
//吃食物音效
let mp3 = new Audio("images/Minecraft吃东西音效_爱给网_aigei_com.mp3");
mp3.loop = false;
mp3.play();
- 并在判吃到食物的函数中调用添加音效的播放函数mp3.play():
//吃到食物的判定
if (foodX == x && foodY == y) {
//吃到食物加10分
score += 10;
//更新状态栏中当前得分
var currentScore = document.getElementById("currentScore");
currentScore.innerHTML = score;
//在随机位置绘制下一个食物
drawFood1();
//蛇身长度加1
t++;
mp3.play();
}
else if (foodM == x && foodN == y) {
//吃到食物加10分
score += 10;
//更新状态栏中当前得分
var currentScore = document.getElementById("currentScore");
currentScore.innerHTML = score;
//在随机位置绘制下一个食物
drawFood2();
//蛇身长度加1
t++;
mp3.play();
}
至此,贪吃蛇游戏的功能都已基本实现。
具体代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>贪吃蛇游戏的设计与实现</title>
<link rel="stylesheet" href="css/snake.css" />
</head>
<body>
<div id="container">
<h3>基于HTML5的贪吃蛇小游戏</h3>
<hr />
<!--状态信息栏-->
<div id="status">
<!--历史最高分-->
<div class="box">历史最高分:<span id="bestScore">0</span></div>
<!--当前分数-->
<div class="box">当前分数:<span id="currentScore">0</span></div>
</div>
<!--设置游戏画布-->
<canvas
id="myCanvas"
width="400"
height="400"
style="border: 1px solid"
></canvas>
<audio
id="eatSound"
src="images/Minecraft吃东西音效_爱给网_aigei_com.mp3"
></audio>
<div>
<button οnclick="window.location.reload()">重新开始</button>
</div>
</div>
<script>
//=====================
// 游戏参数设置
//=====================
//游戏界面刷新的间隔时间(数字越大,蛇的速度越慢)
var time = 200;
var speed = 187;
//蛇身长
var t = 3;
//记录蛇运行轨迹,用数组记录每一个坐标点
var snakeMap = [];
//蛇身单元大小
var w = 10;
// 方向代码:左37,上38,右39,下40
var direction = 37;
//蛇的初始坐标
var x = 0;
var y = 0;
//食物的初始化坐标
var foodX = 0;
var foodY = 0;
//当前得分
var score = 0;
//历史最高分纪录
var bestScore = 0;
//画布的宽和高
var width = 400;
var height = 400;
//根据id找到指定的画布
var c = document.getElementById("myCanvas");
//创建2D的context对象
var ctx = c.getContext("2d");
var timer;
// 获得历史最高分记录
showBestScore();
//开始游戏
GameStart();
//=====================
// 显示历史最高分记录
//=====================
function showBestScore() {
//从本地存储数据中读取历史最高分
bestScore = localStorage.getItem("bestScore");
//如果尚未记录最高分,则重置为0
if (bestScore == null) bestScore = 0;
//将历史最高分更新到状态栏中
var best = document.getElementById("bestScore");
best.innerHTML = bestScore;
}
//=====================
// 启动游戏
//=====================
function GameStart() {
// 调用drawFood()函数,在随机位置绘制第一个食物
drawFood();
drawFood2();
//随机生成贪吃蛇的蛇头坐标
x = Math.floor((Math.random() * width) / w) * w;
y = Math.floor((Math.random() * height) / w) * w;
//随机生成蛇的前进方向
direction = 37 + Math.floor(Math.random() * 4);
// 每隔time毫秒,则刷新一次游戏内容
timer = setInterval("gameRefresh()", time);
}
//=====================
// 游戏画面刷新函数
//=====================
function gameRefresh() {
//将当前坐标数据添加到贪吃蛇的运动轨迹坐标数组中
snakeMap.push({
x: x,
y: y,
});
//绘制贪吃蛇
drawSnake();
// 根据方向移动蛇头的下一个位置
switch (direction) {
//左37
case 37:
x -= w;
break;
//上38
case 38:
y -= w;
break;
//右39
case 39:
x += w;
break;
//下40
case 40:
y += w;
break;
}
//碰撞检测,返回值0表示没有撞到障碍物
var code = detectCollision();
//如果返回值不为0,表示游戏失败
if (code != 0) {
//如果当前得分高于历史最高分,则更新历史最高分记录
if (score > bestScore) localStorage.setItem("bestScore", score);
//返回值1表示撞到墙壁
if (code == 1) {
alert("撞到了墙壁,游戏失败!当前得分:" + score);
GameStart();
}
//返回值2表示撞到蛇身
else if (code == 2) {
alert("撞到蛇身,游戏失败!当前得分:" + score);
}
//重新加载页面
window.location.reload();
}
// 获取音频
var eatSound = document.getElementById("eatSound");
function eatFood() {
// 播放音效
eatSound.play();
}
//吃到食物判定
if (foodX == x && foodY == y) {
//吃到一次食物加10分
score += 10;
//更新状态栏中的当前分数
var currentScore = document.getElementById("currentScore");
currentScore.innerHTML = score;
// 播放音效
eatFood();
//在随机位置绘制下一个食物
drawFood();
//蛇身长度加1
t++;
}
if (foodX2 == x && foodY2 == y) {
//吃到一次食物加10分
score += 10;
//更新状态栏中的当前分数
var currentScore = document.getElementById("currentScore");
currentScore.innerHTML = score;
// 播放音效
eatFood();
//在随机位置绘制下一个食物
drawFood2();
//蛇身长度加1
t++;
}
}
//==================
// 绘制贪吃蛇函数
//==================
function drawSnake() {
//设置蛇身内部填充颜色
ctx.fillStyle = "lightblue";
//绘制最新位置的蛇身矩形
ctx.fillRect(x, y, w, w);
//数组只保留蛇身长度的数据,如果蛇前进了则删除最旧的坐标数据
if (snakeMap.length > t) {
//删除数组第一项,即蛇的尾部最后一个位置的坐标记录
var lastBox = snakeMap.shift();
//清除蛇的尾部最后一个位置,从而实现移动效果
ctx.clearRect(lastBox["x"], lastBox["y"], w, w);
}
}
//==================
// 改变蛇方向的按键监听
//==================
document.onkeydown = function (e) {
// 根据按键更新前进方向code:左37,上38,右39,下40
if (
e.keyCode == 37 ||
e.keyCode == 38 ||
e.keyCode == 39 ||
e.keyCode == 40
)
direction = e.keyCode;
if (e.keyCode == 187 || e.keyCode == 189) {
clearInterval(timer);
speed = e.keyCode;
switch (speed) {
case 187:
time -= 10;
break;
case 189:
time += 10;
break;
}
timer = setInterval("gameRefresh()", time);
}
};
//================
// 碰撞检测函数
//================
function detectCollision() {
//蛇头碰撞到了四周的墙壁,则游戏失败
if (x > width || y > height || x < 0 || y < 0) {
return 1;
}
//蛇头碰撞到了蛇身,则游戏失败
for (var i = 0; i < snakeMap.length; i++) {
if (snakeMap[i].x == x && snakeMap[i].y == y) {
return 2;
}
}
return 0;
}
//================
// 绘制食物函数
//================
function drawFood() {
//随机生成食物坐标
foodX = Math.floor((Math.random() * width) / w) * w;
foodY = Math.floor((Math.random() * height) / w) * w;
//内部填充颜色
ctx.fillStyle = "#FF0000";
//绘制矩形
ctx.fillRect(foodX, foodY, w, w);
}
function drawFood2() {
//随机生成食物坐标
foodX2 = Math.floor((Math.random() * width) / w) * w;
foodY2 = Math.floor((Math.random() * height) / w) * w;
//内部填充颜色
ctx.fillStyle = "#0000FF";
//绘制矩形
ctx.fillRect(foodX2, foodY2, w, w);
}
</script>
</body>
</html>
css代码:
body{
background-color:silver;/*设置页面的背景颜色为银色*/
}
/*游戏主界面总体样式*/
#container {
text-align: center;
width: 600px;
margin: auto;
padding:10px;
background-color:white;
box-shadow: 10px 10px 15px gray;
}
/*状态栏样式*/
#status {
padding: 10px;
width: 400px;
height: 20px;
margin: auto;
}
/*状态栏中栏目盒子样式*/
.box {
float: left;
width: 200px;
}
/*设置游戏按钮样式*/
button {
width: 200px;
height: 50px;
margin: 10px 0;
border: 0;
outline: none;
font-size: 25px;
font-weight: bold;
color: white;
background-color: lightcoral;
}
/*设置鼠标悬浮时的按钮样式*/
button:hover {
background-color: coral;
}