废话不多说直接上代码,大家有时间摸鱼的话可以复制代码到本地过一遍代码,没时间摸鱼的点个赞再走吧哈哈哈哈哈哈哈。❤
HTML页面代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#map {
position: relative;
width: 400px;
height: 400px;
background: #000;
}
</style>
</head>
<body>
<h1 id="grade">0</h1>
<div id="map"></div>
</body>
</html>
script脚本代码之Map类
/*
渲染map页面数据
--setData(newData) 拿到需要渲染到页面的数据(蛇和食物)
--clearData() 清空所有数据(蛇和食物)
--include({x,y}) 判断已经在map上的坐标是否包含传进来的数据
--render() 把数据渲染上(map)页面
*/
class Map {
/*
el是<div id="map"></div>
rect是每一个格子的大小
this.data是在地图中的蛇和食物的对象数组
*/
constructor(el,rect = 10){
this.el = el;
this.rect = rect;
this.data = [
// {
// x: 10,
// y: 5,
// color: "red"
// }
];
//几行几列
this.rows = Math.ceil(Map.getStyle(el,"height") / rect);
this.cells = Math.ceil(Map.getStyle(el,"width") / rect);
//设置map的宽高
Map.setStyle(el,"height",this.rows*rect);
Map.setStyle(el,"width",this.cells*rect);
}
//静态属性通过类来调用
static getStyle(el,attr){
return parseFloat(getComputedStyle(el)[attr])
}
//用来设置样式
static setStyle(el,attr,val){
el.style[attr] = val + "px";
}
//将数据(包括蛇和食物的数组)拼接在this.data中
setData(newData){
this.data = this.data.concat(newData);
}
//清空数据
clearData(){
this.data.length = 0;
}
//判断已经在map上的坐标是否包含传进来的数据
include({x,y}){
return !!this.data.find(item=>(item.x==x&&item.y==y));
// return this.data.some(item=>(item.x==x&&item.y==y));
}
//把数据渲染到map(页面)上面
render(){
this.el.innerHTML = this.data.map(item=>{
return `<span style="position:absolute;left:${item.x*this.rect}px;top:${item.y*this.rect}px;width:${this.rect}px;height:${this.rect}px;background:${item.color};"></span>`
}).join("");
}
}
script脚本代码之Food类
/*
创建map上的食物类
--create() 每一次调用该方法都创建一个食物
*/
class Food {
//在食物这map的作用是传递cells和rows
//可以通过传值的方式把这两个参数传递进去
//把食物渲染到哪张地图上
//map是实例化对象
constructor(map,colors = ['red',"blue","yellow","pink"]){
this.map = map;
//食物默认随机的颜色有红、蓝、黄、粉
this.colors = colors;
//创建出来的食物存在这里(Food类)的this.data中
//食物每次只有一个
this.data = null;
this.create();
}
//创建出在页面上随机生成的食物
create(){
//以cells为6是为例随机位置为0、1、2、3、4、5
let x = Math.floor(Math.random()*this.map.cells);
let y = Math.floor(Math.random()*this.map.rows);
//食物已经可以随机出现在地图上的某处
// console.log(x,y);
let color = this.colors[parseInt(Math.random()*this.colors.length)];
//食物已经可以出现默认的随机颜色了
// console.log(x,y,color);
//这里的this.data是食物的数据(包括坐标和颜色)
this.data = {x,y,color};
//下面的四条语句可以拆离出去在控制器中完成
//自己决定能不能用不能用就在创建一个
//当map上的数据已经包含了该随机生成的食物数据
//我们需要重新再重新创建一个食物数据
if(this.map.include(this.data)){
this.create();
}
//然后加入map类中的(总)this.data
this.map.setData(this.data);
}
}
script脚本代码之Snake类
/*
--this.data 蛇的数据
--move() 蛇能移动的逻辑
--changeDir(dir) 改变蛇移动的方向
--eatFood() 蛇吃到食物
*/
class Snake {
//初始化一条蛇
constructor(map){
//第一条数据是绿色的蛇头
//头尾尾尾尾
//如果 蛇头 吃到了食物增加这里的数据
this.data = [
{x: 6, y: 4, color: "green"},
{x: 5, y: 4, color: "white"},
{x: 4, y: 4, color: "white"},
{x: 3, y: 4, color: "white"},
{x: 2, y: 4, color: "white"}
];
this.map = map;
//蛇头移动的方向
this.direction = "right";
//地图只有在初始化渲染蛇的数据的时候用到了
//可以把它抽出来在game中做
//这里的this.data是蛇的数据
this.map.setData(this.data);
}
move(){
let i = this.data.length-1;
//把最后一份数据存一份一旦它吃到食物则增加一份
this.lastData = {
x: this.data[i].x,
y: this.data[i].y,
color: this.data[i].color
}
/*让后边每一格走到前一格的位置上*/
//不动蛇头
for(i; i > 0; i--){
this.data[i].x = this.data[i-1].x;
this.data[i].y = this.data[i-1].y;
}
// 根据方向移动蛇头
switch(this.direction){
case "left":
this.data[0].x--;
break;
case "right":
this.data[0].x++;
break;
case "top":
this.data[0].y--;
break;
case "bottom":
this.data[0].y++;
break;
}
}
changeDir(dir){
// 如果蛇本身现在在左右移动,我们只能修改让蛇上下移动
if(this.direction === "left"||this.direction === "right"){
if(dir==="left"||dir==="right"){
return false; // 不能修改方向
}
} else {
//如果蛇正在上下移动我们在让其上下移动返回false
//返回false即修改不成功
if(dir==="top"||dir==="bottom"){
return false; // 不能修改方向
}
}
this.direction = dir;
return true;
// 如果蛇正在上下移动,我们只能修改让蛇左右移动
}
// 吃到了食物蛇应该变大了
eatFood(){
this.data.push(this.lastData);
}
}
script脚本代码之Game类
/*
start 开始游戏
stop 暂停游戏
isOver 判断是否结束
control 游戏控制器
*/
class Game {
//什么时候吃到食物
constructor(el,rect,toControl = null,toGrade=null){
// let gameMap = new Map(map,10);
// let gameFood = new Food(gameMap);
// let gameSnake = new Snake(gameMap);
//前三句变为下面一句
//let game = new Game(map,10);
//实现方法为下面几句:
this.map = new Map(el,rect);
this.food = new Food(this.map);
this.snake = new Snake(this.map);
//初始化渲染蛇和食物到页面上
this.map.render();
//Game
//timer不断增长
this.timer = 0;
this.interval = 200;
this.toControl = toControl;
//绑定事件
this.keyDown = this.keyDown.bind(this);
//分数
this.grade = 0;
this.toGrade = toGrade;
this.control();
}
// 开始游戏
start(){
this.move();
}
// 暂停游戏
stop(){
clearInterval(this.timer);
}
// 控制移动
move(){
this.stop();
this.timer = setInterval(()=>{
this.snake.move();
//先清空之前的数据
this.map.clearData();
if(this.isEat()){
console.log('吃到食物');
//计算分数
this.grade++;
//吃食物变大的方法
this.snake.eatFood();
//重新在地图上渲染出随机食物
this.food.create();
//触发分数改变
this.changeGrade(this.grade);
//提高速度
this.interval *= .9;
//提高速度后重启定时--clearInterval
this.stop();
//提高速度后重开定时器--this.move()
this.start();
if(this.grade >= 5){
//调用over告知游戏结束你赢了
this.over(1);
}
}
if(this.isOver()){
//调用over告知游戏结束你输了
this.over(0);
return;
}
//设置蛇和食物的数据
this.map.setData(this.snake.data);
this.map.setData(this.food.data);
// 显示蛇和食物
this.map.render();
},this.interval);
}
// 判断是否吃到食物
isEat(){
//蛇头的位置是否和食物的位置一样
return (this.snake.data[0].x === this.food.data.x)&&(this.snake.data[0].y === this.food.data.y);
}
//判断是否结束
isOver(){
// 判断蛇出了地图
// 判断蛇头的位置
if(this.snake.data[0].x < 0
|| this.snake.data[0].x >= this.map.cells
|| this.snake.data[0].y < 0
|| this.snake.data[0].y >= this.map.rows){
return true; //返回true则出了地图
}
// 判断蛇撞到了自己
// 蛇头一直在动 蛇尾都是跟着蛇头动
// 判断蛇头是否和其中任意一个蛇尾的坐标数据相同
for(let i = 1; i < this.snake.data.length; i++){
if(this.snake.data[0].x == this.snake.data[i].x
&& this.snake.data[0].y == this.snake.data[i].y){
return true; //返回true则重叠了
}
}
return false;
}
// 游戏结束
/*
overState
0 中间停止,完挂了
1 胜利了游戏结束
*/
over(overState = 0){
if(overState){
this.toWin&&this.toWin();
} else {
this.toOver&&this.toOver();
}
this.stop();
}
// 分数改变
changeGrade(grade){
this.toGrade&&this.toGrade(grade);
}
keyDown({keyCode}){
//console.log(keyCode);
//这里是上下左右,默认的控制方向键
let isDir;
switch(keyCode){
case 37:
isDir = this.snake.changeDir("left");
break;
case 38:
isDir = this.snake.changeDir("top");
break;
case 39:
isDir = this.snake.changeDir("right");
break;
case 40:
isDir = this.snake.changeDir("bottom");
break;
}
}
// 控制器
control(){
if(this.toControl){
this.toControl();
return ;
}
window.addEventListener("keydown",this.keyDown);
}
addControl(fn){
fn.call(this);
window.removeEventListener("keydown",this.keyDown);
}
}
script脚本代码之执行逻辑
{
let map = document.querySelector("#map");
let game = new Game(map,10);
//
let gradeEl = document.querySelector("#grade")
game.toGrade = function(grade){
console.log(grade);
gradeEl.innerHTML = grade;
};
document.onclick = function(){
game.start();
};
game.toOver = function(){
alert("游戏结束");
}
game.toWin = function(){
alert("太棒了,您完成了游戏");
}
}