给大家分享一下这几天我研究的一个贪吃蛇,挺简单的,但是实现起来其实有点绕的,我给大家附上完整代码,一起分析学习一下,主要用的是构造函数。
思想:
1、设计蛇:属性有宽、高、方向、状态(有多少节),方法:显示,跑 2、设计食物:属性宽、高 3、显示蛇:根据状态向地图里加元素 4、蛇跑起来:下一节到前一节的位置,蛇头根据方向变,删除原来的蛇,新建蛇;当出界时,死亡,初始化;当蛇头吃到自己的时候,死亡,初始化 5、食物被吃掉,蛇加一节,去掉原来的食物,生成新的食物 6、添加定时器,绑定按键
这里先给大家简单的说一下构造函数:
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。
new data();
然后开始我们的贪吃蛇之旅:
1.设置变量,定义蛇的初始状态
1 // 设置蛇的宽、高、默认走的方向 2 this.width = 10; 3 this.height = 10; 4 this.direction = 'right'; 5 6 // 记住蛇的状态,当吃完食物的时候,就要加一个,初始为3个小点为一个蛇, 7 this.body = [ 8 {x:2, y:0}, // 蛇头,第一个点 9 {x:1, y:0}, // 蛇脖子,第二个点 10 {x:0, y:0}, // 蛇尾,第三个点 11 ];
这里面我会在代码里做详细的解释,大家注意看!!!!
2.显示蛇
1 for (var i=0; i<this.body.length; i++) { 2 if (this.body[i].x != null) { // 当吃到食物时,x==null,不能新建,不然会在0,0处新建一个 3 var s = document.createElement('div');//创建元素节点 4 // 将节点保存到状态中,以便于后面删除 5 this.body[i].flag = s; 6 // 设置宽高 7 s.style.width = this.width + 'px'; 8 s.style.height = this.height + 'px'; 9 s.style.borderRadius = "50%"; 10 s.style.background = "rgb(" + Math.floor(Math.random()*256) + "," + Math.floor(Math.random()*256) + "," + Math.floor(Math.random()*256) + ")";//这个就是一个随机颜色 11 // 设置位置 12 s.style.position = 'absolute'; 13 s.style.left = this.body[i].x * this.width + 'px'; 14 s.style.top = this.body[i].y * this.height + 'px';//设置每一个舍身的位置 15 // 添加进去 16 map.appendChild(s);//把元素追加到div中 17 } 18 }
这里使用到for循环,为每一个对象创建一个flag对象,也就是蛇身。
3.让蛇动起来
我的想法就是让后一个元素到前一个元素来,然后这样蛇就动起来了,
1 // 后一个元素到前一个元素的位置 2 for (var i=this.body.length-1; i>0; i--) { 3 this.body[i].x = this.body[i-1].x; 4 this.body[i].y = this.body[i-1].y; 5 //这里就相当于是后一个元素到前一个元素 这样让小蛇动起来 6 }
4.调整蛇头方向
1 // 根据方向处理蛇头 2 switch(this.direction) 3 { 4 //这里不仅调整蛇头方向,还为迟到的食物赋值 下边会有解释 5 case "left": 6 this.body[0].x -= 1; 7 break; 8 case "right": 9 this.body[0].x += 1; 10 break; 11 case "up": 12 this.body[0].y -= 1; 13 break; 14 case "down": 15 this.body[0].y += 1; 16 break; 17 }
5.判断吃到食物和吃到自己和撞墙的事件
这里提醒一下呢就是,不管你是吃到食物了还是撞墙了还是吃到自己了你到要重新创建蛇。
然后呢我会在代码里标注清楚每一个代码的作用。
1 // 判断是否出界,一蛇头判断,出界的话, 2 if (this.body[0].x < 0 || this.body[0].x > 79 || this.body[0].y < 0 || this.body[0].y > 39) { 3 clearInterval(timer); // 清除定时器, 4 alert("你瞎吗?撞死了!"); 5 // 删除旧的 6 for (var i=0; i<this.body.length; i++) { 7 if (this.body[i].flag != null) { // 如果刚吃完就死掉,会加一个值为null的 8 map.removeChild(this.body[i].flag); 9 } 10 } 11 //这里呢一定要删除 之后 初始化一下 因为上边的this.display() 为每一个body对象都添加了一个flag 所以这里删除原本的蛇 重新初始化一下蛇 12 this.body = [ // 回到初始状态, 13 {x:2, y:0}, 14 {x:1, y:0}, 15 {x:0, y:0} 16 ]; 17 this.direction = 'right'; 18 this.display(); // 显示初始状态 19 return false; // 结束 20 } 21 22 // 判断蛇头吃到食物,xy坐标重合, 23 if (this.body[0].x == food.x && this.body[0].y == food.y) { 24 // 蛇加一节,因为根据最后节点定,下面display时,会自动赋值的 25 this.body.push({x:null, y:null, flag: null}); 26 27 //在这里 看了上边的小伙伴可能会有疑惑 this.display 函数里面生成小蛇的判断是x!=null 那我吃到实物以后push进去的都是null 为什么还会创建出来???? 28 //这里呢大家就注意看 this.run() 这个方法 他让后一个元素到前一个元素来 然后你新添加的这一截蛇它会被替换为蛇头 然后进入 下边的switch语句 x,y都会变为-1 或者是1 那么这个时候在走上边的 this.display() 就可以生成一个新的小蛇。 29 30 31 // 清除食物,重新生成食物 32 map.removeChild(food.flag); 33 food.display(); 34 } 35 // 吃到自己死亡,从第五个开始与头判断,因为前四个永远撞不到 36 for (var i=4; i<this.body.length; i++) { 37 if (this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y) { 38 clearInterval(timer); // 清除定时器, 39 alert("傻子!你怎么能吃自己呢?"); 40 // 删除旧的 41 for (var i=0; i<this.body.length; i++) { 42 if (this.body[i].flag != null) { // 如果刚吃完就死掉,会加一个值为null的 43 map.removeChild(this.body[i].flag); 44 } 45 } 46 this.body = [ // 回到初始状态, 47 {x:2, y:0}, 48 {x:1, y:0}, 49 {x:0, y:0} 50 ]; 51 this.direction = 'right'; 52 this.display(); // 显示初始状态 53 return false; // 结束 54 } 55 } 56 57 // 先删掉初始的蛇,在显示新蛇 58 for (var i=0; i<this.body.length; i++) { 59 if (this.body[i].flag != null) { // 当吃到食物时,flag是等于null,且不能删除 60 map.removeChild(this.body[i].flag); 61 } 62 } 63 // 重新显示蛇 64 this.display();
6.构造食物
食物呢,我们就是创建一个,当蛇吃到食物,也就是蛇头与食物的xy坐标一样后删除食物,然后随机新建一个食物
1 this.width = 10; 2 this.height = 10; 3 4 this.display = function() { 5 var f = document.createElement('div'); 6 this.flag = f; 7 f.style.width = this.width + 'px'; 8 f.style.height = this.height + 'px'; 9 f.style.background = 'red'; 10 f.style.borderRadius = '50%'; 11 f.style.position = 'absolute'; 12 //设置随机位置 13 this.x = Math.floor(Math.random()*80); 14 this.y = Math.floor(Math.random()*40); 15 f.style.left = this.x * this.width + 'px'; 16 f.style.top = this.y * this.height + 'px'; 17 //把创建好的食物添加进地图里 18 map.appendChild(f);
7.然后我们要使用构造函数调用食物和蛇的方法
1 var snake = new Snake();// 构造函数 2 var food = new Food(); 3 snake.display(); // 初始化显示 4 food.display();
这里呢,一定要注意,他改变了this的指向,大家可以输出一下this指向,
如果直接调用函数的话,this是指向window,使用构造函数的话this就指向调用者
8.点击开始游戏添加键盘事件
1 document.body.onkeydown = function(e) { 2 // 有事件对象就用事件对象,没有就自己创建一个,兼容低版本浏览器 3 var ev = e || window.event; 4 5 switch(ev.keyCode) 6 { 7 case 38: 8 if (snake.direction != 'down') { // 不允许返回,向上的时候不能向下 9 snake.direction = "up"; 10 } 11 break; 12 case 40: 13 if (snake.direction != "up") { 14 snake.direction = "down"; 15 } 16 break; 17 case 37: 18 if (snake.direction != "right") { 19 snake.direction = "left"; 20 } 21 break; 22 case 39: 23 if (snake.direction != "left") { 24 snake.direction = "right"; 25 } 26 break; 27 } 28 };
9.设置定时器,让小蛇自己动起来
1 // 点击开始时,动起来 2 var begin = document.getElementById('begin'); 3 var timer; 4 begin.onclick = function() { 5 clearInterval(timer); 6 // 先执行run函数,把执行得到的结果,每500毫秒执行一次,不会在执行内部代码 7 // 小技巧,每500毫秒执行字符串,字符串执行内部代码 8 timer = setInterval("snake.run()", 500); 9 };
最后一定要记住,调用定时器前一定要先清除定时器!!!!
给大家附上完整代码:
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" 6 content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 7 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 8 <title>Document</title> 9 <style type="text/css"> 10 body { 11 margin: 0; 12 padding: 0; 13 } 14 .main { 15 800px; 16 height: 400px; 17 margin: 50px auto; 18 } 19 .btn { 20 100px; 21 height: 40px; 22 background: red; 23 } 24 .map { 25 position: relative; 26 800px; 27 height: 400px; 28 background: yellow; 29 } 30 </style> 31 </head> 32 <body> 33 <div class="main"> 34 <button class="btn" id="begin">开始游戏</button> 35 <div class="map" id="map"></div> 36 37 <script type="text/javascript"> 38 var map = document.getElementById('map'); 39 // 使用构造方法创建蛇, 40 function Snake() 41 { 42 // 设置蛇的宽、高、默认走的方向 43 this.width = 10; 44 this.height = 10; 45 this.direction = 'right'; 46 47 // 记住蛇的状态,当吃完食物的时候,就要加一个,初始为3个小点为一个蛇, 48 this.body = [ 49 {x:2, y:0}, // 蛇头,第一个点 50 {x:1, y:0}, // 蛇脖子,第二个点 51 {x:0, y:0}, // 蛇尾,第三个点 52 ]; 53 54 // 显示蛇 55 this.display = function() { 56 // 创建蛇 57 for (var i=0; i<this.body.length; i++) { 58 if (this.body[i].x != null) { // 当吃到食物时,x==null,不能新建,不然会在0,0处新建一个 59 var s = document.createElement('div');//创建元素节点 60 // 将节点保存到状态中,以便于后面删除 61 this.body[i].flag = s; 62 // 设置宽高 63 s.style.width = this.width + 'px'; 64 s.style.height = this.height + 'px'; 65 s.style.borderRadius = "50%"; 66 s.style.background = "rgb(" + Math.floor(Math.random()*256) + "," + Math.floor(Math.random()*256) + "," + Math.floor(Math.random()*256) + ")";//这个就是一个随机颜色 67 // 设置位置 68 s.style.position = 'absolute'; 69 s.style.left = this.body[i].x * this.width + 'px'; 70 s.style.top = this.body[i].y * this.height + 'px';//设置每一个舍身的位置 71 // 添加进去 72 map.appendChild(s);//把元素追加到div中 73 } 74 } 75 }; 76 77 // 让蛇跑起来,后一个元素到前一个元素的位置 78 // 蛇头根据方向处理,所以i不能等于0 79 this.run = function() { 80 // 后一个元素到前一个元素的位置 81 for (var i=this.body.length-1; i>0; i--) { 82 this.body[i].x = this.body[i-1].x; 83 this.body[i].y = this.body[i-1].y; 84 //这里就相当于是后一个元素到前一个元素 这样让小蛇动起来 85 } 86 87 // 根据方向处理蛇头 88 switch(this.direction) 89 { 90 //这里不仅调整蛇头方向,还为迟到的食物赋值 下边会有解释 91 case "left": 92 this.body[0].x -= 1; 93 break; 94 case "right": 95 this.body[0].x += 1; 96 break; 97 case "up": 98 this.body[0].y -= 1; 99 break; 100 case "down": 101 this.body[0].y += 1; 102 break; 103 } 104 105 // 判断是否出界,一蛇头判断,出界的话, 106 if (this.body[0].x < 0 || this.body[0].x > 79 || this.body[0].y < 0 || this.body[0].y > 39) { 107 clearInterval(timer); // 清除定时器, 108 alert("你瞎吗?撞死了!"); 109 // 删除旧的 110 for (var i=0; i<this.body.length; i++) { 111 if (this.body[i].flag != null) { // 如果刚吃完就死掉,会加一个值为null的 112 map.removeChild(this.body[i].flag); 113 } 114 } 115 //这里呢一定要删除 之后 初始化一下 因为上边的this.display() 为每一个body对象都添加了一个flag 所以这里删除原本的蛇 重新初始化一下蛇 116 this.body = [ // 回到初始状态, 117 {x:2, y:0}, 118 {x:1, y:0}, 119 {x:0, y:0} 120 ]; 121 this.direction = 'right'; 122 this.display(); // 显示初始状态 123 return false; // 结束 124 } 125 126 // 判断蛇头吃到食物,xy坐标重合, 127 if (this.body[0].x == food.x && this.body[0].y == food.y) { 128 // 蛇加一节,因为根据最后节点定,下面display时,会自动赋值的 129 this.body.push({x:null, y:null, flag: null}); 130 131 //在这里 看了上边的小伙伴可能会有疑惑 this.display 函数里面生成小蛇的判断是x!=null 那我吃到实物以后push进去的都是null 为什么还会创建出来???? 132 //这里呢大家就注意看 this.run() 这个方法 他让后一个元素到前一个元素来 然后你新添加的这一截蛇它会被替换为蛇头 然后进入 下边的switch语句 x,y都会变为-1 或者是1 那么这个时候在走上边的 this.display() 就可以生成一个新的小蛇。 133 134 135 // 清除食物,重新生成食物 136 map.removeChild(food.flag); 137 food.display(); 138 } 139 // 吃到自己死亡,从第五个开始与头判断,因为前四个永远撞不到 140 for (var i=4; i<this.body.length; i++) { 141 if (this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y) { 142 clearInterval(timer); // 清除定时器, 143 alert("傻子!你怎么能吃自己呢?"); 144 // 删除旧的 145 for (var i=0; i<this.body.length; i++) { 146 if (this.body[i].flag != null) { // 如果刚吃完就死掉,会加一个值为null的 147 map.removeChild(this.body[i].flag); 148 } 149 } 150 this.body = [ // 回到初始状态, 151 {x:2, y:0}, 152 {x:1, y:0}, 153 {x:0, y:0} 154 ]; 155 this.direction = 'right'; 156 this.display(); // 显示初始状态 157 return false; // 结束 158 } 159 } 160 161 // 先删掉初始的蛇,在显示新蛇 162 for (var i=0; i<this.body.length; i++) { 163 if (this.body[i].flag != null) { // 当吃到食物时,flag是等于null,且不能删除 164 map.removeChild(this.body[i].flag); 165 } 166 } 167 // 重新显示蛇 168 this.display(); 169 170 } 171 } 172 173 // 构造食物 174 function Food() 175 { 176 this.width = 10; 177 this.height = 10; 178 179 this.display = function() { 180 var f = document.createElement('div'); 181 this.flag = f; 182 f.style.width = this.width + 'px'; 183 f.style.height = this.height + 'px'; 184 f.style.background = 'red'; 185 f.style.borderRadius = '50%'; 186 f.style.position = 'absolute'; 187 //设置随机位置 188 this.x = Math.floor(Math.random()*80); 189 this.y = Math.floor(Math.random()*40); 190 f.style.left = this.x * this.width + 'px'; 191 f.style.top = this.y * this.height + 'px'; 192 //把创建好的食物添加进地图里 193 map.appendChild(f); 194 } 195 } 196 197 var snake = new Snake();// 构造函数 198 var food = new Food(); 199 snake.display(); // 初始化显示 200 food.display(); 201 202 // 给body加按键事件,上下左右 203 document.body.onkeydown = function(e) { 204 // 有事件对象就用事件对象,没有就自己创建一个,兼容低版本浏览器 205 var ev = e || window.event; 206 207 switch(ev.keyCode) 208 { 209 case 38: 210 if (snake.direction != 'down') { // 不允许返回,向上的时候不能向下 211 snake.direction = "up"; 212 } 213 break; 214 case 40: 215 if (snake.direction != "up") { 216 snake.direction = "down"; 217 } 218 break; 219 case 37: 220 if (snake.direction != "right") { 221 snake.direction = "left"; 222 } 223 break; 224 case 39: 225 if (snake.direction != "left") { 226 snake.direction = "right"; 227 } 228 break; 229 } 230 }; 231 232 // 点击开始时,动起来 233 var begin = document.getElementById('begin'); 234 var timer; 235 begin.onclick = function() { 236 clearInterval(timer); 237 // 先执行run函数,把执行得到的结果,每500毫秒执行一次,不会在执行内部代码 238 // 小技巧,每500毫秒执行字符串,字符串执行内部代码 239 timer = setInterval("snake.run()", 500); 240 }; 241 242 243 </script> 244 </div> 245 </body> 246 </html>
详细的解释我已经在代码中注释,大家把代码复制下来就可以直接查看效果!!!