• Python 用Pygame写一个Flappy Bird经典小游戏


    Pygame是Python用于开发游戏的外置库,可通过pip install pygame安装~

    这篇文章,我们将用Pygame编写一个Flappy Bird小游戏,游戏效果如下:

     设计该游戏需要的照片如下,大家可以下载使用:

    0.png

     

    1.png

     

    2.png

     

    bg_day.png


     现在开始写代码吧!

    先导入模块,导入pygame,pygame的常量,random随机库,sys用于退出程序,copy用于深度克隆,避免不必要的错误

    1. import pygame
    2. from pygame.locals import *
    3. import random as rd
    4. import sys
    5. import copy

    定义常量,path是图片储存目录,后面两个分别是pygame事件中给用户用的接口,一个用于进入游戏时的倒计时事件,另一个则是创建新的管道的事件,后面我们会用到

    1. path="resources/"
    2. COUNTDOWN=USEREVENT+1
    3. NEWTUBE=USEREVENT+2

    我们使用类和对象的方法,定义game类,初始化函数中,先初始化pygame,定义self.W和self.H,表示窗口长宽,创建窗口self.screen,然后设置标题和图标,接下来导入背景图片,并用smoothscale功能缩放到窗口大小

    接下来定义玩家self.player,Player()类在后面的代码中会讲到

    self.tubes列表用于储存管道所在的坐标

    定义管道的宽度

    self.counter 搭配self.toUpdate使用

    self.toUpdate 刷新管道位置的速率

    self.tubeSpeed 管道移动的速度

    self.countdown 倒计时

    self.state 游戏的状态,初始是倒计时

    self.score 分数,这个就不用说了吧。。

    最后设置每1000毫秒(1秒)切换数字,也就是从3数到1就开始游戏,这里需要用set_timer方法

    1. class Game:
    2. def __init__(self):
    3. pygame.init()
    4. self.W,self.H=800,800
    5. self.screen=pygame.display.set_mode((self.W,self.H))
    6. pygame.display.set_caption("FlappyBird Classic")
    7. pygame.display.set_icon(pygame.image.load(path+"0.png"))
    8. self.bg=pygame.transform.smoothscale(pygame.image.load(path+"bg_day.png"),(self.W,self.H))
    9. self.player=Player()
    10. self.tubes=[]
    11. self.tubeWidth=45
    12. self.counter=0
    13. self.toUpdate=2
    14. self.tubeSpeed=2
    15. self.state="countdown"
    16. self.countdown=3
    17. self.score=0
    18. pygame.time.set_timer(COUNTDOWN,1000)

    接下来是listen函数(Game类方法),用于监听事件

    先遍历pygame的事件,如果是关闭窗口操作(QUIT),则退出窗口

    注意!这里退出窗口可以直接写exit(),就不用导入sys库了,但是如果要将python文件打包成exe的话,就必须使用sys库,否则程序找不到exit函数,就要使用sys里的exit进行退出操作!

    接下来,如果是键盘事件(KEYDOWN),就再判断,是否按下空格,是的话就跳跃,jump类方法待会会讲

    如果是COUNTDOWN事件,就进行倒计时操作

    如果是NEWTUBE则创建管道,newTube函数待会会讲

    1. def listen(self):
    2. for event in pygame.event.get():
    3. if event.type==QUIT:
    4. sys.exit()
    5. if event.type==KEYDOWN:
    6. if event.key==K_SPACE and self.state=="play":
    7. self.player.jump()
    8. if event.type==COUNTDOWN:
    9. self.countdown-=1
    10. if self.countdown<=0:
    11. self.state="play"
    12. pygame.time.set_timer(COUNTDOWN,0)
    13. pygame.time.set_timer(NEWTUBE,2200)
    14. if event.type==NEWTUBE:
    15. self.newTube()

    接下来,是绘制操作,先绘制背景,判断,如果输了,就直接在屏幕中显示分数

    正在游戏,则更新player的位置,待会Player类会一起讲这个类方法

    然后就是绘制玩家和管道,还有左上角的分数,倒计时的时候呢就直接显示倒计时的数字即可,这部分代码不做太详细的讲解

    这里,self.tubes中的参数类型如下:

    [x坐标,y坐标,管道高度]

    1. def draw(self):
    2. self.screen.blit(self.bg,(0,0))
    3. if self.state=="lose":
    4. t=self.print_text("simhei",128,str(self.score),(255,0,0))
    5. tr=t.get_rect()
    6. tr.center=self.W/2,self.H/2
    7. self.screen.blit(t,tr)
    8. if self.state=="play":
    9. self.player.update()
    10. self.screen.blit(self.player.image,self.player.rect)
    11. if self.state=="play":
    12. self.updateTube()
    13. for tube in self.tubes:
    14. x,y=tube[0],tube[1]
    15. height=tube[2]
    16. rect=pygame.draw.rect(self.screen,(0,255,0),(x,y,self.tubeWidth,height))
    17. if self.player.rect.colliderect(rect):
    18. self.state="lose"
    19. while self.player.rect.top<self.H:
    20. self.screen.blit(self.bg,(0,0))
    21. self.player.update()
    22. self.screen.blit(self.player.image,self.player.rect)
    23. pygame.display.update()
    24. return
    25. t=self.print_text("simhei",35,str(self.score),(255,0,0))
    26. self.screen.blit(t,(25,25))
    27. self.check()
    28. if self.state=="countdown":
    29. t=self.print_text("simhei",72,str(self.countdown),(255,0,0))
    30. tr=t.get_rect()
    31. tr.center=self.W/2,self.H/2
    32. self.screen.blit(t,tr)

    检查是否撞到管道

    1. def check(self):
    2. if self.player.rect.bottom>=self.H or self.player.rect.top<=0:
    3. self.state="lose"
    4. while self.player.rect.top<self.H:
    5. self.screen.blit(self.bg,(0,0))
    6. self.player.update()
    7. self.screen.blit(self.player.image,self.player.rect)
    8. pygame.display.update()

    更新管道位置函数

    1. def updateTube(self):
    2. self.counter+=1
    3. if self.counter>=self.toUpdate:
    4. for i,tube in enumerate(self.tubes):
    5. tube[0]-=self.tubeSpeed
    6. self.counter=0
    7. for i,tube in enumerate(copy.copy(self.tubes)):
    8. if tube[0]+self.tubeWidth<=0:
    9. self.tubes.pop(i)
    10. if tube[1]<1:
    11. self.score+=1
    12. break

    主循环

    1. def run(self):
    2. while True:
    3. self.listen()
    4. self.draw()
    5. pygame.display.update()

    将文字处理成Surface对象的静态方法

    1. @staticmethod
    2. def print_text(name,size,text,color):
    3. font=pygame.font.SysFont(name,size)
    4. image=font.render(text,True,color)
    5. return image

    创建新管道的方法

    1. def newTube(self):
    2. spacing=200
    3. t1h=rd.randint(50,self.H-spacing)
    4. t2h=self.H-spacing-t1h
    5. self.tubes.append([self.W,0,t1h])
    6. self.tubes.append([self.W,t1h+spacing,t2h])

    最后是Player类,update即更新Player位置,每被运行4次update进行一次掉落和改变图像的操作,这里有3张图片,连贯起来就像鸟儿在飞的动画一样

    1. class Player:
    2. def __init__(self):
    3. self.images=[pygame.image.load(path+"0.png"),
    4. pygame.image.load(path+"1.png"),
    5. pygame.image.load(path+"2.png")]
    6. self.item=0
    7. self.image=self.images[self.item]
    8. self.rect=self.image.get_rect()
    9. try:
    10. self.rect.center=90,game.H/2
    11. except NameError:
    12. self.rect.center=90,800/2
    13. self.fallSpeed=0
    14. self.counter=0
    15. self.toUpdate=4
    16. def jump(self):
    17. self.fallSpeed=-10
    18. def fall(self):
    19. self.rect.y+=self.fallSpeed
    20. self.fallSpeed+=1
    21. def change(self):
    22. self.item+=1
    23. if self.item>2:
    24. self.item=0
    25. self.image=self.images[self.item]
    26. def update(self):
    27. self.counter+=1
    28. if self.counter>=self.toUpdate:
    29. self.fall()
    30. self.change()
    31. self.counter=0

    接下来,创建game对象,并启动主循环

    1. if __name__ == '__main__':
    2. game=Game()
    3. game.run()

    这样就可以实现这个经典FlappyBird游戏啦!代码不长,但可能有些复杂,如果有不清楚的地方可以在评论中提出,或是私信问我,我看到了就会第一时间回复你们提出的问题哒~

    这里附上最终的代码

    1. import pygame
    2. from pygame.locals import *
    3. import random as rd
    4. import sys
    5. import copy
    6. path="resources/"
    7. COUNTDOWN=USEREVENT+1
    8. NEWTUBE=USEREVENT+2
    9. class Game:
    10. def __init__(self):
    11. pygame.init()
    12. self.W,self.H=800,800
    13. self.screen=pygame.display.set_mode((self.W,self.H))
    14. pygame.display.set_caption("FlappyBird Classic")
    15. pygame.display.set_icon(pygame.image.load(path+"0.png"))
    16. self.bg=pygame.transform.smoothscale(pygame.image.load(path+"bg_day.png"),(self.W,self.H))
    17. self.player=Player()
    18. self.tubes=[]
    19. self.tubeWidth=45
    20. self.counter=0
    21. self.toUpdate=2
    22. self.tubeSpeed=2
    23. self.state="countdown"
    24. self.countdown=3
    25. self.score=0
    26. pygame.time.set_timer(COUNTDOWN,1000)
    27. def listen(self):
    28. for event in pygame.event.get():
    29. if event.type==QUIT:
    30. sys.exit()
    31. if event.type==KEYDOWN:
    32. if event.key==K_SPACE and self.state=="play":
    33. self.player.jump()
    34. if event.type==COUNTDOWN:
    35. self.countdown-=1
    36. if self.countdown<=0:
    37. self.state="play"
    38. pygame.time.set_timer(COUNTDOWN,0)
    39. pygame.time.set_timer(NEWTUBE,2200)
    40. if event.type==NEWTUBE:
    41. self.newTube()
    42. def draw(self):
    43. self.screen.blit(self.bg,(0,0))
    44. if self.state=="lose":
    45. t=self.print_text("simhei",128,str(self.score),(255,0,0))
    46. tr=t.get_rect()
    47. tr.center=self.W/2,self.H/2
    48. self.screen.blit(t,tr)
    49. if self.state=="play":
    50. self.player.update()
    51. self.screen.blit(self.player.image,self.player.rect)
    52. if self.state=="play":
    53. self.updateTube()
    54. for tube in self.tubes:
    55. x,y=tube[0],tube[1]
    56. height=tube[2]
    57. rect=pygame.draw.rect(self.screen,(0,255,0),(x,y,self.tubeWidth,height))
    58. if self.player.rect.colliderect(rect):
    59. self.state="lose"
    60. while self.player.rect.top<self.H:
    61. self.screen.blit(self.bg,(0,0))
    62. self.player.update()
    63. self.screen.blit(self.player.image,self.player.rect)
    64. pygame.display.update()
    65. return
    66. t=self.print_text("simhei",35,str(self.score),(255,0,0))
    67. self.screen.blit(t,(25,25))
    68. self.check()
    69. if self.state=="countdown":
    70. t=self.print_text("simhei",72,str(self.countdown),(255,0,0))
    71. tr=t.get_rect()
    72. tr.center=self.W/2,self.H/2
    73. self.screen.blit(t,tr)
    74. def check(self):
    75. if self.player.rect.bottom>=self.H or self.player.rect.top<=0:
    76. self.state="lose"
    77. while self.player.rect.top<self.H:
    78. self.screen.blit(self.bg,(0,0))
    79. self.player.update()
    80. self.screen.blit(self.player.image,self.player.rect)
    81. pygame.display.update()
    82. def updateTube(self):
    83. self.counter+=1
    84. if self.counter>=self.toUpdate:
    85. for i,tube in enumerate(self.tubes):
    86. tube[0]-=self.tubeSpeed
    87. self.counter=0
    88. for i,tube in enumerate(copy.copy(self.tubes)):
    89. if tube[0]+self.tubeWidth<=0:
    90. self.tubes.pop(i)
    91. if tube[1]<1:
    92. self.score+=1
    93. break
    94. def run(self):
    95. while True:
    96. self.listen()
    97. self.draw()
    98. pygame.display.update()
    99. @staticmethod
    100. def print_text(name,size,text,color):
    101. font=pygame.font.SysFont(name,size)
    102. image=font.render(text,True,color)
    103. return image
    104. def newTube(self):
    105. spacing=200
    106. t1h=rd.randint(50,self.H-spacing)
    107. t2h=self.H-spacing-t1h
    108. self.tubes.append([self.W,0,t1h])
    109. self.tubes.append([self.W,t1h+spacing,t2h])
    110. class Player:
    111. def __init__(self):
    112. self.images=[pygame.image.load(path+"0.png"),
    113. pygame.image.load(path+"1.png"),
    114. pygame.image.load(path+"2.png")]
    115. self.item=0
    116. self.image=self.images[self.item]
    117. self.rect=self.image.get_rect()
    118. try:
    119. self.rect.center=90,game.H/2
    120. except NameError:
    121. self.rect.center=90,800/2
    122. self.fallSpeed=0
    123. self.counter=0
    124. self.toUpdate=4
    125. def jump(self):
    126. self.fallSpeed=-10
    127. def fall(self):
    128. self.rect.y+=self.fallSpeed
    129. self.fallSpeed+=1
    130. def change(self):
    131. self.item+=1
    132. if self.item>2:
    133. self.item=0
    134. self.image=self.images[self.item]
    135. def update(self):
    136. self.counter+=1
    137. if self.counter>=self.toUpdate:
    138. self.fall()
    139. self.change()
    140. self.counter=0
    141. if __name__ == '__main__':
    142. game=Game()
    143. game.run()

    喜欢的话就点赞关注吧!

  • 相关阅读:
    cobol数据类型
    Java下打印1-100以内的质数
    如何给页面元素添加水印背景,在vue中怎么处理?
    RL_sysu_homework
    凡客平台数据接口,根据ID取商品详情,Onebound电商API
    关于汽车html网页设计完整版,10个以汽车为主题的网页设计与实现
    洛谷P1242 新汉诺塔
    HTML下雪/烟花
    【网络杂烩 ---> 网络安全】DLL 注入 --- c/c++ 代码实现(超 · 详细)
    Java多线程超级详解(只看这篇就够了)
  • 原文地址:https://blog.csdn.net/leleprogrammer/article/details/125617706