目录
一、前言
二、项目介绍
三、游戏的实现
一、 创建地图
二、设置食物
三、创建蛇
四、蛇的操作
1、蛇的移动
2、游戏结束
3、吃到食物身体变长
五、设置定时器
六、蛇拐弯
四、总结
一、前言
大家应该都玩过贪吃蛇游戏,这是一款比较经典的游戏,我们可以通过上下左右方向键来蛇的方向,从而让蛇吃到食物,可以变得更长。但是注意:撞到墙壁或吃到自己会结束游戏。
二、项目介绍
这是一个基础的小项目,通过js来设计一个贪吃蛇的小游戏。
需解决的问题:
1、如何让蛇前进
2、如何让蛇吃到食物边长
3、如何通过方向键控制蛇的行动
三、游戏的实现
一、 创建地图
使用构造函数创建地图并设置宽高和颜色,this.ditu属性是为了存放地图的div标签并且为了方便后续使用地图的div标签。
// 创建地图
function Map(){
// 属性
this.width = 600;
this.height = 600;
this.backgroundColor = 'gray';
this.ditu = null; // 创建一个空属性,负责放地图的div
为了节省内存我们可以将构造函数的方法放在原型对象上 (本次为小实验,可以选择直接声明一个方法,不会占用太多内存),通过if判断Map.prototype有没有定义show这个方法,如果定义了则不在定义。为了防止多次实例化时多次定义show方法占用内存。
然后创建一个div的标签负责当背景,根据设置的属性配置样式(也可以直接将属性值放在方法,不过建议尽量选择拿出来单独定义成一个属性,方便更改和代码的查看并保证代码的美观)
通过document.body绑定html中的body标签,并使用appendChild(为标签的末尾追加子节点)将设置好的地图的div标签追加到body中。
if(!Map.prototype.show){
Map.prototype.show = function(){
// 创建div
var div = document.createElement('div');
// 设置样式
div.style.width = this.width + 'px';
div.style.height = this.height + 'px';
div.style.backgroundColor = this.backgroundColor;
document.body.appendChild(div);
// 将地图的div,保存到地图对象的属性上,方便后续使用
this.ditu = div;
}
}
}
创建一个map对象并调用show方法
var map = new Map();
map.show();
二、设置食物
同样的定义一个食物的构造函数,并设置宽、高、颜色、坐标和元素的位置
function Food(map){
// 属性
this.width = 20;
this.height = 20;
this.backgroundColor = 'green';
this.x = 0;
this.y = 0;
this.position = 'absolute';
this.shiwu = null;
同样为了节省内存我们可以将食物的构造函数的方法放在原型对象上,同时利用if判断食物的构造函数中是否存在show方法,如果定义了则不在定义,为了防止多次实例化时多次定义show方法占用内存。创建div标签,将对应的属性放入方法中,通过appendChild将食物放在地图的标签中。
if(!Map.prototype.show){
Map.prototype.show = function(){
var div = document.createElement('div');
// 设置样式
div.style.width = this.width + 'px';
div.style.height = this.height + 'px';
div.style.backgroundColor = this.backgroundColor;
document.body.appendChild(div);
this.ditu = div;
}
}
}
创建食物对象和调用食物的show方法
var map = new Map();
map.show();
三、创建蛇
同样的定义一个蛇的构造函数,并设置宽、高、颜色、坐标、元素的位置 、设置direction属性为蛇开局的默认方向和设置canChange属性为确认蛇是否可以改变方向。
因为蛇的身体是有数个方块组成的并且蛇头与蛇尾的颜色不一,所以我们可以将蛇的身体设置成一个数组,数组里包含身体的横纵坐标、颜色并给身体的每个数组设置第四个元素负责存放蛇每个部分单独的div元素。
function Snake(){
this.body = [[5,3,'red',null], [4,3,'blue',null], [3,3,'blue',null]];
this.width=20;
this.height=20;
this.position='absolute';
this.direction='right';
this.canChange='false';
依旧是为了节省内存我们可以将蛇的构造函数的方法放在原型对象上,同时利用if判断食物的构造函数中是否存在show方法,如果定义了则不在定义,为了防止多次实例化时多次定义show方法占用内存。使用for循环为蛇的每节身体创造一个单独的div,并设置style样式和元素的位置。并将蛇的每个div元素通过appendChild追加到地图中。
通过调用蛇的body数组中的第三个元素(也就是div元素),设置蛇头和蛇身对应的颜色(this.body[i][3]代表蛇身的div元素,style.backgroundColor设置颜色,this.body[i][2]将设置好的属性颜色赋值),同时可以通过div元素设置蛇的初始位置。
canChange属性我们用于后面确定方向是否可以改变。
function Snake(){
this.body = [[5,3,'red',null], [4,3,'blue',null], [3,3,'blue',null]];
this.width=20;
this.height=20;
this.position='absolute';
this.direction='right';
this.canChange='false';
// 方法 显示和移动方法
if(!Snake.prototype.show){
Snake.prototype.show = function(){
// 创建每节身体的div
for(var i=0; i<this.body.length; i++){
// this.body[i] 每节身体信息,是一个数组
if(!this.body[i][3]){
var div = document.createElement('div');
// 设置样式
div.style.width=this.width + 'px';
div.style.height=this.height + 'px';
div.style.position=this.position;
// 显示到地图中
map.ditu.appendChild(div);
this.body[i][3]=div;
}
this.body[i][3].style.backgroundColor = this.body[i][2];
//设置蛇的初始位置
this.body[i][3].style.left=this.body[i][0] * this.width + 'px';
this.body[i][3].style.top=this.body[i][1] * this.height + 'px';
}
// 显示完成,可以改变方向
this.canChange = true;
四、蛇的操作
1、蛇的移动
首先应该考虑到蛇是向前移动,那么蛇头会向前前进一个位置, 第二元素会前进到蛇头之前的位置,以此类推。
为蛇设置一个名为move的方法放在原型对象上,先不管蛇头,利用循环让我蛇身每次可以到达前面的元素的位置。单独利用if else设置蛇头前进,如果direction等于right、left、down、up分别更改蛇头所对应的方向。(right、left、down、up在后面会根据键盘的上下左右键为direction属性进行赋值)
Snake.prototype.move = function(){
// 循环 修改每节身体的坐标(先不管蛇头)
// 让后面的蛇身等于前面的蛇身,蛇头除外
for(var i=this.body.length-1; i>0; i--){
this.body[i][0] = this.body[i-1][0];
this.body[i][1] = this.body[i-1][1];
}
//根据蛇头控制方向(用方向判断,蛇头的坐标怎么改)
if(this.direction == 'right'){
//蛇头的横坐标+1
this.body[0][0] += 1;
}else if(this.direction == 'left'){
//蛇头的横坐标-1
this.body[0][0] -= 1;
}else if(this.direction == 'down'){
//蛇头的总坐标+1
this.body[0][1] += 1;
}else if(this.direction == 'up'){
//蛇头的总坐标-1
this.body[0][1] -= 1;
}
2、游戏结束
首先当蛇撞到墙壁或者吃到自己时会显示弹窗Game over表示游戏结束。
先来判断蛇头撞到墙壁。蛇头的元素大小为20,而游戏背景宽高是600,(在定义蛇时我们使用了absolute以地图为父元素进行了绝对定位)那么就可以确定出蛇头移动到右方下方第三十个元素时或者向左方上方小于第零个元素撞到墙壁,游戏结束。(定时器的设置请往下看)
if(this.body[0][0] < 0 || this.body[0][0] > 29 || this.body[0][1] < 0 || this.body[0][1] > 29){
// 游戏结束,停止定时器
clearInterval(timer);
alert('Game over');
return;
}
其次判断蛇头的坐标是否与身体坐标重合,(注意:判断时一定要记得蛇头与蛇头重合不算失败)。那么我们可以推断出蛇头的横纵坐标与任意一个蛇身的横纵坐标重合就算游戏失败。
for(var i=1; i<this.body.length; i++){
if(this.body[0][0] == this.body[i][0] && this.body[0][1] == this.body[i][1]){
// 游戏结束,停止定时器
clearInterval(timer);
alert('Game over');
return;
}
}
当蛇的操作完成之后调用蛇的show方法重新显示蛇的位置
// 修改完坐标后重新显示
this.show();
3、吃到食物身体变长
首先应该考虑蛇如何算吃到食物,吃到食物之后蛇身如何加长?
当蛇头碰到食物时就算吃到食物,那么蛇头的x坐标或是y坐标只要与食物的x坐标或y坐标重合即可。蛇身延长,延长的蛇身加入到蛇的末尾,那么蛇的元素的长度可以用this.body.length-1显示,加到末尾的蛇身的元素在蛇的数组中的下标可以用this.body.length表示,然后赋值宽高(宽高直接设置为0,因为当蛇变长之后,蛇向前走时最后一个蛇身元素的坐标会变成到数第二个蛇身的坐标)、颜色和null(null为空值,为了存放div)。然后重新刷新食物(调用食物的show方法)。并在后面调用蛇的show方法。
吃到食物加速中的setTimer方法在下面解释。
if(this.body[0][0] == food.x && this.body[0][1] == food.y){
this.body[this.body.length] = [0, 0, 'blue', null];
// 吃到食物了就刷新
food.show();
// 吃到食物让蛇加速
if(t >= 160){
t -= 20;
// 当速度加快时候需要从新开始定时间并关闭之前的定时器
setTimer();
}
}
}
}
}
var snake = new Snake;
snake.show();
五、设置定时器
可以通过定时器实现让蛇自动向前走。t代表500在setInterval中代表计时,为500毫秒。设置setTimer方法,方法中主要有两个作用,一个是设置定时器,一个是暂定定时器。setInterval方法中函数的第一个参数为调用蛇移动的方法,第二个t为500毫秒调用一次。
var t = 500; // 为了实现吃到食物可以加快蛇的速度,所以将毫秒事件用变量表示,方便改变
var timer; // 将timer改为全部变量,为了在下面函数中方便使用
function setTimer(){
//先停止定时器
clearInterval(timer); // 第一个使用定时器时没有timer这个定时间,所以不起作用
var timer = setInterval(function(){
snake.move();
},t);
}
setTimer();
六、蛇拐弯
想让蛇转弯首先要确定键盘上的键位。使用onkeyup并在函数中设置一个参数,通过console.log和点击键盘上的键位我们可以知道键盘键位对应的值。
其次我们通过snake.canChange属实判断是否可以转弯,如果为false则直接return不执行下面的代码(为了防止玩家输入其他键位)。
最后使用if else将点击的键位值进行判断(注意:蛇不能向后走),然后将canChange属性改为false。在蛇的show方法显示完毕,在show方法中会将canChange属性改为true(为了在玩家改变方向时观察到蛇的方向改变后在进行其他方向的改变,可以有更好的体验)。
window.onkeyup = function(e){ // e代表一个形参,是传送键盘点击的值
var evt = e || window.event;
//console.log(evt); // 通过打印evt知道上下左右键的keycode值为37 38 39 40
if(!snake.canChange){ // 当方向不能更改时返回空
return;
}
if(e.keyCode == 37 && snake.direction != 'right'){ // 判断keyCode值是否为左并禁止向右(反方向)走
snake.direction = 'left';
}else if(e.keyCode == 38 && snake.direction != 'down'){
snake.direction = 'up';
}else if(e.keyCode == 39 && snake.direction != 'left'){
snake.direction = 'right';
}else if(e.keyCode == 40 && snake.direction != 'up'){
snake.direction = 'down';
}
snake.canChange = false;
}
四、总结
这是一个非常简单的贪吃蛇游戏模板,很适合刚刚学js的伙伴进行实验,同时可以尝试将游戏背景换成自己喜欢的图片,也可以将蛇更实体化。