社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

用Python3+pygame实现酷炫flappy bird

马哥Linux运维 • 3 年前 • 500 次点击  

之前一直在手机上玩flappy bird游戏,闲暇时间就编写了一个是采用python3+pygame模块制作而成的,运行效果非常流畅,会让你大吃一惊哦


一、运行效果展示

下载游戏之后,注意在自己的python环境中安装pygame模块,如果没有安装可以使用pip install pygame 进行安装


然后使用使用命令运行起这个.py文件,运行之后的第一个界面效果如下,是不是很酷炫

当点击上图中的“Play”按钮之后的效果如下:

运行之后是有音乐的,大家可以下载代码的时候一起将素材下载,这样就在运行时就能听到音乐


二、完整代码

下面代码用到了素材(背景图片,音乐等,下载地址https://www.itprojects.cn/detail.htmlexample_id=8af93ac601523a955f8280c95c2a9e0b )

  1 import math
2 import os
3 import time
4 from random import randint
5 from random import uniform
6 import pygame
7 from pygame.locals import * #导入一些常用的变量
8 from 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 #用于设置鸟的种类
56 def little_bird(list):
57 global a_i
58 global b_i
59 global c_i
60 a_i=list[0]
61 b_i=list[1]
62 c_i=list[2]
63
64
65 #用于设置关卡难度
66 def seteasy(list):
67 global BK_MOVE_SPEED # 背景每毫秒移动的速度 就是柱子移动的速度
68 global ADD_TIME # 每隔多少毫秒就增加一个柱子
69 global TOTAL_PIPE_BODY # 像素值必须为整数 占窗口的3/5
70 global PIPE_RATE
71 global STONE_LEVEL # 鸟出现的等
72 global 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 #子弹类
84 class Bullet(pygame.sprite.Sprite):
85 speed = BULLET_SPEED
86 width = BULLET_WIETH
87 height = BULLET_HEIGHT
88
89 def __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)
94 def update(self):#计算鸟在下一点的新坐标并更新
95 self.x=self.x+self.speed*frames_to_msec(1)
96 @property
97 def image(self):
98 return self.bullet
99 @property
100 def mask(self):
101 return self.mask_bullet
102 @property
103 def rect(self):
104 return Rect(self.x,self.y,Bullet.width,Bullet.height)
105 def visible(self):
106 return 0107
108
109 #小鸟做竖直上抛运动 当小鸟加速到一定状态时 就不再加速了
110 class Bird(pygame.sprite.Sprite):
111
112 width =BIRD_WIDTH #鸟宽
113 height = BIRD_HEIGHT #鸟长
114 sink_gravity = 0.001#鸟的下降重力
115
116 def __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
127 def update(self,t):#计算鸟在下一点的新坐标并更新
128 y_ = self.inital_speed*t+0.5*self.sink_gravity*t*t
129 if self.inital_speed<=0.3:
130 self.inital_speed = self.inital_speed +self.sink_gravity*t
131 self.y+=y_ #在主函数里计算时间
132
133 @property
134 def image(self):
135 if pygame.time.get_ticks()%400>=120:
136 return self.wing_up
137 elif pygame.time.get_ticks()%400>=280:
138 return self.wing_mid
139 else:
140 return self.wing_down
141 @property
142 def mask(self):
143 if pygame.time.get_ticks()%400>=120:
144 return self.mask_wing_up
145 elif pygame.time.get_ticks()%400>=280:
146 return self.mask_wing_mid
147 else:
148 return self.mask_wing_down
149
150 @property
151 def rect(self):
152 return Rect(self.x,self.y,Bird.width,Bird.height)
153
154
155
156 class Pipe(pygame.sprite.Sprite):
157 width = PIPE_WIDTH
158 pipe_head_height = PIPE_HEAD_HEIGHT
159 add_time = ADD_TIME
160
161 def __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
174 for 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
182 for 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
190 def rect(self):
191 return Rect(self.x,0,Pipe.width,PIPE_HEIGHT)
192 @property
193 def visible(self):
194 return -Pipe.width195
196 def update(self,delta_frames=1):
197 self.x-=BK_MOVE_SPEED*frames_to_msec(delta_frames)
198
199 def collides(self,bird):
200 return pygame.sprite.collide_mask(self,bird)
201
202
203 def change_add_time():
204 Pipe.add_time= int( (Pipe.add_time*PIPE_RATE) /100)*100
205 #改变管子的增加时间
206
207
208 #石头具有速度 位置等不同属性
209 #起始的x属性为固定值 y随机 速度在一定范围内随机
210 class Stone(pygame.sprite.Sprite):
211 add_time = STONE_ADD_TIME
212 width = STONE_WIDTH
213 height = STONE_HEIGHT
214 def __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
223 def rect(self):
224 return Rect(self.x,self.y,self.width,self.height)
225 @property
226 def image(self):
227 return self.stone_image
228
229 @property
230 def mask(self):
231 return self.mask_image
232
233 def update(self,frame = 1):
234 self.x -= int(self.speed*frames_to_msec(frame))
235
236 def collides(self, b):
237 return pygame.sprite.collide_mask(self, b)
238
239 def visible(self):
240 return -self.width241
242
243 #返回每关需要达到的通关分数
244 def level_goal(bird):
245 return bird.level*BIRD_INIT_SCORE
246
247 #载入图片
248 def load_image(img_file_name):
249 file_name = os.path.join(".","images",img_file_name)#进行路径字符串的合并
250 img = pygame.image.load(file_name)
251 img.convert()
252 return img
253
254 #根据所在的等级返回需要的背景名
255 def search_bk(bird):
256 return "bk"+str(bird.level)
257
258 img_x = load_image('backgroundx.png')#加载背景图像
259 def load_images():
260 #加载所有游戏需要用到的图像
261 #上面写了这个函数下面就用了起来 join用于分隔符和元组的拼接 os.path.join 用于路径的顺序拼接
262 return {'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
279 def frames_to_msec(frames,fps=FPS):
280 return 1000.0*frames/fps #难道限制的意思就是我可以限制图片出来的时间
281
282 def msec_to_frames(milliseconds, fps=FPS):
283 return fps * milliseconds / 1000.0#转化成对应的帧数
284 #转化成每秒的相应的帧数
285
286
287 def game_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
305 while not done :#当没有按下中止键
306 clock.tick(FPS)
307 if not (pause or frames%msec_to_frames(Pipe.add_time)):#如果没有按下暂停 或者满足新生成柱子的条件
308 pp=Pipe(images['pipe-end'], images['pipe-body'])
309 pipes.append(pp)#生成新管子 并加入队列
310
311 if not (pause or frames%msec_to_frames(Stone.add_time)or bird.level312 ss = Stone(images["stone"])
313 stones.add(ss) #加入新生成的石头
314
315 #判断发生了什么事件进行相应的处理
316 for e in pygame.event.get():
317 if e.type == QUIT:
318 done = True
319 break
320 elif e.type == KEYUP :
321 if e.key == K_p:
322 pause = not pause
323 elif e.key ==K_d:#发射子弹
324 bb=Bullet(bird,images["bullet"])
325 bullets.add(bb)
326 elif e.key ==K_s or e.key == K_SPACE:
327 bird.inital_speed = INITAL_SPEED
328
329
330 elif e.type == MOUSEBUTTONUP:
331 bird.inital_speed =INITAL_SPEED
332
333 # 重新更新时间
334 # 使小鸟又进入相应的运动的开始
335 if pause:
336 continue # 这个时段什么都不做
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
342 if 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
350 if 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
357 if 0>=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
368 while pipes and not pipes[0].visible:
369 pipes.popleft()#当队列不为空 且管子 0 已经不可见的时候
370 for s in stones:#删除看不见的石头
371 if not s.visible():
372 del s
373 for b in bullets:#删除看不见的子弹
374 if not b.visible():
375 del b
376
377
378 for p in pipes:
379 p.update()
380 screen.blit(p.image,p.rect)#在指定的位置 画柱子
381 for s in stones:
382 s.update()
383 screen.blit(s.image,s.rect)
384
385 for b in bullets:
386 b.update()
387 screen.blit(b.bullet,b.rect)
388
389 for p in pipes:
390 if bird.x>p.x+Pipe.width and not 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()#绘制图像到屏幕
407 if bird.score >= level_goal(bird):#如果已经达到了通关分数
408 #升入下一级 首先要初始化所有变量#清空柱子#改变等级
409 change_add_time()
410 pipes.clear()
411 stones.empty()
412 bullets.empty()
413 bird.level += 1 # 分数先暂不做清空后续再加入吧
414 if 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)
419 if 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
432 def quit_but():
433 pygame.quit()
434 exit()
435
436
437 def buttons(x, y, w, h, color, color2, text,action,list=[]):
438 mouse_position = pygame.mouse.get_pos()
439 click = pygame.mouse.get_pressed()
440 if x+w > mouse_position[0] > x and y+h > mouse_position[1] > y:
441 color = color2
442 #get_pressed 只返回鼠标三个键是否被按过的状态 不会分辨它是在哪里被按的
443 if click[0]== 1 and action != None:
444 pygame.mixer.Sound.play(music1, -1)
445 time.sleep(0.215)
446 pygame.mixer.Sound.stop(music1)
447 if list:
448 action(list)
449 else:
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
461 def setting():
462 # img = load_image('backgroundx.png')
463 screen.blit(img_x, (0, 0)) # 画背景墙 这种是分开两张的
464 pygame.display.flip()
465 while True:
466 for event in pygame.event.get():
467 if 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
479 def main():
480 screen.blit(img_x, (0, 0)) # 画背景墙 这种是分开两张的
481 pygame.display.flip()
482 while True:
483 for event in pygame.event.get():
484 if 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
490 if __name__ =="__main__":
491 main()

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

文章转载:Python编程学习圈

(版权归原作者所有,侵删)


点击下方“阅读原文”查看更多

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/115449
 
500 次点击