js之以面向对象的形式书写贪吃蛇

此代码存在一定的小bug,当蛇出边界之后存在一定的小问题

分析贪吃蛇功能需求:

                                 1.食物

                                   (1)每次生成一个,位置随意但不可超出规定范围

                                   (2)每次蛇吃到食物之后,前一个食物消失同时新的食物又生成

                                   (3)属性:大小、颜色、位置

                                  2.蛇

                                   (1)属性:长度,颜色,位置、方向

                                   (2)每吃掉一个食物,蛇尾新增一节,默认方向向右

                                 3.游戏对象

                                    管理食物对象和蛇对象的生成和逻辑

良好的代码书写习惯及注意:

                                 使用(function(){})(),开启自调用函数,启动新的局部作用域,防止命名冲突

                                 在防止命名冲突的同时,为了使外界可以同时访问到自调用函数中的所有对象,使用Window.函数名的方式,将其悬挂到window对象上

                                 理解此代码需要提前了解原型链的构成

                                  在index文件中注意引用文件顺序提前用的需要先引用

解决随机生成的问题:                       

                                    //使用字面量的方式创建对象,给对象的属性添加了一个 方法
                                      (function(){
                                       var Tools = {
                                                   getRandom:function(min,max){
                                                   //Math.random生成 [0,1)的随机数,Math.floor向下取整
                                                   //此时需要取到[min,max]的值,例如取[2,5],所以[0,1)*(5-2+1)+2=[2,6)即[2,5]
                                                   return Math.floor(Math.random()*(max-min+1)+min);
                                                   }
                                      }
                                     window.Tools = Tools;
                                })()
食物:
(function(){
//记录上一次创建的食物,为删除做准备
var elements=[];
function Food(options){
//new一个对象时如果没有传参数,此时options为空,后面的语句就会出错所以需要给options一个默认值
options = options || {};
this.x = options.x || 0;
this.y = options.y || 0;
this.width = options.width || 20;
this.height = options.height || 20;
this.color = options.color || 'pink';
}
//应该把食物传到父容器上
Food.prototype.render = function(map){
//删除之前创建的食物
remove();
this.x = Tools.getRandom(0,map.offsetWidth/this.width-1)*this.width;
this.y = Tools.getRandom(0,map.offsetHeight/this.height-1)*this.height;
//动态创建div,也就是食物
var div = document.createElement('div');
map.appendChild(div);
 
elements.push(div);
//设置div的样式
div.style.position = 'absolute';
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.color;
 
}
function remove(){
//遍历删除数组中的元素
for(var i=elements.length-1;i>=0;i--){
//找到父元素移除其子元素,删除div
elements[i].parentNode.removeChild(elements[i]);
//删除数组中的元素
elements.splice(i,1);
}
}
//外部无法直接访问测试代码,利用window全局对象访问
// var food = new Food();
// var map = document.getElementById('map');
// food.render(map);
window.Food = Food;
})()
//var food = new Food();
//var map = document.getElementById('map');
//food.render(map);
蛇:
(function(){
//面向对象过程中用变量来存储内容,便于后续的维护开发
var position = 'absolute';
//记录之前创建的蛇
var elements = [];
function Snake(options){
options = options || {};
//设置蛇节的大小
this.width = options.width || 20;
this.height = options.height || 20;
//设置蛇节的方向,默认往右移动
this.direction = options.direction || 'right';
//设置蛇的身体,默认有三个块
this.body = [
//第一个蛇头的位置x:3左边距离有三个方块,y:2上面距离有两个方块
{x:3,y:2,color:'lightgreen'},
{x:2,y:2,color:'white'},
{x:1,y:2,color:'white'}
];
}

//将蛇渲染到地图上
Snake.prototype.render = function(map){
//每次render渲染的时候都应该先移除掉之前创建的蛇
remove();
//要把蛇的每一个部分都渲染到地图上
//i<len比i<length的好处是,每循环一次this.body.length都会计算一次,但是如果在var中定义就只会执行一次,这样会提高效率
for(var i=0, len = this.body.length;i<len;i++){
var obj = this.body[i];
var div = document.createElement('div');
map.appendChild(div);

//记录当前的蛇
elements.push(div);
//设置样式
div.style.position = position;
div.style.width = this.width + 'px';
div.style.height = this.height + 'px';
//个数乘边距离,就是位置
div.style.left = obj.x*this.width +'px';
div.style.top = obj.y*this.height + 'px';
div.style.backgroundColor = obj.color;
}
}

function remove(){
for(var i=elements.length-1;i>=0;i--){
//删除div
//删除数组中的元素
elements[i].parentNode.removeChild(elements[i]);
elements.splice(i,1);
}
}

//控制蛇移动的方法
Snake.prototype.move = function(food,map){
 
//控制蛇的身体移动,每次移动当前蛇节走到上一个蛇节的位置
//只取身体部分不取头部
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;
}
//控制蛇头的移动
//判断蛇移动的方向
var head = this.body[0];
switch(this.direction){
case 'right':
head.x +=1;
break;
case 'left':
head.x -=1;
break;
case 'top':
head.y -=1;
break;
case 'bottom':
head.y +=1;
break;
}

//判断食物的坐标是否和蛇头坐标重合
if(food.x === head.x*this.width && food.y === head.y*this.height){
//让蛇身体增加一格
//获取蛇的最后一节
var last = this.body[this.body.length-1];
//将最后一节的属性作为新属性赋值给新的一节
this.body.push({
x:last.x,
y:last.y,
color:last.color
})
//随机在地图上重新生成食物
food.render(map);
}
}
 
//测试:先将Sanke挂到Window对象上,便于外部测试访问
window.Snake = Snake;
})()

 游戏对象: 

(function(){
//记录游戏对象,避免定时器this指向错误
var that;
function Game(map){
this.food = new Food();
this.snake = new Snake();
this.map = map;
that = this;
}
Game.prototype.start = function (){
//把食物和蛇渲染到地图
this.food.render(this.map);
this.snake.render(this.map);
//测试move方法
// this.snake.move();
// this.snake.render(this.map);
// this.snake.move();
// this.snake.render(this.map);
 
//开始游戏逻辑:让蛇移动起来;用键盘控制蛇移动的方向;当蛇遇到食物做相应的处理;当蛇遇到边界,游戏结束
runSnake();
controlSnake();
}
//通过键盘控制蛇的移动
function controlSnake(){
document.addEventListener('keydown',function(e){
//console.log(e.keyCode);
//输出按下键盘方向的键盘码:37-left,38-top,39-right,40-bottom
switch(e.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);
}
//开启定时器让蛇移动,用私有函数而不是原型,因为不需要外部访问
function runSnake(){
var timerId = setInterval(function (){
//让蛇走一格,在定时器中this指向window,没法用this.sanke
that.snake.move(that.food,that.map);
that.snake.render(that.map);
//蛇遇到边界游戏结束
//获取蛇头的坐标
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;
//此时有个小问题,alert之后蛇头仍然会超出边界,这是渲染问题
if(headX < 0 || headX >= maxX){
alert('Game Over');
clearInterval(timerId);
timerId = null;
}
if(headY < 0 || headY >= maxY){
alert('Game Over');
clearInterval(timerId);
timerId = null;
}
},300);
}
 
window.Game = Game;
})()

测试:        

(function(){
//测试代码
var map = document.getElementById('map');
var game = new Game(map);
game.start();
})()

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>贪吃蛇</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="map"></div>
<script src="js/tool.js"></script>
<script src="js/food.js"></script>
<script src="js/snake.js"></script>
<script src="js/game.js"></script>
<script src="js/main.js"></script>
</body>
</html>
 

猜你喜欢

转载自www.cnblogs.com/toMe-studio/p/11333407.html