共计 6780 个字符,预计需要花费 17 分钟才能阅读完成。
前言
我的博客
看到掘金上有这样一种成果,感觉很难看,就是那种毛玻璃成果,于是想试试写一个登录页面并且实现遮罩,然而写成了开始游戏,可是光一个开始游戏也没意思,罗唆写一个小游戏吧,间接试试贪吃蛇。
如何实现
<div class="main">
<!-- 毛玻璃遮罩盒子 -->
<div id=beginBox>
<div class="btn" id="begin"> 开始游戏 </div>
</div>
<!-- 蛇 -->
<div class="map" id="map"></div>
</div>
这是我 HTML
中body
局部的代码,main
是主体,也就是游戏场地。
beginBox
是开始游戏的界面,我再这个盒子外面实现了毛玻璃遮罩,还不错。
而后上面那个盒子就是蛇了。
如果你也想试试毛玻璃遮罩成果,能够看看我的css
。
间接看 js
代码吧。
首先,咱们先定义好全局变量,做好筹备。
// 蛇的速度,即计时器的间隔时间
var SnakeTime = 200;
// 蛇的身材
var map = document.getElementById('map');
速度是计时器管制的。
接下来,咱们创立一个办法,Snake()
,这是蛇整个的构造方法。
我再这个办法外面写了蛇的一些货色。
我的蛇初始是 3 个 10*10 的正方形拼成的。
// 设置蛇的宽、高、默认走的方向
this.width = 10;
this.height = 10;
this.direction = 'right';
所以办法外面,我首先确定了宽高,以及应用 direction
属性确定方向。
而后,咱们这个蛇的三个点,须要依照法则排好,我这里应用了一个数组。
this.body = [{ x: 2, y: 0}, // 蛇头,第一个点
{x: 1, y: 0}, // 蛇脖子,第二个点
{x: 0, y: 0} // 蛇尾,第三个点
];
这还只是蛇的初始化状态哈!蛇还没创立。
而后咱们来创立蛇。
定义一个办法。这个办法在 snake
办法外面。
// 显示蛇
this.display = function () {
// 创立蛇
for (var i = 0; i < this.body.length; i++) {if (this.body[i].x != null) {
// 当吃到食物时,x==null,不能新建,不然会在 0,0 处新建一个
var s = document.createElement('div');
// 将节点保留到状态中,以便于前面删除
this.body[i].flag = s;
// 设置宽高
s.style.width = this.width + 'px';
s.style.height = this.height + 'px';
// 设置色彩
s.style.backgroundColor = 'yellow';
// 设置地位
s.style.position = 'absolute';
s.style.left = this.body[i].x * this.width + 'px';
s.style.top = this.body[i].y * this.height + 'px';
// 增加进去
map.appendChild(s);
}
}
// 设置蛇头的色彩
this.body[0].flag.style.backgroundColor = 'orange';
};
在这个办法外面,s
就是一个 div,而 body
数组的长度是 3,咱们循环 3 此,顺次追加,就拼成了,头、身、尾。
然而,此时蛇,是进去了,然而不能动啊 ….
所以在定义一个办法,也是在 snake
办法外面。
this.run = function () {
// 后一个元素到前一个元素的地位
for (var i = this.body.length - 1; i > 0; i--) {this.body[i].x = this.body[i - 1].x;
this.body[i].y = this.body[i - 1].y;
}
// 依据方向解决蛇头
switch (this.direction) {
case "left":
this.body[0].x -= 1;
break;
case "right":
this.body[0].x += 1;
break;
case "up":
this.body[0].y -= 1;
break;
case "down":
this.body[0].y += 1;
break;
}
// 判断是否出界, 依据蛇头判断
if (this.body[0].x < 0 || this.body[0].x > 150 || this.body[0].y < 0 || this.body[0].y > 60) {clearInterval(timer);
// 革除定时器
alert("出界啦,游戏完结!");
document.getElementById('beginBox').style.display = 'block';
// 删除旧的
for (var i = 0; i < this.body.length; i++) {if (this.body[i].flag != null) {
// 如果刚吃完就死掉,会加一个值为 null 的
map.removeChild(this.body[i].flag);
}
}
this.body = [ // 回到初始状态,{x: 2, y: 0},
{x: 1, y: 0},
{x: 0, y: 0}
];
this.direction = 'right';
this.display(); // 显示初始状态
return false; // 完结
}
// 判断蛇头吃到食物,xy 坐标重合,if (this.body[0].x == food.x && this.body[0].y == food.y) {
// 蛇加一节,因为依据最初节点定,上面 display 时,会主动赋值的
this.body.push({x: null, y: null, flag: null});
// 获取蛇的长度
var len = this.body.length;
// 依据蛇的长度,设置定时器频率 SnakeTime
SnakeTime = SnakeTime - (len - 3) * 5;
// SnakeTime 最低不能小于 40
if (SnakeTime < 40) {SnakeTime = 40;}
refresh();
// 革除食物, 从新生成食物
map.removeChild(food.flag);
food.display();}
// 吃到本人死亡,从第五个开始与头判断,因为前四个永远撞不到
for (var i = 4; i < this.body.length; i++) {if (this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y) {clearInterval(timer); // 革除定时器,alert("你咬到了本人,游戏完结!");
// 显示 id 为 beginBox 的毛玻璃遮罩盒子
document.getElementById('beginBox').style.display = 'block';
// 删除旧的
for (var i = 0; i < this.body.length; i++) {if (this.body[i].flag != null) {
// 如果刚吃完就死掉,会加一个值为 null 的
map.removeChild(this.body[i].flag);
}
}
this.body = [ // 回到初始状态,{x: 2, y: 0},
{x: 1, y: 0},
{x: 0, y: 0}
];
this.direction = 'right';
this.display(); // 显示初始状态
return false; // 完结
}
}
// 先删掉初始的蛇,在显示新蛇
for (var i = 0; i < this.body.length; i++) {if (this.body[i].flag != null) {
// 当吃到食物时,flag 是等于 null,且不能删除
map.removeChild(this.body[i].flag);
}
}
// 从新显示蛇
this.display();}
}
这段代码有点多哈。咱们拆开看。
// 后一个元素到前一个元素的地位
for (var i = this.body.length - 1; i > 0; i--) {this.body[i].x = this.body[i - 1].x;
this.body[i].y = this.body[i - 1].y;
}
首先,蛇是一节节动的,所以咱们应用循环,让他后一个代替前一个的地位。
而后,依据 direction
属性来判断方向。
// 依据方向解决蛇头
switch (this.direction) {
case "left":
this.body[0].x -= 1;
break;
case "right":
this.body[0].x += 1;
break;
case "up":
this.body[0].y -= 1;
break;
case "down":
this.body[0].y += 1;
break;
}
而后,咱们就要定义出界后游戏完结了,这个就不多说了。
// 判断是否出界, 依据蛇头判断
if (this.body[0].x < 0 || this.body[0].x > 150 || this.body[0].y < 0 || this.body[0].y > 60) {clearInterval(timer);
// 革除定时器
alert("出界啦,游戏完结!");
document.getElementById('beginBox').style.display = 'block';
// 删除旧的
for (var i = 0; i < this.body.length; i++) {if (this.body[i].flag != null) {
// 如果刚吃完就死掉,会加一个值为 null 的
map.removeChild(this.body[i].flag);
}
}
this.body = [ // 回到初始状态,{x: 2, y: 0},
{x: 1, y: 0},
{x: 0, y: 0}
];
this.direction = 'right';
this.display(); // 显示初始状态
return false; // 完结
}
这里的 x 和 y 都是整体的px
/ 蛇的盒子高宽,也就是除 10,1 = 10px
。
而后,就是吃食物了。
当蛇头与食物相遇,咱们就认为它吃了食物,于是咱们通过地位来判断吃货色。
// 判断蛇头吃到食物,xy 坐标重合,if (this.body[0].x == food.x && this.body[0].y == food.y) {
// 蛇加一节,因为依据最初节点定,上面 display 时,会主动赋值的
this.body.push({x: null, y: null, flag: null});
// 获取蛇的长度
var len = this.body.length;
// 依据蛇的长度,设置定时器频率 SnakeTime
SnakeTime = SnakeTime - (len - 3) * 5;
// SnakeTime 最低不能小于 40
if (SnakeTime < 40) {SnakeTime = 40;}
refresh();
// 革除食物, 从新生成食物
map.removeChild(food.flag);
food.display();}
阐明一下:这个
flag
是过后创立食物时留下的一个对象。
创立食物办法我写在了前面,一步步看吧。
而上面这部分代码:
// 获取蛇的长度 var len = this.body.length; // 依据蛇的长度,设置定时器频率 SnakeTime SnakeTime = SnakeTime - (len - 3) * 5; // SnakeTime 最低不能小于 40 if (SnakeTime < 40) {SnakeTime = 40;}
是为了能够动静的实现蛇吃到食物后,速度放慢。
这里,我有一个
refresh();
这个前面再看。
而后就是咬到本人,游戏完结,这个不多说。
当初就到了结构食物了。
// 结构食物
function Food() {
this.width = 10;
this.height = 10;
this.display = function () {// 创立一个 div(一节蛇身)
var f = document.createElement('div');
this.flag = f;
f.style.width = this.width + 'px';
f.style.height = this.height + 'px';
f.style.background = 'red';
f.style.position = 'absolute';
this.x = Math.floor(Math.random() * 80);
this.y = Math.floor(Math.random() * 40);
f.style.left = this.x * this.width + 'px';
f.style.top = this.y * this.height + 'px';
map.appendChild(f);
}
}
实际上,这个“食物”就是创立了蛇的一节身材。
前面也能够看见,有一个追加到蛇身。
map.appendChild(f);
看到这,你可能还纳闷,不应该啊,这也无奈分辨出明确的蛇和食物啊,也就是说,很形象啊。
因为我最初面,还有一个创建对象过程。
var snake = new Snake();
var food = new Food();
// 初始化显示
snake.display();
food.display();
将办法作为了一个对象。
而咱们为了管制蛇的方向,咱们须要应用键盘事件来扭转蛇的属性。
// 给 body 加按键事件,上下左右
document.body.onkeydown = function (e) {
// 有事件对象就用事件对象,没有就本人创立一个,兼容低版本浏览器
var ev = e || window.event;
switch (ev.keyCode) {
case 38:
if (snake.direction != 'down') { // 不容许返回,向上的时候不能向下
snake.direction = "up";
}
break;
case 40:
if (snake.direction != "up") {snake.direction = "down";}
break;
case 37:
if (snake.direction != "right") {snake.direction = "left";}
break;
case 39:
if (snake.direction != "left") {snake.direction = "right";}
break;
// 兼容 WASD 键
case 87:
if (snake.direction != "down") {snake.direction = "up";}
break;
case 83:
if (snake.direction != "up") {snake.direction = "down";}
break;
case 65:
if (snake.direction != "right") {snake.direction = "left";}
break;
case 68:
if (snake.direction != "left") {snake.direction = "right";}
break;
}
};
当然,我这里做了兼容,WASD
和上下左右键都通用管制。
最初就是点击开始游戏的事件了。
// 获取开始按钮
var btn = document.getElementById('begin');
// 点击开始游戏事件
btn.onclick = function () {
// 开始按钮毛玻璃幕布
var parent = this.parentNode;
// 暗藏开始按钮
parent.style.display = 'none';
// 获取定时器工夫
let time = SnakeTime;
timer = setInterval(function () {snake.run();
}, time);
}
咱们这外面是应用了 setInterval
来实现一直的后退走动。
timer = setInterval(function () {snake.run();
}, time);
然而啊,因为这个计时器他是不刷新的,也就是说启动时,
time = 200
,而后你扭转time
的值。此时
time
值的确变了,然而,这个setInterval
它只认定第一次的设置,它不会动静扭转。
那怎么办呢?首先,剖析,他要什么时候做出 time
值的刷新,必定是吃到食物的时候对吧。
于是,咱们写一个刷新函数。
// 定义刷新定时器办法
function refresh() {
// 进行定时器
clearInterval(timer);
// 刷新定时器
timer = setInterval(function () {snake.run();
console.log(SnakeTime);
}, SnakeTime);
}
而后,你们就晓得我下面说的 refresh()
办法是什么了吧?就是用于动静刷新 setInterval
的。
这样,这个贪吃蛇就写好了。
成果
开始页面
游戏界面
残缺源码
Github
:JanYork/Snake
Gitee
:janyork/Snake