利用js写一个飞翔小鸟小游戏

html部分

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" type="text/css" href="css/飞翔的小鸟.css"/>
</head>
<body>
<!--最外层的标签-->
<div id="root">
<!--head 开始游戏界面上面的名字-->
<div id="head">
<img src="img/bird.png"/>
</div>
<!--开始菜单 -->
<div id="menu">
<img src="img/start.jpg"/>
</div>
<!--结束游戏菜单 默认隐藏-->
<div id="end">
<img src="img/message.jpg"/>
<span id="currentScore">0</span>
<span id="bestScore">0</span>
</div>
<!--顶部的分数-->
<div id="score">
<img src="img/0.jpg"/>
</div>
<!-------游戏音效 一会通过js控制播放--------->
<audio src="music/bullet.mp3" id="bullet"></audio>
<audio src="music/game_music.mp3" id="gameMusic"></audio>
<audio src="music/game_over.mp3" id="gameOver"></audio>
<!------------------------>
<!--游戏开始之后 管道 和 飞扬的小鸟-->
<!--管道 需要js动态创建 写父标签先占位置即可 pipe就是管道的意思!!-->
<ul id="pipes">
<!--准备写以下结构 管道有多个!! 不准用id选择器!!-->
<!--<li class="pipe">
<div class="upPipe"></div>
<div class="downPipe"></div>
</li>-->
</ul>

<!--飞扬的小鸟-->
<img src="img/bird.png" id="bird"/>

</div>
<script src="js/飞扬的小鸟.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>

css部分

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
*{
margin: 0;
padding: 0;
}

#root{
background: url(../img/bg.jpg);
width: 343px;
height: 480px;
margin: 40px auto;
position: relative;
/*--溢出隐藏*/
overflow: hidden;
}

#head{
width: 236px;
height: 77px;
background: url(../img/head.jpg);
position: absolute;
top: 100px;
left: 53.5px;
}
#head img{
position: absolute;
right: 0;
top: 25px;
animation: birdFly 1.5s linear infinite;
}
/*游戏封面 小鸟上下的动画效果*/
@keyframes birdFly{
0%{
top: 25px;
}
25%{
top: 0px;
}
50%{
top: 25px;
}
75%{
top: 50px;
}
100%{
top: 25px;
}
}
/*-------------------------------------*/
/*开始游戏菜单*/
#menu{
width: 100%;
text-align: center;
top:280px;
position: absolute;
}
#menu img{
/*鼠标的状态*/
cursor: pointer;
}
/*------------------*/
/*结束游戏菜单 写完要隐藏*/
#end{
width: 100%;
text-align: center;
position: absolute;
top: 120px;
font-size: 25px;
display: none;
}
/*设置本局的分数 current 当前的;*/
#currentScore{
position: absolute;
left: 250px;
top: 35px;
}
/*历史最高分*/
#bestScore{
position: absolute;
left: 250px;
top: 90px;
}
/*顶部实时改变的分数*/
#score{
width: 100%;
/*要脱离文档流 因为小鸟和管道都要从页面滑过*/
position: absolute;
top: 0;
text-align: center;
display: none;
}
/*----------------------------------*/
/*管道 ul父标签*/
#pipes{
list-style: none;
height: 100%;
}
/*给子标签li 设置样式 一个li中有两个管道 上下两个*/
.pipe{
width: 62px;
height: 423px;
background-color: yellow;
position: absolute;
left: 343px;
top: 0;
}
/*分别给上下 两个管道设置样式 高度都应该由js随机 */
.upPipe{
width: 100%;
background: url(../img/up_pipe.png) bottom no-repeat, url(../img/up_mod.png) repeat-y;
position: absolute;
top:0;
left: 0;
/*height: 150px;*/
}

.downPipe{
width: 62px;
background: url(../img/down_pipe.png) top no-repeat, url(../img/down_mod.png) repeat-y;
position: absolute;
bottom: 0;
/*height: 180px;*/
}
/*飞扬的小鸟 默认也要隐藏*/
#bird{
position: absolute;
left: 62px;
top: 48%;
display: none;
}

js部分

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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//获取标签
//开始游戏按钮
var startBtn = document.querySelector("#menu img");
//游戏封面head
var headEl = document.querySelector("#head");
//开始菜单
var menuEl = document.querySelector("#menu");
//结束菜单
var endEl = document.querySelector("#end");
//管道
var pipeUl = document.querySelector("#pipes");
//飞扬的小鸟
var birdImg = document.querySelector("#bird");
//游戏结束的本局分数
var currentScoreEL = document.querySelector("#currentScore");
//最高分
var bestScoreEl = document.querySelector("#bestScore");
//实时变化的分数 顶部的那个
var scoreEL = document.querySelector("#score");
//游戏音效 点击的声音
var bulletMusic = document.querySelector("#bullet");
//游戏进行的背景音乐
var gameMusic = document.querySelector("#gameMusic");
//游戏结束的时候音乐
var gameOverMusic = document.querySelector("#gameOver");
//底部的div root 需要设置点击事件
var rootEl = document.querySelector("#root");



//----------------------------------------------------------
//2.补充变量
var num = 0;//记录分数
//用来模拟小鸟飞的速度
var speed = 0;
//小鸟下降的计时器
var birdDownTimer;
//小鸟上升的计时器
var birdUpTimer;



//3.封装函数
function randomNum(min, max){
return Math.floor(Math.random() * (max - min + 1) + min);
}
//点击游戏开始按钮---------------------
startBtn.onclick = function(){
//先阻止冒泡! 因为其父标签root 也有click点击事件
event.stopPropagation();
//或者
// event.cancelBubble = true;
//播放音乐 循环播放
gameMusic.loop = true; //循环
gameMusic.play();
//隐藏开始菜单 和 head
headEl.style.display = "none";
menuEl.style.display = "none";
//显示飞扬的小鸟 和 顶部的分数
scoreEL.style.display = "block";
birdImg.style.display = "block";
//生成管道!!较为复杂,封装一下!!!!
setInterval(createPipe, 3000);
//小鸟下降--调用 需要计时器 最好写全局计时器 因为一旦点击屏幕小鸟会上飞,就需要取消下降计时器!!
birdDownTimer = setInterval(birdDown, 20);
//---在这里给root添加点击事件!! 也就是说 只要你点击了start开始按钮
//root才能被点击!!!
rootEl.onclick = clickRoot;//将函数赋值给点击事件 不要带()

//--------------
//碰撞检测 只要游戏开始 就不停 检测

setInterval(function(){

//获取对应的li
var liArray = document.querySelectorAll("li");
for(var i = 0; i < liArray.length; i++){
//判断li中 上下管道是否与小鸟碰撞
crash(birdImg, liArray[i].firstElementChild);//上
crash(birdImg, liArray[i].lastElementChild);//下
}

}, 10)




}




//---------------------------------------------
//创建管道的函数!!!!!!!!!!!!!
function createPipe(){
//创建li
var li = document.createElement("li");
li.className = "pipe";
pipeUl.appendChild(li);
//开始设置小鸟飞过的通道
//假设通道是 130px
var passHeight = 130;
//随机一下上管道的高度
var upHeight = randomNum(80, 200);
//总高度是li的高度是 423px 空隙是130px 上管道是随机[80, 200] 下管道?
var downHeight = li.clientHeight - passHeight - upHeight;
//继续创建上下两个管道的div
var upDiv = document.createElement("div");
upDiv.className = "upPipe";
upDiv.style.height = upHeight + "px";
li.appendChild(upDiv);
var downDiv = document.createElement("div");
downDiv.className = "downPipe";
downDiv.style.height = downHeight + "px";
li.appendChild(downDiv);
console.log("上管道", upDiv.offsetLeft + "; 下管道" + downDiv.offsetLeft);
console.log("管道父级li" + li.offsetLeft);
//管道默认位置在距离父标签左侧 343px 刚好在游戏页面的右侧 需要动画向左滑动
//li管道的当前位置
var x = 343;
//计时器 移动
var pipeMoveTimer = setInterval(function(){
x--;
li.style.left = x + "px";
//一旦滑动到屏幕左边 就停止计时器 并且删除这个管道 管道宽度就是62
if(x <= -li.clientWidth){
clearInterval(pipeMoveTimer);
li.remove();
}

//重点!小鸟距左62因为管道宽度也是62
if(x == 0){
//那不就说明 小鸟已经安全飞过管道了!!
//加分 ---封装函数 修改顶部实时变化的分数
changeScore();
}


}, 10);
}
//封装函数--------修改分数
function changeScore(){
num++;
//清空现在正在现实的分数图片
scoreEL.innerHTML = "";
//添加图片
if(num < 10){
var img = document.createElement("img");
img.src = "img/" + num + ".jpg"; //分数实时 变化 做路径拼接!!!
scoreEL.appendChild(img);
}else{
//考虑100分以内 100分以外的自己写
//双位数 十位
var shiImg = document.createElement("img");
//获取十位上的数字
var shi = Math.floor(num / 10);
shiImg.src = "img/" + shi + ".jpg";
scoreEL.appendChild(shiImg);
//双位数的 个位
var geImg = document.createElement("img");
var ge = num % 10;
geImg.src = "img/" + ge + ".jpg";
scoreEL.appendChild(geImg);
}
}

//--------------------------------------------------------
//小鸟的下降函数
function birdDown(){
birdImg.src = "img/down_bird.png";
//注意!我们让小鸟做变速动画 就是往下掉的越来越快 speed默认是0
speed += 0.5;
if(speed >= 10){
speed = 10;
}
//就是修改计时器每隔20毫秒 往下降的间距值!!!

// 写个计时器 每隔100毫秒下降8px 这是匀速下降!! 也可以speed = 8;
// 如果是重力加速度下降!! 就是每隔一个时间段 下降距离越来越大!!
//-----------------------------------------------------------
birdImg.style.top = birdImg.offsetTop + speed + "px";
//判断 小鸟是否落地
//小鸟最大距上的top是
var maxTop = 423 - birdImg.clientHeight;
if(birdImg.offsetTop >= maxTop){
//游戏结束--封装函数 小鸟落地
gameOver();
}

}

//----游戏结束的函数
function gameOver(){
//暂停游戏背景音乐
gameMusic.pause();
//播放游戏结束的音乐
gameOverMusic.play();
//清除所有的计时器! 不管是创建管道 还是小鸟在上飞或者下降等 全部清除!!!
//js中的计时器 都是有自己的编号 从1开始
//我也不知道目前哪个计时器在执行 但是 我能获取最后一个计数器的编号!!
var lastTimer = setInterval(function(){}, 10);
//从1开始 到最后一个计时器 全部清除!!!
for(var i = 1; i <= lastTimer; i++){
clearInterval(i);
}
//显示分数板
endEl.style.display = "block";
endEl.style.zIndex = 10;
currentScoreEL.innerHTML = num;//num是全局变量
//游戏结束 屏幕的点击事件也结束
rootEl.onclick = null;
}

//---------------------------------------------
//屏幕的点击事件!!!! 注意root太大了 封面盖不住全部的root
//这里不能给root直接设置点击事件 会导致游戏按钮还没点 就已经开始游戏啦
//rootEl.onclick = function(){ 这样写有bug
function clickRoot(){
//每点击一下 播放点击音效
bulletMusic.play();
//每点击一下屏幕 需要让小鸟上飞
//那么此时无论小鸟是哪种飞翔的姿态 都需要重新向上飞
clearInterval(birdDownTimer);
clearInterval(birdUpTimer);
//同一时刻 只能有一个计时器!!
//上飞的是初始速度
speed = 10;
//调用上飞的计时器
birdUpTimer = setInterval(birdUp, 20);
}

//小鸟上升函数 封装----------
function birdUp(){
birdImg.src = "img/up_bird.png";
//注意往上飞 刚开始快 然后会变慢 变速动画!!
speed -= 0.5;
if(speed < 0){
//清除上飞的计时器 开始往下坠
speed = 0;
clearInterval(birdUpTimer);
birdDownTimer = setInterval(birdDown, 20);
}
//-----------------------
birdImg.style.top = birdImg.offsetTop - speed + "px";
//判断是否到达了顶部 小鸟图片的top值 最小是0
if(birdImg.offsetTop <= 0){
//游戏结束
gameOver();
}
}

//---------
//碰撞检测!! 参数1就是小鸟 参数2就是上管道 或者下管道
//需要注意 管道外面包着li 所有有效的距左left值 是应该取li的left值! 我们做管道动画移动也是修改了 li的left值进行动画的! 管道相对li的left值始终都是0!
function crash(div1, div2){
var xLeft = div2.offsetParent.offsetLeft - div1.offsetWidth;
var xRight = div2.offsetParent.offsetLeft + div2.offsetWidth;

var yTop = div2.offsetTop - div1.offsetHeight;
var yBottom = div2.offsetTop + div2.offsetHeight;
//只要div1 在这4个临界值内 那么就说明发生了碰撞
if(div1.offsetLeft >= xLeft && div1.offsetLeft <= xRight && div1.offsetTop >= yTop && div1.offsetTop <= yBottom){
//如果发生了碰撞 游戏结束
gameOver();
}
}



代码仅供学习参考


利用js写一个飞翔小鸟小游戏
http://ultracode.cn/2020/12/08/JS/利用js写一个飞翔小鸟小游戏/
作者
Win
发布于
2020年12月8日
许可协议