不同模块写入注释中,稍难理解,加油。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0
}
</style>
</head>
<body>
</body>
<script>
function SnakeGame() {
this.cols = 20;
this.rows = 30;
this.snake = [];//蛇本体的每一部分 是一个数组
//初始化游戏是否开始
this.playing = false;
}
SnakeGame.prototype = {
/**
* 初始化游戏窗口
*/
initGame() {
this.screen = document.createElement("div");
// 设置 舞台定位方式为相对定位
this.screen.style.position = "relative";
this.screen.style.width = "400px";
this.screen.style.height = "600px";
this.screen.style.border = "5px solid lightblue"
this.screen.style.margin = "100px auto 0";
this.screen.style.fontSize = "0";
//画出单元格
for (let i = 0; i < this.rows; i++) {
for (let j = 0; j < this.cols; j++) {
// 创建一个 span 标签
let span = document.createElement("span");
// 将 span 设置为 行内块
span.style.display = "inline-block";
span.style.width = "19px";
span.style.height = "19px";
span.style.borderRight = "1px solid #ddd"
span.style.borderBottom = "1px solid #ddd"
//给初始化的蛇身(左上角三个)添加 class = sk
if (i == 0 && j < 3) {
span.classList.add("sk")
}
this.screen.append(span);
}
}
//初始化蛇身 3个p标签
for (let i = 0; i < 3; i++) {
let p = document.createElement("p");
p.style.position = "absolute";
p.style.width = "20px";
p.style.height = "20px";
p.style.backgroundColor = "#5bf";
p.style.top = "0";
p.style.left = i * 20 + "px";//第一二三格
//将蛇头重置到最后的P
this.snake.unshift(p);
this.screen.appendChild(p);
}
//随机生成食物
this.randomFood();
this.spans = this.screen.querySelectorAll("span");
document.body.appendChild(this.screen);
},
/**
* 随机生成食物
*
**/
randomFood() {
// 获取 没有蛇的所有 span
let spans = this.screen.querySelectorAll("span:not(.sk)");
// console.log(spans)
// 随机产生一个 0 ~ spans.length 的 索引
let index = Math.floor(Math.random() * spans.length)
// 获取随机产生的 span
let span = spans[index];
// 动态创建一个 b 标签
let tag = document.createElement("b");
tag.style.display = "block";
tag.style.width = "10px"
tag.style.height = "10px";
tag.style.margin = "5px";
tag.style.backgroundColor = "#d27"
tag.style.borderRadius = "50%"
// 将生成的 b标签,添加到 span 中
span.appendChild(tag);
},
/**
* 开始游戏
*/
start() {
//初始化游戏
this.initGame();
//按下空格键,开始游戏 (绑定键盘事件)
document.addEventListener("keydown", (event) => {
//event.keyCode获取按键按下的编码(空格编码是32)
if (event.keyCode == 32 && !this.playing) {
this.dir = "right";//默认运动方向
//开始游戏
this.playing = setInterval(() => {
//获取蛇头
let sk = this.snake[0];
//获得距离父元素的位置,用于移动
let left = sk.offsetLeft;
let tops = sk.offsetTop;
//位移
if (this.dir === "right") {
left += 20;
} else if (this.dir === "left") {
left -= 20;
} else if (this.dir === "top") {
tops -= 20;
} else if (this.dir === "down") {
tops += 20;
}
this.addSnake(left, tops);
}, 200)//修改速度-----------------------
//改变运动方向
} else if (this.playing && event.keyCode == 37 && this.dir != "right") {
this.dir = "left";
} else if (this.playing && event.keyCode == 38 && this.dir != "down") {
this.dir = "top";
} else if (this.playing && event.keyCode == 39 && this.dir != "left") {
this.dir = "right";
} else if (this.playing && event.keyCode == 40 && this.dir != "top") {
this.dir = "down";
}
})
},
//获取块元素 坐标的方法
// 距离边界的长度 / 单位块长度 = 本行(列)的 第几块
getIndex(left, tops) {
let x = left / 20;//第几列
let y = tops / 20;//第几行
return y * 20 + x;//没看懂。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
},
//游戏结束
gameOver() {
alert("o(╥﹏╥)o 游戏该束");
//游戏结束 停止轮询
clearInterval(this.playing);
//游戏初始化
//删除舞台
this.screen.remove();
//蛇身清0
this.snake = [];
//游戏状态改为false
this.playing = false;
//方向改为默认向右
this.dir = "right";
// 然后初始化舞台
this.initGame();
},
//蛇的运动 以及 给蛇吃食物时增长
//蛇的运动不是原有块的移动,而是不断给新块染色,添加属性等,让块变成蛇的身体
//从而实现移动
addSnake(left, tops) {
//判断蛇头是否出界 左上右下都要判断
if (left >= this.screen.clientWidth || tops < 0 || left < 0 || tops >= this.screen.clientHeight) {
this.gameOver();
return;//在这里返回 否则该次方法会运行完
}
//生成一个P标签 作为蛇移动时 需要的块
let p = document.createElement("p");
p.style.position = "absolute";
p.style.width = "20px";
p.style.height = "20px";
p.style.backgroundColor = "#5bf";
// p.style.backgroundImage = "/.海豹.png";
p.style.top = tops + "px";
p.style.left = left + "px";
//找到新生成的蛇头的span 给他添加一个 class = sk 代表是蛇身体的一部分
//获取psan
let index = this.getIndex(left, tops);//新生蛇头的 span的坐标
let span = this.spans[index];
//蛇头吃食物的判定
//判断该span上有没有食物
let b = span.querySelector("b");
if (b == null) {//没食物 正常移动
//移除蛇尾
let p2 = this.snake.pop();//移除数据中的蛇尾
left = p2.offsetLeft;
tops = p2.offsetTop;
//移除网页中的蛇尾
// 并把 代表 蛇身的属性 class = sk 清除
index = this.getIndex(left, tops);//拿坐标
let span2 = this.spans[index];//得到块
span2.classList.remove("sk");//清除属性
//屏幕中移除蛇尾
p2.remove();
} else {//有食物,删除(吃掉)食物块
b.remove();
//产生新的食物
this.randomFood();
}
//判断是否咬到自身
if (span.classList.contains("sk")) {
this.gameOver();
return ;
}
//没有要到自身,这时可以先消除蛇尾,添加蛇头(消除与添加就是蛇移动的过程)
span.classList.add("sk")
// unshift 添加元素,保证最后的p 是蛇头
this.snake.unshift(p);
this.screen.appendChild(p);
}
}
new SnakeGame().start();
</script>
</html>