前言
我的博客
看到掘金上有这样一种成果,感觉很难看,就是那种毛玻璃成果,于是想试试写一个登录页面并且实现遮罩,然而写成了开始游戏,可是光一个开始游戏也没意思,罗唆写一个小游戏吧,间接试试贪吃蛇。
如何实现
<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