# 贪吃蛇 **Repository Path**: duans/retro_snaker ## Basic Information - **Project Name**: 贪吃蛇 - **Description**: js网页版贪吃蛇 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2018-09-15 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #贪吃蛇案例 ## 食物对象 ### 全局变量 ``` // 定位模式 var position = 'absolute'; // 用于保存食物dom对象的数组 var elements = []; ``` ### Food构造函数 - 参数: - map: 地图对象 - options: 食物属性对象 - 注意 - 构造函数只设置相关属性 ``` function Food(map, options) { // 防止用户不传递参数导致代码报错 options = options || {} this.width = options.width || 20; this.height = options.height || 20; this.backgroundColor = this.backgroundColor || 'green'; this.position = position; this.x = options.x || 0; this.y = options.y || 0; } ``` ### Food.prototype.render方法 - 作用: 把食物渲染到地图上 - 参数: map(地图对象) - 步骤: 1. 清除之前的食物 2. 将食物div挂载到页面上 3. 将食物对象存入全局数组`elements`中 4. 使用随机数为食物div生成随机坐标 5. 设置食物div的样式 - 注意: - 设置食物`width height left top`等属性的时候一定要记得带上单位`px` - 生成随机坐标的时候 ``` Food.prototype.render = function (map) { // 1-先删除之前的食物 remove(); //创建div div = document.createElement('div'); // 2-将食物div挂载到页面上 map.appendChild(div); // 3-将食物的dom对象存入数组 elements.push(div); // 4-随机生成x坐标 this.x = Tools.getRandom(0, map.offsetWidth / this.width - 1) * this.width; // 随机生成y坐标 this.y = Tools.getRandom(0, map.offsetHeight / this.height - 1) * this.height; // 5-设置样式 div.style.left = this.x + 'px'; div.style.top = this.y + 'px'; div.style.width = this.width + 'px'; div.style.height = this.height + 'px'; div.style.backgroundColor = this.backgroundColor; div.style.position = this.position; } ``` ### remove函数 - 作用: 清除食物 - 参数:无 - 注意: - 删除数组中的元素,一定要从后往前删除,否则无法将数组中的元素删除干净,因为从前面开始删除的话,数组的索引会重排. - 先删除页面上的元素,在删除`elements`数组中的元素,顺序不能颠倒,因为删除页面中的食物`div`的时候需要通过`elements[i].parentNode`找到父级元素,如果先删除`elements`数组中的元素的话,会导致`elements[i]`为`undefined`. ``` function remove() { // 1-删除数组;删除数组的时候一定要从后往前删,否则数组元素删除不完 for (var i = elements.length - 1; i >= 0; i--) { // 注意:顺序不能颠倒 // 1-删除页面上的食物:先找到父元素,然后父元素调用removeChild()删除子元素 elements[i].parentNode.removeChild(elements[i]); // 2-从数组中删除 elements.splice(i, 1); } } ``` ### 使用自调用函数 - 目的:解决命名冲突 - 注意: - 一定要通过`window.Food`将构造函数暴露给外部作用域 ### 测试 ``` var map = document.getElementById('container'); var f = new Food(); f.render(map); ``` ## 蛇对象 ### 全局变量 ``` var position = 'absolute'; // 用于保存蛇节dom对象的数组 var elements = []; ``` ### Snake构造函数 - 参数: - options:蛇的相关属性 ``` function Snake(options) { // 防止用户不传递参数导致代码报错 options = options || {}; this.width = options.width || 20; this.height = options.height || 20; this.position = position; // 记录蛇移动的方向 this.direction = options.direction || 'right'; // 蛇身配置信息,蛇初始状态由3个蛇节组成 this.body = [ {x: 2,y: 0,color: 'red'}, {x: 1,y: 0,color: 'blue'}, {x: 0,y: 0,color: 'blue'} ] } ``` ### Snake.prototype.render渲染方法 - 作用: 将蛇节渲染到地图上 - 参数: - map: 地图对象 - 注意: - 设置蛇节的属性时,一定要记得加上单位`px` ``` Snake.prototype.render = function (map) { for (var i = 0; i < this.body.length; i++) { // 1-创建蛇节div var div = document.createElement('div'); // 2-将蛇节对象挂载到地图上 map.appendChild(div); // 3-将蛇节对象存入elements数组 elements.push(div); div.style.position = this.position; div.style.width = this.width + 'px'; div.style.height = this.height + 'px'; div.style.backgroundColor = this.body[i].color; div.style.left = this.body[i].x * this.width + 'px'; div.style.top = this.body[i].y * this.height + 'px'; } } ``` ### Snake.prototype.move 移动方法 - 作用: 控制蛇节移动 - 参数: - food: 食物对象 - map: 地图对象 - 步骤: 1. 删除原来的蛇 2. 移动蛇节:修改蛇节坐标信息--把前一节的坐标赋值给后一节即可,蛇头需要根据移动方向做特殊处理 3. 根据移动方向对蛇头的坐标做特殊处理 4. 判断蛇是否吃到食物:判断蛇头和食物的坐标是否重合 - 重新生成食物:调用`food.render(map)`即可 - 增加蛇身长度:只修改`this.body`数组即可 - 注意: - 判断蛇头是否吃到食物:此处蛇节的x,y存储的不是具体的像素单位,要转换成具体的像素单位,再和食物的坐标进行比较 ``` Snake.prototype.move = function (food,map) { // 1-删除原来的蛇 remove(); // 2-移动蛇节:把前一节的坐标赋值给后一节即可;蛇头需要特殊处理,和移动方向相关 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; } // 3-根据移动方向对蛇头做特殊处理 var head = this.body[0]; switch (this.direction) { case 'left': head.x -= 1; break; case 'top': head.y -= 1; break; case 'right': head.x += 1; break; case 'bottom': head.y += 1; break; } /* 4-判断蛇头是否吃到食物 注意:此处蛇节的x,y存储的不是具体的像素单位,要转换成具体的像素单位,再和食物的坐标进行比较 */ var headX=head.x*this.width; var headY=head.y*this.height; if(headX==food.x&&headY==food.y){ // 4.1-重新生成食物 food.render(map); // 4.2-增加蛇身 var last=this.body[this.body.length-1]; this.body[this.body.length]={ x:last.x, y:last.y, color:last.color } } } ``` ### remove函数 - 参数: 无 - 功能: 删除原来的蛇对象 - 注意: - 删除数组中的元素,一定要从后往前删除,否则无法将数组中的元素删除干净,因为从前面开始删除的话,数组的索引会重排. - 先删除页面上的元素,在删除`elements`数组中的元素,顺序不能颠倒,因为删除页面中的食物`div`的时候需要通过`elements[i].parentNode`找到父级元素,如果先删除`elements`数组中的元素的话,会导致`elements[i]`为`undefined`. ``` function remove() { for (var i = elements.length - 1; i >= 0; i--) { // 1-删除页面上的div elements[i].parentNode.removeChild(elements[i]); // 2-删除数组中的元素 elements.splice(i, 1); } } ``` ### 使用自调用函数 - 作用: 解决命名冲突 - 注意: - 一定要通过`window.Snake`将构造函数暴露给外部作用域 ### 测试 ``` var map = document.getElementById('container'); var s1 = new Snake(); s1.render(map); for(var i=0;i<10;i++){ s1.move(map); } ``` ## 游戏对象 ### 全局变量 ``` // 记录游戏对象 var that; ``` ### Game构造函数 - 参数: - map: 地图对象 ``` function Game(map) { //蛇对象 this.snake = new Snake(); //食物对象 this.food = new Food(); //地图对象 this.map = map; that = this; } ``` ### Game.prototype.start 方法 - 作用: 开始游戏 - 参数: 无 ``` Game.prototype.start = function () { this.snake.render(this.map); this.food.render(this.map); // 控制蛇移动 runSnake(); // 注册键盘事件-控制移动方向 bindKey(); } ``` ### bindKey 函数 - 作用: 注册键盘事件,控制移动方向 - 参数: 无 - 注意: - 函数中使用全局变量`that`记录游戏对象,通过`that.snake`指向蛇对象 ``` //注册鼠标事件 function bindKey() { document.addEventListener('keydown', function (event) { // 事件对象兼容处理 event = window.event || event; switch (event.keyCode) { case 37: that.snake.direction = 'left'; break; case 38: that.snake.direction = 'top'; break; case 39: that.snake.direction = 'right'; break; case 40: that.snake.direction = 'bottom'; break; } }, false) } ``` ### runSnake 函数 - 作用: 通过定时器控制蛇移动 - 参数: 无 - 步骤: 1. 移动: 调用 `that.snake.move(that.food,that.map)` 2. 重新渲染蛇身: 调用`that.snake.render(that.map)` 3. 控制蛇头不能走出地图 - 走出地图结束游戏: 清除定时器 - 注意: - ``` function runSnake() { var timerId = setInterval(function(){ // 1-移动:仅仅修改蛇节坐标信息 that.snake.move(that.food,that.map); // 2-重新在页面上绘制一条蛇 that.snake.render(that.map); // 3-控制蛇头不能走出地图 var maxX=that.map.offsetWidth/that.snake.width; var maxY=that.map.offsetHeight/that.snake.height; var headX=that.snake.body[0].x; var headY=that.snake.body[0].y; if(headX<0||headX>=maxX){ alert('game over!!!'); clearInterval(timerId); } if(headY<0||headY>=maxY){ alert('game over!!!'); clearInterval(timerId); } }, 150); } ``` ### 使用自调用函数 - 作用: 解决命名冲突 - 注意: - 一定要通过`window.Game`将构造函数暴露给外部作用域 ### 测试 ``` var map = document.getElementById('container'); var g1 = new Game(map); g1.start(); ``` ## Tools工具对象 ### getRandom 方法 - 作用: 获取指定范围的随机整数 - 参数: - min: 最小数 - max: 最大数 ``` var Tools={ // 生成指定范围的随机整数 getRandom:function(min,max){ return Math.floor(Math.random()*(max-min+1))+min; } } ``` ## 地图对象 - html ``` 贪吃蛇
``` - css ``` *{ padding:0; margin: 0; } #container{ position: relative; width: 800px; height: 600px; background: #CCC url(../img/bg.png); margin: 10px auto; border:1px solid lightgreen; box-sizing:border-box; } ``` ###代码压缩混淆在线工具 [oschina](http://tool.oschina.net/jscompress/)