看了人家视频上写的贪吃蛇,瞬间觉得自己low爆了。。。。就学他写了三遍,以我的记性,觉得还是有必要记录下过程,以便记忆。
先看下截图:
首先html分析:
注:下列的id是用在js里的,class是用在css里的
<div class="startPage" id="startPage"> <!-- 这是开始页的大页面 -->
<div class="startBtn" id="startBtn"></div> <!-- 载在大页面上的开始按钮键 -->
</div>
<div class="wrapper"> <!-- 开始后的大页面 -->
<div class="left-side"> <!-- 将大页面分出一个左边,用来承载暂停和计分的按键 -->
<div class="header"> <!-- 在这个左边的开头写个用来载计分的框 -->
<div class="score"> <!-- 显示“分数” -->
分数:
<span id="score"></span> <!-- 用在之后js里的动态计分操作 -->
</div>
</div>
<img src="img/zanting.png" id="startP" alt=""> <!-- 显示暂停按钮,这个按钮用图片代替 -->
</div>
<div class="main"> <!-- 大页面中的主页面,和左边的区分下 -->
<div class="content" id="content"></div> <!-- 游戏区域,在js里控制 -->
</div>
</div>
<div class="lose" id="lose"> <!-- 游戏结束的大页面 -->
<div class="con"> <!-- 游戏结束后的计分清单 -->
<span class="loseScore" id="loseScore"> <!-- js动态计分 -->
</span>
<div class="close" id="close"></div> <!-- 游戏结束计分上的关闭按钮 -->
</div>
</div>
上述是贪吃蛇的全部html代码,将贪吃蛇游戏的大致界面划分出来。
接下来是css代码:
*{ /* css的书写习惯,先将所有的margin和padding的默认值都去掉 */
margin: 0;
padding: 0;
}
.startPage{ /* 开始游戏的大页面设置 */
width: 100%;
height: 640px; /* 游戏界面高度,这是我按自己电脑浏览器大小设置的 */
position: absolute; /* 给它个绝对定位 */
z-index: 999; /* 显示在界面最前头 */
}
.startBtn{ /* 游戏开始按键的设置 */
width: 200px; /* 设置按键图片的宽和高 */
height: 50px;
position: absolute; /* 从这到margin:auto,这几行放在一起,表示居中在大页面的正中间 */
top: 0;
left: 0;
bottom: 0;
right: 0;
margin:auto;
background-image: url(img/begin.png); /* 把开始按键的图片放入 */
background-size: 100% 100%; /* 将图片平摊开 */
cursor: pointer; /* 鼠标划入时,箭头变成小手 */
}
.wrapper{ /* 游戏开始后的大页面 */
width: 100%;
height: 640px; /* 同上含义 */
background-image: url('img/bj1.png'); /* 放入游戏背景图片 */
background-size: 100% 100%; /* 平摊 */
position: relative; /* 相对定位 */
top: 0px;
left: 0px;
}
.left-side{ /* 在大页面上划分出的左边部分 */
width: 14%; /* 占大页面的14%宽度 */
height: 640px; /* 高度 */
position: absolute; /* 绝对定位 */
top: 0;
left: 0;
}
.left-side img{ /* 显示暂停按键图片的设置 */
width: 120px; /* 设置图片的宽高 */
height: 40px;
position: absolute; /* 绝对定位 */
left: 15%; /* 该图片在左边划分框中,距离左边距15% */
top: 100px; /* 图片距离上边距100px */
}
.header{ /* 用来承载计分的框 */
width: 100%;
height: 40px;
margin-top: 30px; /* 外边距30px */
position: absolute; /* 绝对定位 */
}
.score{ /* 计分数字显示 */
width: 100px;
height: 40px;
font-size: 18px;
color: #fff; /* 字体白色 */
position: absolute; /* 基于上个absolute的中心定位 */
top: 0;
left: 0;
bottom: 0;
right: 0;
margin:auto;
}
.main{ /* 在大页面中划分出的另一个部分 */
position: absolute; /* 绝对定位 */
left: 15%; /* 距离大页面左边据15% */
width: 52%; /* 占大页面的宽52% */
height: 640px; /* 值含义同上 */
border: 1px solid black; /* 边框,作为标识,可去除 */
}
.content{ /* 游戏边框,用来表示蛇不能出去的部分 */
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin:auto; /* 从position到这表示基于父级定位的中心定位 */
width: 525px; /* 边框的宽高 */
height: 525px;
border: 13px solid rgb(2, 32, 7); /* 边框框 */
}
.lose{ /* 表示游戏结束后的出现的一个大页面 */
width: 100%;
height: 640px;
position: absolute;
top: 0;
left: 0;
display: none; /* 先设置不显示,之后在js中修改 */
}
.con{ /* 表示游戏结束后出现的结束图片 */
width: 340px; /* 为图片设置宽高 */
height: 160px;
background-image:url('img/over.png'); /* 加入图片 */
background-size: 100% 100%; /* 平摊图片 */
position: absolute; /* 同上 */
top: 0;
left: 0;
bottom: 0;
right: 0;
margin:auto;
border-radius: 15% 15%; /* 图片的四个角设置圆度 */
}
.loseScore{ /* 在js中调用,用来显示最后的计分结果 */
font-size: 24px; /* 字体大小24px */
color: #fff;
position: absolute;
left: 130px; /* 计分数字在游戏结束图片上找到合适的位置存放 ,这是按自己的图片设置的*/
top: 80px;
}
.close{ /* 设置游戏结束右上角的关闭按钮 */
background-image: url('img/shanchu.png'); /* 插入关闭按钮图片 */
background-size: 100% 100%; /* 平摊图片 */
width: 35px; /* 设置关闭按钮的宽高 */
height: 35px;
position: absolute; /* 绝对定位 */
top: 0px;
right: 0px; /* 图片放到右上角 */
}
还有部分css代码,在js代码编辑的时候一同写出,现在上述的css算是将贪吃蛇游戏的页面背景设置完毕。
接下来js代码:
var content = document.getElementById('content'); //将html中有id = content的元素提取出来
function init(){ //设置初始时的所有默认值
this.foodW = 20; //表示食物的宽
this.foodH = 20; //高
this.foodX = 0; //表示食物的x坐标位置
this.foodY = 0; //y坐标位置
this.snakeW = 0; //蛇的宽高
this.snakeH = 0;
this.snakeBody = [[4,1,'head'],[3,1,'body'],[2,1,'body']]; //表示三节蛇身(包括蛇头),用数组表示
this.mapH = parseInt(getComputedStyle(content).height); //表示游戏界面的宽高,用getComputedStyle(content).height来提取出content里的height属性值
this.mapW = parseInt(getComputedStyle(content).width); //同上含义
this.mapDiv = content; //表示界面整体
this.direct = 'right'; //初始时蛇的方向
this.up = true; // 表示向上蛇可动
this.down = true; //向下可动
this.right = false; //向右边不可动
this.left = false; //向左边不可动
//上述表示的具体意思是,当蛇的运动方向为右时,不能操作蛇的左右运动,只能操作上下
this.score = 0; //初始计分为0
bindEvent(); //触发bindEvent()函数
}
设置完初始值后,开始设置蛇和食物的初始状态
function food(){ //食物的设置
var food = document.createElement('div'); //生成一个div的食物food
food.style.width = this.foodW + 'px'; //设置食物的宽高,等于上述函数中已经设置好的默认食物宽高
food.style.height = this.foodH + 'px'; //同上
food.style.position = 'absolute'; //设置食物的位置为绝对定位
this.foodX = Math.floor(Math.random()*this.mapW/20); //食物的x坐标的随机位置,表示0到(游戏界面的宽除以食物自己的宽)的随机数
this.foodY = Math.floor(Math.random()*this.mapH/20); //食物的y坐标的随机位置,同上
food.style.top = this.foodY * 20 + 'px'; //表示食物的上边距距离为食物的y坐标乘以自己的高
food.style.left = this.foodX * 20 + 'px'; //同上
this.mapDiv.appendChild(food).setAttribute('class','food'); //给每个食物div都设置上名为food的class,再到css中设置食物的大小和载入图片
}
function snake(){ //蛇的设置
for(var i = 0;i < this.snakeBody.length;i ++){ //用for循环,i小于蛇身的长度,(初始时的length为3)
var snake = document.createElement('div'); //生成一个div的蛇snake
snake.style.width = this.snakeW + 20 + 'px'; //设置生成的蛇的宽高
snake.style.height = this.snakeH + 20 + "px";
snake.style.position = 'absolute'; //设置蛇的绝对位置
snake.style.top = this.snakeBody[i][1] * 20 + 'px'; //设置每节蛇的距离上边距的位置
snake.style.left = this.snakeBody[i][0] * 20 + 'px'; //设置每节蛇的距离左边距的位置
snake.classList.add(this.snakeBody[i][2]); //给每节蛇添加class,这里的class为上述的snakeBody中设置的第3个位置上的‘head’和’body‘,因此又需要对head和body设置css代码,用来区分蛇头和蛇身
this.mapDiv.appendChild(snake).classList.add('snake'); //在html中添加上snake的div,并加上class = ‘snake’
switch(this.direct){ //蛇头的转向(注:由于我找的蛇头的方向默认为右边)
case 'right': //由于默认图片蛇头向右所以不需要改变操作
break;
case 'left' : //将蛇头的方向转向左边
snake.style.transform = 'rotate(180deg)';
break;
case 'up' : //将蛇头方向旋转到上边
snake.style.transform = 'rotate(270deg)';
break;
case 'down' : //将蛇头方向旋转到下方
snake.style.transform = 'rotate(90deg)';
break;
default :
break;
}
}
}
食物和蛇的css代码:
.food{
background-image: url('img/pg.png'); //加入食物的图片
background-size: 100% 100%;
}
.head{
background-image: url('img/s.png'); //加入蛇头的图片
background-size: 100% 100%;
}
.body{
background-image: url('img/she.png'); //加入蛇身的图片
background-size: 100% 100%;
}
设置完蛇和食物的状态后,设置游戏开始时的界面:
var startPage = document.getElementById('startPage'); //从html中获取到id = startPage的元素节点,startPage表示开始游戏的按钮
var startP = document.getElementById('startP'); //同上,startP表示暂停的按钮
function startGame(){ //游戏开始
startPage.style.display = 'none'; // 将该节点设置为不显示
startP.style.display = 'block'; //使该节点显示
food(); //调用food函数
snake(); //调用snake函数
}
接下来设置蛇的运动状态js:
var scoreBox = document.getElementById('score'); //获取用于计分的节点
function move(){
for(var i = this.snakeBody.length-1;i > 0;i --){ //for循环,表示蛇运动时,后一节蛇的位置是前一节蛇之前所在的位置
this.snakeBody[i][0] = this.snakeBody[i-1][0];
this.snakeBody[i][1] = this.snakeBody[i-1][1];
}
switch(this.direct){ //表示蛇头的转动位置情况
case 'right' :
this.snakeBody[0][0] +=1; //右边时,蛇头的x位置加1
break;
case 'left' :
this.snakeBody[0][0] -=1; //左边时,蛇头的x位置减1,
break;
case 'up' :
this.snakeBody[0][1] -=1; //上边时,蛇头的y位置减1
break;
case 'down' :
this.snakeBody[0][1] +=1; //下边时,蛇头的y位置加1
default:
break;
}
removeClass('snake'); //删除class = ‘snake’的节点
snake(); //重新调用snake函数
if(this.snakeBody[0][0] == this.foodX && this.snakeBody[0][1] == this.foodY){ //当蛇吃到食物时
var snakeEndX = this.snakeBody[this.snakeBody.length-1][0]; //表示吃到食物后,蛇尾的x坐标为蛇的长-1
var snakeEndY = this.snakeBody[this.snakeBody.length-1][1]; //同上
switch(this.direct){
case 'right':
this.snakeBody.push([snakeEndX ,snakeEndY,'body']); //蛇头向右时,蛇尾的x位置需要+1
break;
case 'left':
this.snakeBody.push([snakeEndX ,snakeEndY,'body']); //蛇头向左时,蛇尾的x位置需要-1
break;
case 'up':
this.snakeBody.push([snakeEndX,snakeEndY ,'body']); //蛇头向上时,蛇尾的y位置需要-1
break;
case 'down':
this.snakeBody.push([snakeEndX,snakeEndY ,'body']); //蛇头向下时,蛇尾的y位置需要+1
break;
default:
break;
} //注:当蛇吃到食物时,添加的尾巴会在原来的尾巴的位置上加上,直到蛇头离开食物的位置,此时的尾巴就会变成真正的尾巴
this.score += 1; //吃到食物,分数+1
scoreBox.innerHTML = this.score; //使html中的id = ‘scoreBox’的节点上加上此时的分数
removeClass('food'); //蛇吃到食物后,所吃的食物消失
food(); //又重新出现新的食物的位置
}
if(this.snakeBody[0][0] < 0 || this.snakeBody[0][0] > this.mapW/20){ //当蛇头x碰到边界时,游戏结束
reloadGame(); //结束游戏,重新加载游戏
}
if(this.snakeBody[0][1] < 0 || this.snakeBody[0][1] > this.mapH/20){ //当蛇头y碰到边界时,游戏结束
reloadGame(); //结束游戏,重新加载游戏
}
var snakeHX = this.snakeBody[0][0]; //得到蛇头x
var snakeHY = this.snakeBody[0][1]; //得到蛇头y
for(var i = 1;i < this.snakeBody.length;i++){ //当蛇头碰到自己的身体时,游戏结束
if(snakeHX == snakeBody[i][0] && snakeHY == snakeBody[i][1]){
reloadGame();
}
}
}
接下来是删除节点的函数:
function removeClass(className){ //给一个class类名
var ele = document.getElementsByClassName(className); //用ele得到html中有该class类名的节点
while(ele.length > 0){ //当节点长度大于0时,即存在该节点
ele[0].parentNode.removeChild(ele[0]); //删除该节点
}
}
然后是重新加载游戏的js代码:
var loseScore = document.getElementById('loseScore');
function reloadGame(){
removeClass('snake'); //删除class = ‘snake’的节点,即删除蛇
removeClass('food'); //删除食物,同上
clearInterval(snakeMove); //清除snakeMove每隔时间间隔长度运动
this.snakeBody = [[4,1,'head'],[3,1,'body'],[2,1,'body']]; //再添加上默认初始的蛇身坐标
this.direct = 'right'; //初始方向
this.right = false;
this.left = false;
this.up = true;
this.down = true;
lose.style.display = 'block'; //使id = ‘lose’的节点出现,即出现游戏结束后的图片
loseScore.innerHTML = this.score; //记录游戏结束后的最终得分
this.score = 0; //记录后,清0
scoreBox.innerHTML = this.score; //同时将游戏开始时的动态积分也清0
startGameBool = true;
startPauseBool = true;
startP.setAttribute('src','./img/begin.png'); //改变id = ‘startP’节点的图片内容
}
关于操作蛇时的上下左右按键设置:
function setDirect(code){ //给该函数一个表示对应按键数字
switch(code){
case 37: //表示左,方向为左时,上下的按键可触发但是左右的按键不可以触发
if(this.left){
this.direct = 'left';
this.left = false;
this.right = false;
this.up = true;
this.down = true;
}
break;
case 38: //上 ,同上
if(this.up){
this.direct = 'up';
this.left = true;
this.right = true;
this.up = false;
this.down = false;
}
break;
case 39: //右 ,同上
if(this.right){
this.direct = 'right';
this.left = false;
this.right = false;
this.up= true;
this.down = true;
}
break;
case 40: //下 ,同上
if(this.down){
this.direct = 'down';
this.left = true;
this.right = true;
this.up = false;
this.down = false;
}
break;
default:
break;
}
}
接下来是关于游戏的开始和暂停的设置:
var startGameBool = true; //表示该钥匙是开着时
var startPauseBool = true;
var snakeMove;
var speed = 200;
function startAndPush(){
if(startPauseBool){ //关于暂停的钥匙
if(startGameBool){ //关于游戏刚开启时的钥匙
startGame(); //游戏开始,启动游戏开始的界面
startGameBool = false; //游戏开始后,不需要再出现这个过程,将该钥匙锁住
}
startP.setAttribute('src','./img/kk.png'); //为html中的id = ‘startP’设置插入图片
document.onkeydown = function(e){ //触发按键发生的时间
var code = e.keyCode; //得到所触发按键所表示的数字
setDirect(code); //调用上述的按键方向函数
}
snakeMove = setInterval(function(){ 表示每隔speed时间启用一次
move(); 调用move函数,表示每隔speed时间,执行一次move函数事件
},speed);
startPauseBool = false; //执行过后,需要上锁,防止该事件在游戏过程中被启用出错
}else{ //当钥匙关闭时,执行的事件
startP.setAttribute('src','./img/zanting.png'); //改变id = startP的节点中的图片内容
clearInterval(snakeMove); //清除snakeMove每隔speed时间的执行
document.onkeydown = function(e){ //表示禁止按键启用
e.returnValue = false;
return false;
}
startPauseBool = true; //执行上述事件后,将锁打开
}
}
之后是关于主体启用键盘按键和鼠标按键事件:
var lose = document.getElementById('lose');
var close = document.getElementById('close');
var startBtn = document.getElementById('startBtn');
function bindEvent(){
document.onkeydown = function(e){ //同上述描述,是按键的数字获取和启用方向事件
var code = e.keyCode;
setDirect(code)
}
close.onclick = function(){ //表示游戏结束后右上角的关闭按键的触发事件
lose.style.display = 'none'; //使游戏结束界面不显示
}
startBtn.onclick = function(){ //表示刚启动游戏时的开始按钮
startAndPush();
}
startP.onclick = function(){ //表示游戏中的暂停和解除暂停的鼠标按键事件
startAndPush();
}
}
上述就是所有的js代码。。。。
看下成果图:
注:上述代码如有解释错误的,请大佬帮忙改善,谢谢,有不清楚的也可以留言,本人会尽量及时回复的