之前一直在手机上玩flappy bird游戏,闲暇时间就编写了一个
是采用python3+pygame模块制作而成的,运行效果非常流畅,会让你大吃一惊哦:smiley:哈哈
一、运行效果展示
下载游戏之后,注意在自己的python环境中安装pygame模块,如果没有安装可以使用pip install pygame 进行安装
然后使用使用命令运行起这个.py文件,运行之后的第一个界面效果如下,是不是很酷炫
当点击上图中的“Play”按钮之后的效果如下:
运行之后是有音乐的,大家可以下载代码的时候一起将素材下载,这样就在运行时就能听到音乐
二、完整代码
下面代码用到了素材(背景图片,音乐等,下载地址 https://www.itprojects.cn/detail.html?example_id=8af93ac601523a955f8280c95c2a9e0b )
1import
math

2import
os

3import
time

4from
random
import
randint

5from
random
import
uniform

6import
pygame

7from
pygame.locals
import
*
#导入一些常用的变量
8from
collections
import
deque
#加入了队列
9
10
FPS =
60
11
BK_WIDTH =
900#背景宽度
12
BK_HEIGHT =
650#背景高度
13
PIPE_WIDTH =
80#水管的宽度
14
PIPE_HEIGHT =
10#水管素材的高度
15
PIPE_HEAD_HEIGHT =
32#管子头的高度
16
17#初始化全局变量
18
BK_MOVE_SPEED =
0.22#主柱子每毫秒移动的速度
19
ADD_TIME =
2500##每隔多少毫秒就增加一个柱子 这种方法不会有漏洞吗 就是当毫秒数和帧数不匹配啥的 #还需要仔细的思考
20
TOTAL_PIPE_BODY = int(
3
/
5
* BK_HEIGHT)
# 像素值必须为整数 占窗口的3/5
21
PIPE_RATE =
0.96
22
a_i=
"bird-wingup"
23
b_i=
"bird-wingmid"
24
c_i=
"bird-wingdown"
25
26
INITAL_SPEED =
-0.37#鸟的Y轴初速度
27
BIRD_WIDTH =
50
28
BIRD_HEIGHT =
40
29
BIRD_INIT_SCORE =
7#鸟的初始通关分数
30
31
STONE_ADD_TIME =
1000#每隔多少毫秒就增加一个石头
32
STONE_WIDTH =
40
33
STONE_HEIGHT =
30
34
STONE_LEVEL =
4#石头出现的等级
35
36
BUTTON_WIDTH =
140
37
BUTT0N_HEIGHT =
60
38
39
BULLET_SPEED =
0.32#子弹的速度
40
BULLET_WIETH =
50
41
BULLET_HEIGHT =
30
42#设置全局变量 方便修改参数
43
44
45
pygame.init()

46
screen = pygame.display.set_mode((BK_WIDTH,BK_HEIGHT))

47
pygame.mixer.init()

48
49
music_lose = pygame.mixer.Sound(
"lose.wav"
)

50
music1 = pygame.mixer.Sound(
"touch.wav"
)

51
pygame.mixer.music.load(
"bkm.mp3"
)

52
font = pygame.font.SysFont(
'comicsansms'
,
25
)

53
54
55#用于设置鸟的种类
56deflittle_bird
(list):

57global
a_i

58global
b_i

59global
c_i

60
a_i=list[
0
]

61
b_i=list[
1
]

62
c_i=list[
2
]

63
64
65#用于设置关卡难度
66defseteasy
(list):

67global
BK_MOVE_SPEED
# 背景每毫秒移动的速度 就是柱子移动的速度
68global
ADD_TIME
# 每隔多少毫秒就增加一个柱子
69global
TOTAL_PIPE_BODY
# 像素值必须为整数 占窗口的3/5
70global
PIPE_RATE

71global
STONE_LEVEL
# 鸟出现的等
72global
BIRD_INIT_SCORE

73
74
BK_MOVE_SPEED = list[
0
]
# 背景每毫秒移动的速度
75
ADD_TIME = list[
1
]
# 每隔多少毫秒就增加一个柱子
76
TOTAL_PIPE_BODY =list[
2
]
# 像素值必须为整数 占窗口的3/5
77
PIPE_RATE = list[
3
]

78
Pipe.add_time = list[
1
]

79
BIRD_INIT_SCORE = list[
4
]

80
STONE_LEVEL = list[
5
]

81
82
83#子弹类
84classBullet
(pygame.sprite.Sprite):

85
speed = BULLET_SPEED

86
width = BULLET_WIETH

87
height = BULLET_HEIGHT

88
89def__init__
(self,bird,images):

90
super(Bullet,self).__init__()
#d调用父类的初始函数 使用此方法 可以减少代码的更改量 并且解决了多重继承的问题
91
self.x,self.y = bird.x,bird.y

92
self.bullet = images
#给鸟的图片进行赋值
93
self.mask_bullet = pygame.mask.from_surface(self.bullet)

94defupdate
(self):
#计算鸟在下一点的新坐标并更新
95
self.x=self.x+self.speed*frames_to_msec(
1
)

96
@property

97defimage
(self):

98return
self.bullet

99
@property

100defmask
(self):

101return
self.mask_bullet

102
@property

103defrect
(self):

104return
Rect(self.x,self.y,Bullet.width,Bullet.height)

105defvisible
(self):

106return0
<self.x<BK_WIDTH+Bullet.width

107
108
109#小鸟做竖直上抛运动 当小鸟加速到一定状态时 就不再加速了
110classBird
(pygame.sprite.Sprite):

111
112
width =BIRD_WIDTH
#鸟宽
113
height = BIRD_HEIGHT
#鸟长
114
sink_gravity =
0.001#鸟的下降重力
115
116def__init__
(self,x,y,level,images):

117
super(Bird,self).__init__()
#d调用父类的初始函数 使用此方法 可以减少代码的更改量 并且解决了多重继承的问题
118
self.x,self.y = x,y

119
self.wing_up,self.wing_mid,self.wing_down = images
#给鸟的图片进行赋值
120
self.mask_wing_up = pygame.mask.from_surface(self.wing_up)

121
self.mask_wing_mid = pygame.mask.from_surface(self.wing_mid)

122
self.mask_wing_down = pygame.mask.from_surface(self.wing_down)

123
self.inital_speed =
0#鸟向上的初速度
124
self.level = level
#鸟的初始等级
125
self.score =
0#鸟的初始分数为 0
126
127defupdate
(self,t):
#计算鸟在下一点的新坐标并更新
128
y_ = self.inital_speed*t+
0.5
*self.sink_gravity*t*t

129if
self.inital_speed<=
0.3
:

130
self.inital_speed = self.inital_speed +self.sink_gravity*t

131
self.y+=y_
#在主函数里计算时间
132
133
@property

134defimage
(self):

135if
pygame.time.get_ticks()%
400
>=
120
:

136return
self.wing_up

137elif
pygame.time.get_ticks()%
400
>=
280
:

138return
self.wing_mid

139else
:

140return
self.wing_down

141
@property

142defmask
(self):

143if
pygame.time.get_ticks()%
400
>=
120
:

144return
self.mask_wing_up

145elif
pygame.time.get_ticks()%
400
>=
280
:

146return
self.mask_wing_mid

147else
:

148return
self.mask_wing_down

149
150
@property

151defrect
(self):

152return
Rect(self.x,self.y,Bird.width,Bird.height)

153
154
155
156classPipe
(pygame.sprite.Sprite):

157
width = PIPE_WIDTH

158
pipe_head_height = PIPE_HEAD_HEIGHT

159
add_time = ADD_TIME

160
161def__init__
(self,pipe_head_image,pipe_body_image):

162
super(Pipe, self).__init__()

163
self.x = float(BK_WIDTH
-1
)

164
self.score_count =
False
165
self.image = pygame.Surface((Pipe.width,BK_HEIGHT),SRCALPHA)
#创建一个surface 我理解为能画到窗口上的对象
166# #意为创建一个有ALPHA 通道的surface 如果需要透明就需要这个选项
167
self.image.convert()

168
self.image.fill((
0
,
0
,
0
,
0
))
#前三位是颜色 最后一位是透明度
169
total_pipe_length = TOTAL_PIPE_BODY

170
171
self.bottom_length = randint(int(
0.1
*total_pipe_length),int(
0.8
*total_pipe_length))
#用于生成指定范围内的整数
172
self.top_length = total_pipe_length-self.bottom_length

173
174for
i
in
range(
1
,self.bottom_length+
1
):

175
pos = (
0
,BK_HEIGHT - i)

176
self.image.blit (pipe_body_image,pos)
#用重叠的技术画出来管子
177
178
bottom_head_y = BK_HEIGHT - self.bottom_length-self.pipe_head_height
#求出管子头的长度
179
bottom_head_pos = (
0
,bottom_head_y)

180
self.image.blit(pipe_head_image,bottom_head_pos)
#画管子
181
182for
i
in
range(-PIPE_HEIGHT,self.top_length-PIPE_HEIGHT):

183
pos = (
0
,i)

184
self.image.blit(pipe_body_image,pos)

185
top_head_y = self.top_length

186
self.image.blit(pipe_head_image,(
0
,top_head_y))

187
188
self.mask = pygame.mask.from_surface(self.image)

189
@property

190defrect
(self):

191return
Rect(self.x,
0
,Pipe.width,PIPE_HEIGHT)

192
@property

193defvisible
(self):

194return
-Pipe.width<self.x<BK_WIDTH

195
196defupdate
(self,delta_frames=
1
):

197
self.x-=BK_MOVE_SPEED*frames_to_msec(delta_frames)

198
199defcollides
(self,bird):

200return
pygame.sprite.collide_mask(self,bird)

201
202
203defchange_add_time
():

204
Pipe.add_time= int( (Pipe.add_time*PIPE_RATE) /
100
)*
100
205#改变管子的增加时间
206
207
208#石头具有速度 位置等不同属性
209#起始的x属性为固定值 y随机 速度在一定范围内随机
210classStone
(pygame.sprite.Sprite):

211
add_time = STONE_ADD_TIME

212
width = STONE_WIDTH

213
height = STONE_HEIGHT

214def__init__
(self,image):

215
super(Stone, self).__init__()

216
self.x =BK_WIDTH
-5
217
self.y = randint(
1
,int(
0.95
*BK_HEIGHT))

218
self.speed = uniform(
0.1
,
0.5
)

219
self.stone_image = image

220
self.mask_image = pygame.mask.from_surface(self.image)

221
222
@property

223defrect
(self):

224return
Rect(self.x,self.y,self.width,self.height)

225
@property

226defimage
(self):

227return
self.stone_image

228
229
@property

230defmask
(self):

231return
self.mask_image

232
233defupdate
(self,frame =
1
):

234
self.x -= int(self.speed*frames_to_msec(frame))

235
236defcollides
(self, b):

237return
pygame.sprite.collide_mask(self, b)

238
239defvisible
(self):

240return
-self.width<self.x<BK_WIDTH

241
242
243#返回每关需要达到的通关分数
244deflevel_goal
(bird):

245return
bird.level*BIRD_INIT_SCORE

246
247#载入图片
248defload_image
(img_file_name):

249
file_name = os.path.join(
"."
,
"images"
,img_file_name)
#进行路径字符串的合并
250
img = pygame.image.load(file_name)

251
img.convert()

252return
img

253
254#根据所在的等级返回需要的背景名
255defsearch_bk
(bird):

256return"bk"
+str(bird.level)

257
258
img_x = load_image(
'backgroundx.png'
)
#加载背景图像
259defload_images
():

260#加载所有游戏需要用到的图像
261#上面写了这个函数下面就用了起来 join用于分隔符和元组的拼接 os.path.join 用于路径的顺序拼接
262return
{
'bk1'
: load_image(
'background.png'
),

263'bk2'
:load_image(
"background2.png"
),

264"bk3"
:load_image(
"background3.png"
),

265"bk4"
:load_image(
"background4.png"
),

266"bk5"
:load_image(
"background5.png"
),

267"bk6"
:load_image(
"background6.png"
),

268'stone'
:load_image(
'stone.png'
),

269'bullet'
: load_image(
'bullet.png'
),

270'pipe-end'
: load_image(
'pipe_end.png'
),

271'pipe-body'
: load_image(
'pipe_body.png'
),

272'f_u'
: load_image(
'fenghuang_up.png'
),

273'f_m'
: load_image(
'fenghuang_mid.png'
),

274'f_w'
: load_image(
'fenghuang_down.png'
),

275'bird-wingup'
: load_image(
'bird_wing_up.png'
),

276'bird-wingmid'
: load_image(
'bird_wing_mid.png'
),

277'bird-wingdown'
: load_image(
'bird_wing_down.png'
)}

278
279defframes_to_msec
(frames,fps=FPS):

280return1000.0
*frames/fps
#难道限制的意思就是我可以限制图片出来的时间
281
282defmsec_to_frames
(milliseconds, fps=FPS):

283return
fps * milliseconds /
1000.0#转化成对应的帧数
284#转化成每秒的相应的帧数
285
286
287defgame_loop
():

288
pygame.mixer.music.play(
-1
)

289
pygame.display.set_caption(
"Flappy Bird"
)

290
clock = pygame.time.Clock()
#创建一个时钟对象
291
images = load_images()
#建立所有需要的图像字典
292
293
bird = Bird(
20
,BK_HEIGHT//
2
,
1
,(images[a_i],images[b_i] ,images[c_i]))

294
score_font = pygame.font.SysFont(
None
,
50
,bold=
True
)
#名字 大小 粗体 建立画笔 用于记录 分数
295
score_font2 = pygame.font.SysFont(
None
,
40
, bold=
True
)
# 名字 大小 粗体 建立画笔 用于记录 分数
296
score_font3 = pygame.font.SysFont(
None
,
70
, bold=
True
)
# 名字 大小 粗体 建立画笔 用于记录 分数
297
pipes = deque()

298
299
stones =pygame.sprite.Group()
#将石头新建为一个精灵组
300
bullets =pygame.sprite.Group()
#将子弹新建为一个精灵组
301
302
pause = done =
False
303
frames=
0
304
305whilenot
done :
#当没有按下中止键
306
clock.tick(FPS)

307ifnot
(pause
or
frames%msec_to_frames(Pipe.add_time)):
#如果没有按下暂停 或者满足新生成柱子的条件
308
pp=Pipe(images[
'pipe-end'
], images[
'pipe-body'
])

309
pipes.append(pp)
#生成新管子 并加入队列
310
311ifnot
(pause
or
frames%msec_to_frames(Stone.add_time)
or
bird.level<STONE_LEVEL):

312
ss = Stone(images[
"stone"
])

313
stones.add(ss)
#加入新生成的石头
314
315#判断发生了什么事件进行相应的处理
316for
e
in
pygame.event.get():

317if
e.type == QUIT:

318
done =
True
319break
320elif
e.type == KEYUP :

321if
e.key == K_p:

322
pause =
not
pause

323elif
e.key ==K_d:
#发射子弹
324
bb=Bullet(bird,images[
"bullet"
])

325
bullets.add(bb)

326elif
e.key ==K_s
or
e.key == K_SPACE:

327
bird.inital_speed = INITAL_SPEED

328
329
330elif
e.type == MOUSEBUTTONUP:

331
bird.inital_speed =INITAL_SPEED

332
333# 重新更新时间
334# 使小鸟又进入相应的运动的开始
335if
pause:

336continue# 这个时段什么都不做
337
338
pygame.sprite.groupcollide(stones,bullets,
True
,
True
,pygame.sprite.collide_mask)

339
pipe_collision = any(p.collides(bird)
for
p
in
pipes)

340
stone_collision = any(s.collides(bird)
for
s
in
stones)

341
342if
pipe_collision:

343
pygame.mixer.music.stop()

344
done =
True
345
pygame.mixer.Sound.play(music_lose,
-1
)

346
time.sleep(
3.5
)

347
pygame.mixer.Sound.stop(music_lose)

348
time.sleep(
0.1
)

349
350if
stone_collision:

351
pygame.mixer.music.stop()

352
pygame.mixer.Sound.play(music_lose,
-1
)

353
time.sleep(
3.5
)

354
pygame.mixer.Sound.stop(music_lose)

355
time.sleep(
0.1
)

356
done =
True
357if0
>=bird.y
or
bird.y>BK_HEIGHT-Bird.height:

358
done =
True
359
pygame.mixer.music.stop()

360
pygame.mixer.Sound.play(music_lose,
-1
)

361
time.sleep(
3.5
)

362
pygame.mixer.Sound.stop(music_lose)

363
time.sleep(
0.1
)

364
365
366
screen.blit(images[search_bk(bird)], (
0
,
0
))
#画背景墙 这种是分开两张的
367
368while
pipes
andnot
pipes[
0
].visible:

369
pipes.popleft()
#当队列不为空 且管子 0 已经不可见的时候
370for
s
in
stones:
#删除看不见的石头
371ifnot
s.visible():

372del
s

373for
b
in
bullets:
#删除看不见的子弹
374ifnot
b.visible():

375del
b

376
377
378for
p
in
pipes:

379
p.update()

380
screen.blit(p.image,p.rect)
#在指定的位置 画柱子
381for
s
in
stones:

382
s.update()

383
screen.blit(s.image,s.rect)

384
385for
b
in
bullets:

386
b.update()

387
screen.blit(b.bullet,b.rect)

388
389for
p
in
pipes:

390if
bird.x>p.x+Pipe.width
andnot
p.score_count:
#当柱子超过了鸟的位置并且柱子还没有被计分
391
bird.score+=
1
392
p.score_count =
True
393
394
sl = score_font.render(
"level:"
,
True
,(
255
,
255
,
255
))

395
sc = score_font.render(
"score:"
,
True
,(
255
,
255
,
255
))

396
sl2 = score_font2.render(str(bird.level),
True
,(
255
,
255
,
255
))

397
sc2 = score_font2.render(str(bird.score),
True
,(
255
,
255
,
255
))

398
screen.blit (sc,(BK_WIDTH
-170
,
20
))

399
screen.blit(sl, (BK_WIDTH -
320
,
20
))

400
screen.blit(sc2, (BK_WIDTH -
50
,
27
))

401
screen.blit(sl2, (BK_WIDTH -
210
,
27
))

402
403
bird.update(frames_to_msec(
1
))
#计算一帧所需要的时间
404
screen.blit(bird.image,bird.rect)

405
406
pygame.display.flip()
#绘制图像到屏幕
407if
bird.score >= level_goal(bird):
#如果已经达到了通关分数
408#升入下一级 首先要初始化所有变量#清空柱子#改变等级
409
change_add_time()

410
pipes.clear()

411
stones.empty()

412
bullets.empty()

413
bird.level +=
1# 分数先暂不做清空后续再加入吧
414if
bird.level<=
6
:

415
s3 = score_font3.render(
"Next Level"
,
True
, (
255
,
255
,
255
))

416
screen.blit(s3, (BK_WIDTH//
2-150
, BK_HEIGHT//
2-50
))

417
pygame.display.flip()

418
time.sleep(
2
)

419if
bird.level >
6
:

420
s3 = score_font3.render(
"You Win!"
,
True
, (
255
,
255
,
255
))

421
screen.blit(s3, (BK_WIDTH //
2
-
150
, BK_HEIGHT //
2
-
50
))

422
pygame.display.flip()

423
time.sleep(
2
)

424
exit()

425
frames+=
1
426
pygame.mixer.music.stop()

427
428
Pipe.add_time = ADD_TIME
#再次初始化柱子的速度
429
main()

430
431
432defquit_but
():

433
pygame.quit()

434
exit()

435
436
437defbuttons
(x, y, w, h, color, color2, text,action,list=[]):

438
mouse_position = pygame.mouse.get_pos()

439
click = pygame.mouse.get_pressed()

440if
x+w > mouse_position[
0
] > x
and
y+h > mouse_position[
1
] > y:

441
color = color2

442#get_pressed 只返回鼠标三个键是否被按过的状态 不会分辨它是在哪里被按的
443if
click[
0
]==
1and
action !=
None
:

444
pygame.mixer.Sound.play(music1,
-1
)

445
time.sleep(
0.215
)

446
pygame.mixer.Sound.stop(music1)

447if
list:

448
action(list)

449else
:

450
action()

451
452
pygame.draw.rect(screen, color, (x, y, w, h))

453# font = pygame.font.SysFont('comicsansms', 25)
454
TextSurf = font.render(text,
True
, (
0
,
0
,
0
))

455
TextRect = TextSurf.get_rect()

456
TextRect.center = ((x + (w /
2
)), (y + (h /
2
)))

457
screen.blit(TextSurf, TextRect)

458
pygame.display.update()

459
460
461defsetting
():

462# img = load_image('backgroundx.png')
463
screen.blit(img_x, (
0
,
0
))
# 画背景墙 这种是分开两张的
464
pygame.display.flip()

465whileTrue
:

466for
event
in
pygame.event.get():

467if
event.type==pygame.QUIT:

468
exit()

469
470
buttons(
100
,
200
, BUTTON_WIDTH, BUTT0N_HEIGHT,(
255
,
0
,
0
), (
170
,
0
,
0
),
'easy'
,seteasy,[
0.19
,
2500
,int(
5
/
11
* BK_HEIGHT),
0.97
,
5
,
6
])
# 绘制图标 进行事件
471
buttons(
400
,
200
, BUTTON_WIDTH, BUTT0N_HEIGHT,(
0
,
255
,
0
), (
0
,
170
,
0
),
'normal'
, seteasy,[
0.19
,
2500
,int(
3
/
5
* BK_HEIGHT),
0.96
,
7
,
4
])
# 绘制图标 进行事件
472
buttons(
700
,
200
, BUTTON_WIDTH, BUTT0N_HEIGHT,(
0
,
0
,
255
), (
0
,
0
,
160
),
'hard'
,seteasy,[
0.21
,
1300
,int(
9
/
14
* BK_HEIGHT),
0.96
,
2
,
1
])
# 绘制图标 进行事件
473
buttons(
700
,
550
, BUTTON_WIDTH, BUTT0N_HEIGHT, (
0
,
0
,
255
), (
0
,
0
,
160
),
'back'
, main)
# 绘制图标 进行事件
474
buttons(
100
,
400
, BUTTON_WIDTH, BUTT0N_HEIGHT, (
255
,
0
,
0
), (
170
,
0
,
0
),
'huo lie niao'
,little_bird,[
"f_u"
,
"f_m"
,
"f_w"
])
# 绘制图标 进行事件
475
buttons(
400
,
400
, BUTTON_WIDTH, BUTT0N_HEIGHT, (
0
,
255
,
0
), (
0
,
170
,
0
),
'xiao niao'
,little_bird,[
"bird-wingup"
,
"bird-wingmid"
,
"bird-wingdown"
])
# 绘制图标 进行事件
476# buttons(700, 400, BUTTON_WIDTH, BUTT0N_HEIGHT, (0, 0, 255), (0, 0, 160), 'back', main) # 绘制图标 进行事件
477
478
479defmain
():

480
screen.blit(img_x, (
0
,
0
))
# 画背景墙 这种是分开两张的
481
pygame.display.flip()

482whileTrue
:

483for
event
in
pygame.event.get():

484if
event.type==pygame.QUIT:

485
exit()

486
buttons((BK_WIDTH-BUTTON_WIDTH)//
2
,(BK_HEIGHT-BUTT0N_HEIGHT
-100
)//
2
,BUTTON_WIDTH,BUTT0N_HEIGHT,(
0
,
255
,
0
),(
0
,
170
,
0
),
'Play!'
,game_loop)
#绘制图标 进行事件
487
buttons((BK_WIDTH - BUTTON_WIDTH) //
2
, (BK_HEIGHT - BUTT0N_HEIGHT +
100
) //
2
, BUTTON_WIDTH, BUTT0N_HEIGHT,(
0
,
0
,
255
), (
0
,
0
,
160
),
'setting'
, setting)
# 绘制图标 进行事件
488
buttons((BK_WIDTH - BUTTON_WIDTH) //
2
, (BK_HEIGHT - BUTT0N_HEIGHT +
300
) //
2
, BUTTON_WIDTH, BUTT0N_HEIGHT,(
255
,
0
,
0
), (
170
,
0
,
0
),
'Quit'
, quit_but)

489
490if
__name__ ==
"__main__"
:

491
main()

上述代码是第1版本,简单起见 没有完全封装为面向对象,等后面有时间再进行完善 目标是:全部用类进行分装,然后拆分到多个模块中

继续阅读
阅读原文