<body>
<button class="start">开始游戏</button>
<button class="stop">暂停游戏</button>
<button class="reset">重置游戏</button>
<div class="map"></div>
<script>
/*实现贪吃蛇效果
*1.先将贪吃蛇游戏整体看成是一个构造函数(类)
* 贪吃蛇游戏所具备的属性以及方法有:
* 属性:?和蛇的食物
*
*2.蛇可以看成是一个对象,构造函数(类)
* 蛇所具备的属性以及方法有:
* 属性: a.如果蛇想要移动,则得有可以移动的属性也就是身体
* b.如果蛇想要去吃东西,那么就得有属性头:使蛇头能够与食物所在的坐标重合,就可以实现吃东西的效果了
* c.还有蛇头以及蛇的身体的图片属性
* 方法: a.蛇的移动方法:为了能够让蛇在规定的范围内可以移动
* b.蛇的转向方法:为了让蛇能够在用户点击上下左右键的时候,蛇头能够直接改变其所在的转向
*
*3.食物可以看成是一个构造函数(类)
* 食物所具备的属性以及方法有:
* 属性:只有一个:食物的图片属性;没有方法:因为在此游戏中食物只有一个而且还是在规定范围内的随机的位置,所以此方法应该是游戏的方法
*/
// 先定义游戏的构造函数
// 只有获取到了map元素,才能让图片上树,否则看不到情况:由于是让蛇的图片进行上树,那么就得给蛇添加属性
// 获取元素
var start = document.querySelector(".start");
var stop = document.querySelector(".stop");
var reset = document.querySelector(".reset");
// 给其分别添加点击事件
start.onclick = function(){
// 开始游戏需要判定信号量
if(game.flex){
return;
}
game.start();
}
stop.onclick = function(){
game.stop();
}
reset.onclick = function(){
game.reset();
}
function Game(snake,food,map){
this.snake = snake;
this.food = food;
this.map = map;
this.timer = null;
// 设置行列
this.row = 30;
this.column = 40;
// 设置游戏的开关
this.flex = true;
// 设置食物的坐标,随机坐标
this.foodX = parseInt(Math.random() * this.column);
this.foodY = parseInt(Math.random() * this.row);
// 给整个游戏设置一个初始化函数,调用该函数
this.init();
}
// 在蛇将该创建的元素都已经创建好了之后就要将这些元素进行上树
// 定义Game的构造函数的原型方法
// 运用的是第二种方法:直接重定义Game的原型对象
Game.prototype = {
constructor:Game,
// 使创建的元素进行上树操作,定义渲染方法
renderSnake:function(){
// console.log(this);
for(let i = 0;i < this.snake.bodyArr.length;i++){
this.map.appendChild(this.snake.bodyEleArr[i]);
}
},
// 设置一个定时器,因为后面结束游戏需要关闭定时器,所以需要一个变量来保存计数器
// 因为设置定时器是为了能够让游戏开始,蛇进行移动,所以给整个game来设置方法
start:function(){
// console.log(this);
// 游戏开始
this.flex = true;
// 定时器的this指向永远指向window
this.timer = setInterval(function(){
// 此时,要想用定时器rang蛇进行移动,那么就必须是将newHead的位置传入数组中的同时,渲染其样式
this.snake.forword();
// 每当蛇移动一次就要检测边界一次
this.check();
// 同时进行判断,来判断游戏是否开始,防止游戏已经结束了还在渲染
if(this.flex){
this.snake.render();
}
}.bind(this),500);
},
// 再给游戏添加一个游戏结束的方法
stop:function(){
// 关闭开关
this.flex = false;
// 关闭定时器
clearInterval(this.timer);
},
// 边界判定以及判断蛇头与食物是否重合
check: function(){
// 要想判定蛇头是否出边界,先要获取到蛇头的位置
var headX = this.snake.bodyArr[this.snake.bodyArr.length - 1].x;
var headY = this.snake.bodyArr[this.snake.bodyArr.length - 1].y;
// 进行边界判定,由于是x是从0开始,所以最后一个是39和29
if(headX < 0 || headX > this.column - 1 || headY < 0 || headY > this.row - 1){
this.stop();
}
// 判断蛇头与食物是否重合,只要重合就让蛇的身体长长一个,食物的位置重置
if(headX === this.foodX && headY === this.foodY){
//食物和蛇头重合的话,就蛇的身体生长,食物的位置重置以及各自的渲染
this.snake.growUp();
this.renderSnake();
this.foodReset();
this.renderFood();
}
},
// 给整个游戏设置一个初始化游戏的方法
init:function(){
// 渲染蛇,渲染食物
this.renderSnake();
this.renderFood();
// 条用事件函数
this.bindEvent();
},
// 在设置一个方法,用于让用户输入键盘上的上下左右键来控制蛇的移动方向
// 绑定事件
bindEvent:function(){
var _this = this;
// 设置键盘输入事件:键盘按下事件
document.onkeydown = function(e){
// 兼容性
var e = window.e || e;
var code = e.keyCode;
console.log(code);
// 利用keycode:获取字符的acsii码值进行判断
if(code === 37){
// 由于此时的this指向的是document,所以要有一个变量来保存前面的this
// 此时通过判断来改变蛇头的方向
_this.snake.changeDirection("left");
}else if(code === 38){
_this.snake.changeDirection("up");
}else if(code === 39){
_this.snake.changeDirection("right");
}else if(code === 40){
_this.snake.changeDirection("down");
}
}
},
// 要想让食物渲染在整个游戏的空间里的任何一个地方,那么就要让食物在整个游戏中任意出现
renderFood:function(){
// 渲染完样式之后,在让其上树
this.map.appendChild(this.food.ele);
// 在整个游戏中设置其位置,随机
this.food.ele.style.left =this.foodX * 20 + "px";
this.food.ele.style.top =this.foodY * 20 + "px";
} ,
// 食物重置
foodReset:function(){
// 更新食物的坐标,先要创建食物的坐标
this.foodX = parseInt(Math.random() * this.column);
this.foodY = parseInt(Math.random() * this.row);
},
// 事件重置,蛇重置,食物也要重置
reset:function(){
console.log(code);
// 先让原先的蛇下树
for(let i = 0;i < this.snake.bodyEleArr.length;i++){
this.snake.bodyEleArr[i].remove();
}
// 然后重置
this.snake.reset();
// 渲染新的蛇
this.renderSnake();
}
}
// 定义蛇的构造函数
function Snake(headImg,bodyImg){
this.headImg = headImg;
this.bodyImg = bodyImg;
// 给蛇一开始设置身体加上头部的长度
this.bodyArr = [{x: 0,y: 0},{x: 1,y: 0}];
// 给蛇添加属性,使其能够让蛇一开始的身体不管有多少个都能够创建元素
// 用数组的map映射方法,用来将该数组中的任何一个都能够创建元素
this.bodyEleArr = this.bodyArr.map( function(){
return document.createElement("div");
});
// 给蛇的身体与头部添加width和height,常量不用传值
this.stepX = 20;
this.stepY = 20;
// 设置蛇头移动的方向:默认为向右
this.dirction = "right";
}
// 定义蛇的构造函数的原型方法
// 运用的是第二种方法:直接重定义蛇的原型对象
Snake.prototype = {
constructor:Snake,
// 定义蛇的渲染方法,让其具有样式
render:function(){
// 将蛇的身体与头部分开,先给身体设置样式,因为长度不固定,所以要用到for循环
// 数组的前面都是身体,最后一个是头部
for(let i = 0;i < this.bodyArr.length - 1;i++){
// 给蛇的身体添加样式
this.bodyEleArr[i].style.backgroundImage = "url(" + this.bodyImg + ")";
this.bodyEleArr[i].style.width = this.stepX + "px";
this.bodyEleArr[i].style.height = this.stepY + "px";
this.bodyEleArr[i].style.position = "absolute";
this.bodyEleArr[i].style.left = this.bodyArr[i].x * this.stepX + "px";
this.bodyEleArr[i].style.top = this.bodyArr[i].y * this.stepY + "px";
}
// 给蛇的头部添加样式
this.bodyEleArr[this.bodyArr.length - 1].style.backgroundImage = "url(" + this.headImg + ")";
this.bodyEleArr[this.bodyArr.length - 1].style.width = this.stepX + "px";
this.bodyEleArr[this.bodyArr.length - 1].style.height = this.stepY + "px";
this.bodyEleArr[this.bodyArr.length - 1].style.position = "absolute";
this.bodyEleArr[this.bodyArr.length - 1].style.left = this.bodyArr[this.bodyArr.length - 1].x * this.stepX + "px";
this.bodyEleArr[this.bodyArr.length - 1].style.top = this.bodyArr[this.bodyArr.length - 1].y * this.stepY + "px";
},
// 定义蛇的前进的方法
forword:function(){
// 在同一个方向上移动位置
// 是将蛇的尾巴砍下,让头部往前进一步
// 先将尾巴砍下,数组
this.bodyArr.shift();
// 在给蛇的头部设置一个新的位置,在此基础上先要判断蛇移动的方向是如何
//获取旧的头部位置
var oldHead = this.bodyArr[this.bodyArr.length - 1];
// 定义一个新的头部位置
var newHead = {
x: oldHead.x,
y: oldHead.y
}
if(this.dirction === "left"){
newHead.x--;
}else if(this.dirction === "right"){
newHead.x++;
}else if(this.dirction === "up"){
newHead.y--;
}else if(this.dirction === "down"){
newHead.y++;
}
//要想移动就得将其放入数组中,才能上树
this.bodyArr.push(newHead);
},
// 定义一个蛇头改变方向的方法
changeDirection:function(dirction){
// 用dirction来改变方向
this.dirction = dirction;
},
// 定义蛇的生长函数
growUp:function(){
// 想让蛇长大,那么就必须让身体多一个,直接在数组的头部添加一个
this.bodyArr.unshift(this.bodyArr[this.bodyArr.length - 1]);
// 添加完成之后就要创建其元素
this.bodyEleArr.unshift(document.createElement("div"));
},
// 蛇的重置
reset:function(){
// 蛇的重置就是将一开始的状态再写一遍
// 给蛇一开始设置身体加上头部的长度
this.bodyArr = [{x: 0,y: 0},{x: 1,y: 0}];
// 给蛇添加属性,使其能够让蛇一开始的身体不管有多少个都能够创建元素
// 用数组的map映射方法,用来将该数组中的任何一个都能够创建元素
this.bodyEleArr = this.bodyArr.map( function(){
return document.createElement("div");
});
//当蛇重置好就要给他渲染样式
this.render();
}
}
// 定义食物的构造函数
function Food(foodImg){
this.foodImg = foodImg;
// 在此创建元素
this.ele = document.createElement("div");
}
// 给食物设置食物的原型对象
Food.prototype = {
constructor:Food,
// 给食物添加方法
// 食物的渲染方法:要想要渲染,则必须将其创建出来
render:function(){
this.ele.style.position = "absolute";
this.ele.style.width = 20 + "px";
this.ele.style.height = 20 + "px";
this.ele.style.backgroundImage = "url(" + this.foodImg + ")";
}
}
// 定义一个蛇对象
var snake = new Snake("./imgs/head.png","./imgs/body.png");
// 调用蛇的渲染方法
snake.render();
// 定义食物对象
var food = new Food("./imgs/food.png");
food.render();
// 获取到map元素
var map = document.querySelector(".map");
// 定义一个游戏的对象并进行传参
var game = new Game(snake,food,map);
</script>
</body>