01-案例介绍

02-面向对象思维分析

03-构建方块对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 1.方块对象Cube (8行js) */
class Cube {
// 1.1 构造函数
constructor(x = 0, y = 0, bgc = this.randomColor()) {
this.x = x
this.y = y
this.bgc = bgc
this.html = `<div class="cube" style="left:${x * 20}px;top:${y * 20}px;background-color:${bgc}"></div>`
}

// 1.2 原型
randomColor () {
// rgb(0-255,0-255,0-255)

return `rgb( ${parseInt(Math.random() * 256)} , ${parseInt(Math.random() * 256)} , ${parseInt(Math.random() * 256)} )`
}
}

04-构建食物对象

1
2
3
4
5
6
7
/* 2.食物对象Food (3行js) */
class Food extends Cube {
// 2.1 构造函数
constructor() {
super(parseInt(Math.random() * 40), parseInt(Math.random() * 30))
}
}

05-构建蛇对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/* 3.蛇对象Snake(18行js) */
class Snake {
// 3.1 构造函数
constructor() {
this.body = [
{ x: 5, y: 1, bgc: 'red' },
{ x: 4, y: 1, bgc: 'orange' },
{ x: 3, y: 1, bgc: 'green' },
{ x: 2, y: 1, bgc: 'purple' },
{ x: 1, y: 1, bgc: 'blue' },
]
this.direction = 'KeyD'
}
// 3.2 渲染
render () {
return this.body.map(item => new Cube(item.x, item.y, item.bgc).html).join('')
}
// 3.3 反方向
revDirection () {
switch (this.direction) {
case 'KeyW': return 'KeyS'
case 'KeyA': return 'KeyD'
case 'KeyS': return 'KeyW'
case 'KeyD': return 'KeyA'
}
}
// 3.3 移动
move (key) {
// (1)记录当前移动方向
this.direction = key
// (2)每一节身体位置前移
for (let 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
}
// (3)蛇头位置取决于移动方向
if (key === 'KeyW') this.body[0].y--
else if (key === 'KeyA') this.body[0].x--
else if (key === 'KeyS') this.body[0].y++
else if (key === 'KeyD') this.body[0].x++
// (4)碰撞检测 : 蛇头不能与任意身体位置重合 arr.slice(1)返回1下标之后的所有元素
if (this.body.slice(1).some(item => item.x === this.body[0].x && item.y === this.body[0].y)) {
Game.over()
}
// (5)边界检测
if ([-1, 40].includes(this.body[0].x) || [-1, 30].includes(this.body[0].y)) {
Game.over()
}
}
}

06-构建游戏对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* 4.游戏对象(30行js) */
class Game {
// 4.1 构造函数
constructor() {
this.map = document.querySelector('.map')// 地图
this.food = new Food()// 食物
this.snake = new Snake()// 蛇
this.map.innerHTML = this.food.html + this.snake.render()
this.score = 0// 分数
this.timer = null// 定时器
}
// 4.2 开始游戏
start () {
window.addEventListener('keydown', ({ code }) => {
// 按键检测
if (!['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(code) || this.snake.revDirection() === code) return
/* 自动移动 */
clearInterval(this.timer)
this.timer = setInterval(() => {
// (1)蛇移动
this.snake.move(code)
// (2)吃食物判定
this.eat()
// (3)重新渲染
this.map.innerHTML = this.food.html + this.snake.render()
}, 100)
/* 长按方向键可以加速移动 */
// (1)蛇移动
this.snake.move(code)
// (2)吃食物判定
this.eat()
// (3)重新渲染
this.map.innerHTML = this.food.html + this.snake.render()
})
}
// 4.3 吃食物
eat () {
// 1. 判断食物和蛇头位置是否重叠
if (this.food.x === this.snake.body[0].x && this.food.y === this.snake.body[0].y) {
// (1)把食物放在尾巴位置
this.snake.body.push({
x: this.snake.body[this.snake.body.length - 1].x,
y: this.snake.body[this.snake.body.length - 1].y,
bgc: this.food.bgc
})
// (2)产生新的食物
this.food = new Food()
// (3)分数++
this.score++
document.querySelector('.score').innerHTML = this.score
}
}
// 4.4 暂停游戏
pause () {
clearInterval(this.timer)
}
// 4.5 结束游戏
static over () {
alert('Game Over')
location.reload()
}
}

const game = new Game()
game.start()

document.querySelector('.start').addEventListener('click', function () {
location.reload()
})

document.querySelector('.pause').addEventListener('click', function () {
game.pause()
})

07-完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* 1.方块对象Cube (8行js) */
class Cube {
// 1.1 构造函数
constructor(x = 0, y = 0, bgc = this.randomColor()) {
this.x = x
this.y = y
this.bgc = bgc
this.html = `<div class="cube" style="left:${x * 20}px;top:${y * 20}px;background-color:${bgc}"></div>`
}

// 1.2 原型
randomColor () {
// rgb(0-255,0-255,0-255)

return `rgb( ${parseInt(Math.random() * 256)} , ${parseInt(Math.random() * 256)} , ${parseInt(Math.random() * 256)} )`
}
}

/* 2.食物对象Food (3行js) */
class Food extends Cube {
// 2.1 构造函数
constructor() {
super(parseInt(Math.random() * 40), parseInt(Math.random() * 30))
}
}

/* 3.蛇对象Snake(18行js) */
class Snake {
// 3.1 构造函数
constructor() {
this.body = [
{ x: 5, y: 1, bgc: 'red' },
{ x: 4, y: 1, bgc: 'orange' },
{ x: 3, y: 1, bgc: 'green' },
{ x: 2, y: 1, bgc: 'purple' },
{ x: 1, y: 1, bgc: 'blue' },
]
this.direction = 'KeyD'
}
// 3.2 渲染
render () {
return this.body.map(item => new Cube(item.x, item.y, item.bgc).html).join('')
}
// 3.3 反方向
revDirection () {
switch (this.direction) {
case 'KeyW': return 'KeyS'
case 'KeyA': return 'KeyD'
case 'KeyS': return 'KeyW'
case 'KeyD': return 'KeyA'
}
}
// 3.3 移动
move (key) {
// (1)记录当前移动方向
this.direction = key
// (2)每一节身体位置前移
for (let 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
}
// (3)蛇头位置取决于移动方向
if (key === 'KeyW') this.body[0].y--
else if (key === 'KeyA') this.body[0].x--
else if (key === 'KeyS') this.body[0].y++
else if (key === 'KeyD') this.body[0].x++
// (4)碰撞检测 : 蛇头不能与任意身体位置重合 arr.slice(1)返回1下标之后的所有元素
if (this.body.slice(1).some(item => item.x === this.body[0].x && item.y === this.body[0].y)) {
Game.over()
}
// (5)边界检测
if ([-1, 40].includes(this.body[0].x) || [-1, 30].includes(this.body[0].y)) {
Game.over()
}
}
}

/* 4.游戏对象(30行js) */
class Game {
// 4.1 构造函数
constructor() {
this.map = document.querySelector('.map')// 地图
this.food = new Food()// 食物
this.snake = new Snake()// 蛇
this.map.innerHTML = this.food.html + this.snake.render()
this.score = 0// 分数
this.timer = null// 定时器
}
// 4.2 开始游戏
start () {
window.addEventListener('keydown', ({ code }) => {
// 按键检测
if (!['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(code) || this.snake.revDirection() === code) return
/* 自动移动 */
clearInterval(this.timer)
this.timer = setInterval(() => {
// (1)蛇移动
this.snake.move(code)
// (2)吃食物判定
this.eat()
// (3)重新渲染
this.map.innerHTML = this.food.html + this.snake.render()
}, 100)
/* 长按方向键可以加速移动 */
// (1)蛇移动
this.snake.move(code)
// (2)吃食物判定
this.eat()
// (3)重新渲染
this.map.innerHTML = this.food.html + this.snake.render()
})
}
// 4.3 吃食物
eat () {
// 1. 判断食物和蛇头位置是否重叠
if (this.food.x === this.snake.body[0].x && this.food.y === this.snake.body[0].y) {
// (1)把食物放在尾巴位置
this.snake.body.push({
x: this.snake.body[this.snake.body.length - 1].x,
y: this.snake.body[this.snake.body.length - 1].y,
bgc: this.food.bgc
})
// (2)产生新的食物
this.food = new Food()
// (3)分数++
this.score++
document.querySelector('.score').innerHTML = this.score
}
}
// 4.4 暂停游戏
pause () {
clearInterval(this.timer)
}
// 4.5 结束游戏
static over () {
alert('Game Over')
location.reload()
}
}

const game = new Game()
game.start()

document.querySelector('.start').addEventListener('click', function () {
location.reload()
})

document.querySelector('.pause').addEventListener('click', function () {
game.pause()
})