贪吃蛇--JaveScript原生代码
贪吃蛇案例主要分为四个部分:
1 > 地图
2 > 食物
主要是设置随机的食物,还有食物的范围
3 > 小蛇
在设置小蛇运动的时,需要内部的每个盒子都进行移动,移动是通过坐标的修改来实现的,除了蛇头以外,其余的蛇身中的每一个部分都可以使用上一个蛇身的坐标值. 但是,使用正向遍历的时候,导致所有的蛇身坐标都是蛇头的坐标,这个时候,我采用了反向遍历的方式
后期需要给蛇头的坐标进行单独的设置
4 > 游戏控制
1.设置食物和小蛇的显示
2.设置小蛇的自动运动
使用定时器,让小蛇进行自身的移动
检测小蛇是否迟到食物–使用蛇头的坐标和食物的坐标进行对比
检测游戏是否结束
小蛇撞墙(坐标范围X=0-39;Y=0-29;)==>结束
小蛇撞到自己(从第五个坐标进行检测)==>结束
3.设置用户的键盘操作–使用了e.keyCode属性值修改了小蛇中的运动属性
<!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>今天天气不错</title> 6 <style> 7 * { 8 margin: 0; 9 padding: 0; 10 } 11 12 #map { 13 width: 800px; 14 height: 600px; 15 background-color: #CCCCCC; 16 position: relative; 17 } 18 19 #map div { 20 /*用于设置地图中的食物,小蛇的盒子定位*/ 21 position: absolute; 22 } 23 #button{ 24 float: right; 25 height: 50px; 26 line-height: 50px; 27 } 33 </style> 34 </head> 35 <body> 36 37 <div id="map"></div> 38 <div id="button"> 41 </div> 42 43 <script> 44 //贪食蛇案例中一共分为4个部分(对象) 45 //1 地图 2 食物 3 小蛇 4 游戏控制 46 47 //食物对象 48 (function () { 49 50 function Food(options) { 51 //每个食物所必须的属性 52 //宽 高 背景色 坐标 放置位置 53 this.width = options.width || 20; 54 this.height = options.height || 20; 55 this.bgColor = options.bgColor || "green"; 56 57 //坐标设置:只是为了在后期维护时,查看属性比較方便 58 this.x = 0; 59 this.y = 0; 60 61 //放置位置 62 this.map = options.map; 63 64 //用于保存当前存在的食物盒子的DOM对象 65 this.element = null; 66 } 67 68 //对食物进行初始化操作(设置盒子的显示效果) 69 Food.prototype.init = function () { 70 //删除旧的食物盒子 71 if (this.element) { 72 this.map.removeChild(this.element); 73 } 74 75 //1 创建食物盒子 76 var div = document.createElement("div"); 77 //---为了在删除盒子时可以找到当前创建的盒子,先进行一次保存,保存在实例对象的属性中 78 this.element = div; 79 80 //2 设置样式 81 div.style.width = this.width + "px"; 82 div.style.height = this.height + "px"; 83 div.style.backgroundColor = this.bgColor; 84 //3 放入到地图中 85 this.map.appendChild(div); 86 87 //4 调用this的随机位置方法 88 this.setRandomPos(); 89 }; 90 91 //设置食物盒子的随机位置 92 Food.prototype.setRandomPos = function () { 93 //先计算出随机的坐标值,再根据坐标值设置对应的left/top即可 94 //1 先计算可以容纳的坐标最大值 95 //this.map.offsetWidth / this.width 96 //2 获取范围内的随机坐标 97 this.x = Math.floor(Math.random() * this.map.offsetWidth / this.width); 98 this.y = Math.floor(Math.random() * this.map.offsetHeight / this.height); 99 100 //3 根据坐标设置left和top属性 101 this.element.style.left = this.x * this.width + "px"; 102 this.element.style.top = this.y * this.height + "px"; 103 }; 104 105 //暴露操作 106 window.Food = Food; 107 })(); 108 109 //小蛇对象 110 (function () { 111 112 function Snake(options) { 113 //分析小蛇对象需要哪些属性? 114 //宽 高 背景色 坐标 放置位置 运动方向 115 116 //将宽高设置为统一的值,其他的坐标等属性进行分别设置 117 this.width = options.width || 20; 118 this.height = options.height || 20; 119 120 //设置坐标:采用一种较为复杂的结构,目的是保存蛇身内每个小盒子的坐标值 121 this.body = [ 122 //当前设置的为小蛇初始的坐标与颜色 123 {x: 3, y: 1, color: "red"},//蛇头 124 {x: 2, y: 1, color: "pink"},//蛇身 125 {x: 1, y: 1, color: "pink"},//蛇身 126 ]; 127 //设置放置位置 128 this.map = options.map; 129 //设置运动方向 130 this.direction = options.direction || "right"; 131 132 //设置一个属性,用于保存蛇身的所有结构(DOM对象),目的为删除时可以找到对应的元素 133 this.elements = []; 134 } 135 136 //设置小蛇的显示的方法: 137 Snake.prototype.init = function () { 138 //当我们要进行绘制之前,先将旧的蛇身结构进行删除 139 //要删除的是每个标签的结构,先进行遍历操作 140 141 //可以提前对this.elements进行内容检测,但是没有必要 142 // if(this.elements.length !== 0){} 143 for (var i = 0; i < this.elements.length; i++) { 144 //要删除的是每个标签的结构 145 this.map.removeChild(this.elements[i]); 146 //还需要进行数据的删除 147 } 148 this.elements = []; 149 150 151 //1 创建小蛇中的每个部分(遍历this.body中的所有对象) 152 //--- 每次创建完毕后,将蛇身保存到this.elements数组中 153 var div, body; 154 for (i = 0; i < this.body.length; i++) { 155 div = document.createElement("div"); 156 div.style.width = this.width + "px"; 157 div.style.height = this.height + "px"; 158 159 body = this.body[i]; 160 div.style.backgroundColor = body.color; 161 //设置left top 162 div.style.left = body.x * this.width + "px"; 163 div.style.top = body.y * this.height + "px"; 164 this.map.appendChild(div); 165 this.elements.push(div); 166 } 167 // console.log(this.elements); 168 }; 169 170 //设置小蛇运动的方法: 171 Snake.prototype.move = function () { 172 //分析: 173 //小蛇移动时,需要内部的每个盒子都进行移动,移动是通过坐标的修改实现 174 //除蛇头以外,其余的蛇身中的每个部分都可以使用上一个蛇身的坐标值 175 //蛇头根据运动方向进行判断判断设置即可 176 var body;//用于保存蛇身中的某个对象 177 178 179 //需要使用反向遍历的方式 180 for (var i = this.body.length - 1; i >= 1; i--) { 181 //body表示蛇身中的某一个盒子的所有坐标 182 body = this.body[i]; 183 //获取前一项的坐标值即可 184 body.x = this.body[i - 1].x; 185 body.y = this.body[i - 1].y; 186 } 187 188 189 //给蛇头的坐标进行单独设置 190 //---后期添加的功能,根据当前操作的按键,设置不同的运动方向 191 //判断this.direction的值 192 switch (this.direction) { 193 case "left": 194 this.body[0].x--; 195 break; 196 case "right": 197 this.body[0].x++; 198 break; 199 case "up": 200 this.body[0].y--; 201 break; 202 case "down": 203 this.body[0].y++; 204 break; 205 } 206 207 //小蛇body中的所有坐标值更新后,需要进行重新的绘制操作 208 //this.init(); 209 }; 210 211 //暴露操作 212 window.Snake = Snake; 213 })(); 214 215 //游戏对象:用于操作food和snake进行结合,实现具体的游戏效果,规定了游戏的结束条件。。 216 (function () { 217 //游戏对象的设置: 218 //1 设置game对象的基本属性:snake food map 219 //2 设置game的初始化操作: 220 // 2.1 设置食物和小蛇显示 221 // 2.2 设置小蛇的自动运动 222 // 2.3 设置用户的键盘操作:设置keydown事件,并根据e.keyCode属性值修改snake的direction属性。 223 //3 在snake对象中修改move方法:根据2.3中修改的direction属性设置蛇头的坐标的更改方式。 224 225 var that;//在游戏的自调用函数中声明一个变量,在game功能中均可访问 226 function Game(options) { 227 //需要的属性: 小蛇 食物 地图 228 this.snake = options.snake; 229 this.food = options.food; 230 this.map = options.map; 231 that = this;//保存当前实例对象,方便访问 232 233 } 234 235 //游戏初始化操作: 236 Game.prototype.init = function () { 237 //初始化食物 238 this.food.init(); 239 //初始化小蛇 240 this.snake.init(); 241 //设置小蛇跑动:让小蛇可以自动的进行move操作 242 this.snakeRun(); 243 //设置键盘操作(改变小蛇的移动方向) 244 this.changeDirection(); 245 }; 246 247 //设置小蛇的跑动方法: 248 Game.prototype.snakeRun = function () { 249 //设置一个定时器,间隔一段时间,让小蛇执行自身的move方法即可 250 var timer = null; 251 timer = setInterval(function () { 252 var snake = that.snake;//保存了snake对象 253 var food = that.food;//保存了food对象 254 var sheBody = snake.body;//保存了snake的body属性 255 256 //获取移动之前蛇尾的横纵坐标 257 var lastX = sheBody[sheBody.length - 1].x; 258 var lastY = sheBody[sheBody.length - 1].y; 259 260 //设置小蛇的运动 261 snake.move(); 262 263 //检测小蛇是否吃到了食物 264 //使用蛇头的坐标,和食物的坐标进行对比,如果x和y均相等。说明吃到了食物,添加一个新的蛇身 265 if (sheBody[0].x === food.x && sheBody[0].y === food.y) { 266 //获取之前蛇尾(蛇身中的最后一个盒子)的坐标,设置为新的对象,放入到蛇的body属性中 267 sheBody.push({x: lastX, y: lastY, color: "orange"}); 268 //创建一个新的食物 269 food.init(); 270 } 271 272 273 //检测游戏是否结束 274 //1 如果小蛇撞墙了,游戏结束 275 //蛇头的横坐标范围为 0-39 276 //蛇头的纵坐标范围为 0-29 277 var maxX = that.map.offsetWidth / food.width - 1; 278 var maxY = that.map.offsetHeight / food.height - 1; 279 280 if (sheBody[0].x < 0 || sheBody[0].x > maxX || sheBody[0].y < 0 || sheBody[0].y > maxY) { 281 //清除定时器 282 clearInterval(timer); 283 alert("游戏结束"); 284 //检测时,运动已经执行完毕了,渲染小蛇的操作init也执行完毕了,所以死后,蛇会出到地图之外 285 return; 286 } 287 288 289 //2 如果小蛇撞到了自己,游戏结束 290 //前四个(含蛇头)都不可能被蛇头撞到,所以从第五个蛇身开始检测。i表示索引值,从4开始检测。 291 for (var i = 4; i < sheBody.length; i++) { 292 if (sheBody[0].x == sheBody[i].x && sheBody[0].y == sheBody[i].y) { 293 alert("吃到自己了,游戏结束"); 294 clearInterval(timer); 295 return; 296 } 297 } 298 299 //在游戏结束后,不会画出移动到地图外的小蛇,也不会画出吃到自己的小蛇 300 snake.init(); 301 302 303 }, 150); 304 305 }; 306 307 //设置键盘操作: 308 Game.prototype.changeDirection = function () { 309 //给键盘设置操作:使用onkeydown事件,操作后的反应更自然 310 document.onkeydown = function (e) { 311 var e = e || window.event; 312 313 //在点击按键时,返回某个按键的键盘码 314 //我们需要的按键为: 37左 38上 39右 40下 315 //console.log(e.keyCode); 316 that.keyCode = e.keyCode; 317 318 //获取小蛇当前的运动方向 319 320 var snakeDirc = that.snake.direction; 321 //根据点击后得到的键盘码,设置小蛇的不同运动方向 322 switch (true) { 323 case e.keyCode == 37 && snakeDirc != "right" : 324 that.snake.direction = "left"; 325 break; 326 case e.keyCode == 38 && snakeDirc != "down" : 327 that.snake.direction = "up"; 328 break; 329 case e.keyCode == 39 && snakeDirc != "left" : 330 that.snake.direction = "right"; 331 break; 332 case e.keyCode == 40 && snakeDirc != "up" : 333 that.snake.direction = "down"; 334 break; 335 } 336 }; 337 }; 338 //将game构造函数暴露给全局作用域 339 window.Game = Game; 340 })(); 341 342 343 var map = document.getElementById("map"); 344 var game = new Game({ 345 map: map, 346 //传入时需要考虑game功能的需求,需要操作的是实例对象的属性和方法,所以传入的也是实例对象 347 snake: new Snake({map: map}), 348 food: new Food({map: map}) 349 }); 350 351 // console.log(g1); 352 //游戏开始: 353 game.init(); 354 355 356 </script> 357 358 </body> 359 </html>