前言
贪吃蛇游戏在我上小学的时候就接触了,当时只有一台诺基亚,每天无聊的时候就是玩贪吃蛇,回想当年,为了吃到每个球都是小心翼翼,看着蛇慢慢变长。每天都想着突破记录,达到最高分。
现在选择软件工程专业,突然就想能不能做出这个游戏来,不管做成什么样子。因为对web感兴趣,学习了js,开始了尝试。
项目准备阶段
- 软件使用的是vscode
- 创建好三个文件index.html,index.css,index.js
项目开始
第一步先搭建结构
使用html、css搭建好类似于屏幕的盒子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇</title>
<link rel="stylesheet" href="./style/index.css">
</head>
<body>
<!-- 创建游戏的主容器 -->
<div id="main">
<!-- 设置游戏的舞台 -->
<div id="stage">
<!-- 蛇 -->
<div id="snake">
<div></div>
</div>
<!-- 食物 -->
<div id="food">
<!-- 设置四个div来设置食物的样式 -->
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<div id="score-panle">
<div>
SCORE: <span id="score">0</span>
</div>
<div>
level: <span id="level">1</span>
</div>
</div>
</div>
<script src="./index.js"></script>
</body>
</html>
第二步对结构进行布局修饰
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font: bold 20px "Courier";
}
#main {
width: 360px;
height: 420px;
background-color: #b7d4a8;
margin: 100px auto;
border: 10px solid #000;
border-radius: 40px;
display: flex;
flex-flow: column;
justify-content: space-around;
align-items: center;
}
#main #stage {
width: 304px;
height: 304px;
border: 2px solid #000;
position: relative;
}
#main #stage #snake > div {
width: 10px;
height: 10px;
background-color: #000;
border: 1px solid #b7d4a8;
position: absolute;
}
#main #stage #food {
width: 10px;
height: 10px;
background-color: black;
border: 1px solid #b7d4a8;
position: absolute;
left: 40px;
top: 100px;
display: flex;
flex-flow: row wrap;
justify-content: space-between;
align-content: space-between;
}
#main #stage #food > div {
width: 4px;
height: 4px;
background-color: black;
transform: rotate(45deg);
}
#main #score-panle {
width: 300px;
display: flex;
justify-content: space-between;
align-items: space-between;
}
第三步js编写游戏
先从简单的部分开始:记分牌类
- 实现功能有:
- 加分
- 提升等级(提升速度)
// 定义表示记分牌的类
class ScorePanel {
score = 0;
level = 1;
scoreEle;
levelEle;
// 设置一个变量限制等级
maxLevel;
upScore
constructor(maxLevel = 10, upScore = 10) {
this.scoreEle = document.getElementById("score")
this.levelEle = document.getElementById("level")
this.maxLevel = maxLevel
this.upScore = upScore
}
// 设置一个加分的方法
addScore() {
this.scoreEle.innerHTML = ++this.score + ""
if (this.score % this.upScore === 0) {
this.levelUp()
}
}
// 设置提升等级的方法
levelUp() {
if (this.level < this.maxLevel)
this.levelEle.innerHTML = ++this.level + ""
}
}
食物类
- 实现功能:
1. 获得食物的位置
2. 修改食物的位置
// 定义食物类Food
class Food {
// 定义一个属性表示食物所对应的元素
// element: HTMLElement;
element;
constructor() {
this.element = document.getElementById("food");
}
get X() {
return this.element.offsetLeft;
}
get Y() {
return this.element.offsetTop;
}
// 修改食物的位置
change() {
// 生成一个随机的位置
let top = Math.round(Math.random() * 29) * 10;
let left = Math.round(Math.random() * 29) * 10;
this.element.style.left = left + "px";
this.element.style.top = top + "px";
}
}
蛇类
- 实现功能
- 设置和获取头的位置
- 设置蛇的运动
- 蛇吃食物后变长
- 判断蛇有没有撞墙和有没有撞身体
- 设置蛇不能反向移动
class Snake {
head;
element;
// 包括蛇头
bodies;
constructor() {
this.head = document.querySelector("#snake>div")
this.element = document.getElementById("snake")
this.bodies = this.element.getElementsByTagName("div")
}
get X() {
return this.head.offsetLeft
}
get Y() {
return this.head.offsetTop
}
set X(value) {
if (this.X === value) {
return
}
// X的值的合法范围0-290之间
if (value < 0 || value > 290) {
// 进入判断寿命蛇撞墙了
throw new Error("蛇撞墙了")
}
// 修改X是,是在修改水平坐标,蛇不能反向移动
if (this.bodies[1] && this.bodies[1].offsetLeft === value) {
if (value > this.X) {
value = this.X - 10
} else {
value = this.X + 10
}
}
this.moveBody()
this.head.style.left = value + "px"
this.checkHeadBody()
}
set Y(value) {
if (this.Y === value) {
return
}
if (value < 0 || value > 290) {
// 进入判断寿命蛇撞墙了
throw new Error("蛇撞墙了")
}
// 修改Y是,是在修改垂直坐标,蛇不能反向移动
if (this.bodies[1] && this.bodies[1].offsetTop === value) {
if (value > this.Y) {
value = this.Y - 10
} else {
value = this.Y + 10
}
}
this.moveBody()
this.head.style.top = value + "px"
this.checkHeadBody()
}
// 蛇增加身体的方法
addBody() {
this.element.insertAdjacentHTML("beforeend", "<div></div>")
}
// 添加一个蛇身体移动的方法
moveBody() {
for (let i = this.bodies.length - 1; i > 0; i--) {
// 获取前边身体的位置
let X = this.bodies[i-1].offsetLeft;
let Y = this.bodies[i-1].offsetTop;
// 将值设置道当前身体上
this.bodies[i].style.left = X + "px";
this.bodies[i].style.top = Y + "px";
}
}
checkHeadBody() {
// 获取所有的身体,检查其是否和蛇头坐标发生重叠
for (let i = 1; i < this.bodies.length; i++) {
let bd = this.bodies[i]
console.log(bd.offsetTop);
console.log(bd.offsetLeft);
if (this.Y === bd.offsetTop && this.X === bd.offsetLeft) {
throw new Error("撞到自己了")
}
}
}
}
游戏控制类
- 实现功能
- 对蛇、食物、记分牌三个类的引用
- 控制游戏的开始和结束
- 获取用户键盘(如果键盘按的太快有bug)
- 让蛇一直在移动
- 对加分、升级、食物位置变化、蛇变长的调用
class GameControl {
snake = new Snake();
food = new Food();
scorePanel = new ScorePanel();
// 创建一个属性来存储蛇的移动方向
direction = ""
// 创建一个属性用来记录游戏是否结束
isLive = true
constructor() {
this.snake = new Snake()
this.food = new Food()
this.scorePanel = new ScorePanel(10, 8)
this.init()
}
init() {
//绑定键盘按下的事件
document.addEventListener("keydown", this.keydownHandler.bind(this))
this.run()
}
keydownHandler(event) {
// 检查event.key 的值是否合法(用户是否按了正确的按钮)
this.direction = event.key
}
// 创建一个控制蛇移动的方法
run() {
// 获取蛇当前的坐标
let X = this.snake.X
let Y = this.snake.Y
switch (this.direction) {
case "ArrowUp":
case "Up":
Y -=10
break;
case "ArrowDown":
case "Down":
Y += 10
break;
case "ArrowLeft":
case "Left":
X -= 10
break;
case "ArrowRight":
case "Right":
X += 10
break;
}
this.checkEat(X, Y)
// 捕获异常,程序就不会停止了
try {
this.snake.X = X
this.snake.Y = Y
} catch (e) {
alert(e.message + "GameOver")
this.isLive = false
}
//修改蛇的X和Y
// 开启一个定时调用
this.isLive && setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30)
}
// 定义一个方法,用来检测是否吃到食物
checkEat(X, Y) {
if (X === this.food.X && Y === this.food.Y) {
// 食物的位置重置
this.food.change()
// 记分牌加分
this.scorePanel.addScore()
// 蛇身体增加一节
this.snake.addBody()
}
}
}
let game = new GameControl()
game.init()
总结
该项目使用了一些ES6标准,但有些地方优化也不是很好。该项目是学习尚硅谷视频后制作的。
新手尝试,不足之处敬请谅解。欢迎大佬评论区指教指教。