pygame面向对象的飞行小鸟实现(Flappy bird)


Posted in Python onApril 01, 2021

一些想法

自学python已经有快三个月了 最近这段时间没有怎么写过python 很多东西反而又遗忘了 准备翻以前的笔记复习一下在博客上记录下来 自己也没能够做出什么厉害的东西 小鸟游戏算是目前自己写的最好的一个代码了

基本游戏界面就是这样

pygame面向对象的飞行小鸟实现(Flappy bird)

分析需要的功能

我的构思是将游戏分成三个部分

  • 初始游戏菜单界面
  • 游戏进行界面
  • 游戏结束界面

游戏里的角色和道具则使用类

  • 小鸟类
  • 管道类

因为是使用pygame模块 我对这个模块也很不熟悉 很多功能都是论坛参考其他大神的 比如

pygame.transform 里面的各种变化功能
pygame.sprite 精灵模块里面的方法

构建整体框架

1.导入pygame和random

pygame拥有丰富的制作游戏的功能

random是随机模块 游戏里各种随机事件就是通过这个模块功能实现

import pygame
import random

2.我们写一个小的项目之前 需要将每个功能分成不同的代码块

定义的变量都写到最上面

MAP_WIDTH = 288 # 地图大小
MAP_HEIGHT = 512
FPS = 30 # 刷新率
PIPE_GAPS = [110, 120, 130, 140, 150, 160] # 缺口的距离 有这6个随机距离

# 写的途中的全局变量都可以写在最上面

全局变量我一般喜欢使用大写来区分

3.游戏窗口的设置

pygame.init() # 进行初始化
SCREEN = pygame.display.set_mode((MAP_WIDTH, MAP_HEIGHT)) # 屏幕大小
pygame.display.set_caption('飞行小鸟') # 标题
CLOCK = pygame.time.Clock()

4.加载素材
加载游戏图片和音乐

SPRITE_FILE = './images'
IMAGES = {}
IMAGES['guide'] = pygame.image.load(SPRITE_FILE + 'guide.png')
IMAGES['gameover'] = pygame.image.load(SPRITE_FILE + 'gameover.png')
IMAGES['floor'] = pygame.image.load(SPRITE_FILE + 'floor.png')

SPRITE_SOUND = './audio/'
SOUNDS = {} 
SOUNDS['start'] = pygame.mixer.Sound(SPRITE_SOUND + 'start.wav')
SOUNDS['die'] = pygame.mixer.Sound(SPRITE_SOUND + 'die.wav')
SOUNDS['hit'] = pygame.mixer.Sound(SPRITE_SOUND + 'hit.wav')
SOUNDS['score'] = pygame.mixer.Sound(SPRITE_SOUND + 'score.wav')

5.执行函数
就是执行程序的函数

def main():
		menu_window()
    result = game_window()
    end_window(result)

6.程序入口

if __name__ == '__main__':
  main()

7.我将游戏分成了三个界面

  • 初始游戏菜单界面
  • 游戏进行界面
  • 游戏结束界面
def menu_window():
	pass

def game_window():
	pass

def end_window(result):
	pass

# 这里就是写运行三种游戏界面的代码

8.因为要显示游戏得分

所以专门写一个方法在游戏主界面代码里面直接调用这个方法 让代码不会显得冗余

9.最后就是我们游戏角色和道具的类方法

  • 小鸟类
  • 管道类
class Bird(pygame.sprite.Sprite):
  def __init__(self, x, y):
    # super(Bird, self).__init__(x, y)
    pygame.sprite.Sprite.__init__(self)
    pass

  def update(self, flap=False):
    pass

  def go_die(self):
    pass

class Pipe(pygame.sprite.Sprite):
  def __init__(self, x, y, upwards=True):
    pygame.sprite.Sprite.__init__(self)
    pass

  def update(self):
    pass

我们把整体框架搭建好之后 就可以着手完善代码

着手完整代码

"""
Project: pygame
Creator: stan Z
Create time: 2021-03-08 19:37
IDE: PyCharm
Introduction:
"""
import pygame
import random

######################################## 定义变量
MAP_WIDTH = 288 # 地图大小
MAP_HEIGHT = 512
FPS = 30 # 刷新率
PIPE_GAPS = [90, 100, 110, 120, 130, 140] # 缺口的距离 有这6个随机距离
# PIPE_GAPS1 = []
PIPE_HEIGHT_RANGE = [int(MAP_HEIGHT * 0.3), int(MAP_HEIGHT * 0.7)] # 管道长度范围
PIPE_DISTANCE = 120 # 管道之间距离

######################################## 游戏基本设置
pygame.init() # 进行初始化
SCREEN = pygame.display.set_mode((MAP_WIDTH, MAP_HEIGHT)) # 调用窗口设置屏幕大小
pygame.display.set_caption('飞行小鸟byStanZ') # 标题
CLOCK = pygame.time.Clock() # 建立时钟

######################################## 加载素材
SPRITE_FILE = './images'
# 列表推导式 获得三种不同的鸟和三种状态
BIRDS = [[f'{SPRITE_FILE}{bird}-{move}.png' for move in ['up', 'mid', 'down']] for bird in ['red', 'blue', 'yellow']]
BGPICS = [SPRITE_FILE + 'day.png', SPRITE_FILE + 'night.png']
PIPES = [SPRITE_FILE + 'green-pipe.png', SPRITE_FILE + 'red-pipe.png']
NUMBERS = [f'{SPRITE_FILE}{n}.png' for n in range(10)]

# 将图片设置成一个大字典 里面通过key-value存不同的场景图
IMAGES = {}
IMAGES['numbers'] = [pygame.image.load(number) for number in NUMBERS] # 数字素材有10张 因此遍历
IMAGES['guide'] = pygame.image.load(SPRITE_FILE + 'guide.png')
IMAGES['gameover'] = pygame.image.load(SPRITE_FILE + 'gameover.png')
IMAGES['floor'] = pygame.image.load(SPRITE_FILE + 'floor.png')

# 地板的高是一个很常用的变量 因此我们专门拿出来
FLOOR_H = MAP_HEIGHT - IMAGES['floor'].get_height() # 屏幕高减去floor图片的高 就是他在屏幕里的位置

SPRITE_SOUND = './sound'
SOUNDS = {} # 同理声音素材也这样做
SOUNDS['start'] = pygame.mixer.Sound(SPRITE_SOUND + 'start.wav')
SOUNDS['die'] = pygame.mixer.Sound(SPRITE_SOUND + 'die.wav')
SOUNDS['hit'] = pygame.mixer.Sound(SPRITE_SOUND + 'hit.wav')
SOUNDS['score'] = pygame.mixer.Sound(SPRITE_SOUND + 'score.wav')
SOUNDS['flap'] = pygame.mixer.Sound(SPRITE_SOUND + 'flap.wav')
SOUNDS['death'] = pygame.mixer.Sound(SPRITE_SOUND + 'death.wav')
SOUNDS['main'] = pygame.mixer.Sound(SPRITE_SOUND + 'main_theme.ogg')
SOUNDS['world_clear'] = pygame.mixer.Sound(SPRITE_SOUND + 'world_clear.wav')


# 执行函数
def main():
  while True:
    IMAGES['bgpic'] = pygame.image.load(random.choice(BGPICS)) # random的choice方法可以随机从列表里返回一个元素 白天或者黑夜
    IMAGES['bird'] = [pygame.image.load(frame) for frame in random.choice(BIRDS)] # 列表推导式 鸟也是随机
    pipe = pygame.image.load(random.choice(PIPES))
    IMAGES['pipe'] = [pipe, pygame.transform.flip(pipe, False, True)] # flip是翻转 将管道放下面和上面 Flase水平不动,True上下翻转
    SOUNDS['start'].play()
    # SOUNDS['main'].play()
    menu_window()
    result = game_window()
    end_window(result)


def menu_window():
  SOUNDS['world_clear'].play()
  floor_gap = IMAGES['floor'].get_width() - MAP_WIDTH # 地板间隙 336 - 288 = 48
  floor_x = 0

  # 标题位置
  guide_x = (MAP_WIDTH - IMAGES['guide'].get_width()) / 2
  guide_y = MAP_HEIGHT * 0.12

  # 小鸟位置
  bird_x = MAP_WIDTH * 0.2
  bird_y = MAP_HEIGHT * 0.5 - IMAGES['bird'][0].get_height() / 2
  bird_y_vel = 1 # 小鸟飞行的速率 按y坐标向下
  max_y_shift = 50 # 小鸟飞行的最大幅度
  y_shift = 0 # 小鸟起始幅度为0

  idx = 0 # 小鸟翅膀煽动频率
  frame_seq = [0] * 5 + [1] * 5 + [2] * 5 + [1] * 5 # 控制小鸟翅膀运动上中下

  while True:
    for event in pygame.event.get(): # 监控行为
      if event.type == pygame.QUIT:
        quit()
      elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
        return

    if floor_x <= -floor_gap: # 当地板跑到最大间隔的时候
      floor_x = floor_x + floor_gap # 刷新地板的x轴
    else:
      floor_x -= 4 # 地板 x轴的移动速度

    if abs(y_shift) == max_y_shift: # 如果y_shift的绝对值 = 最大幅度
      bird_y_vel *= -1 # 调转方向飞 同时飞行速度为1
    else:
      bird_y += bird_y_vel
    y_shift += bird_y_vel # 小鸟y轴正负交替 上下飞

    # 小鸟翅膀
    idx += 1 # 翅膀煽动频率
    idx %= len(frame_seq) # 通过取余得到 0 1 2
    frame_index = frame_seq[idx] # 小鸟图片的下标 就是翅膀的状态

    SCREEN.blit(IMAGES['bgpic'], (0, 0))
    SCREEN.blit(IMAGES['floor'], (floor_x, FLOOR_H))
    SCREEN.blit(IMAGES['guide'], (guide_x, guide_y))
    SCREEN.blit(IMAGES['bird'][frame_index], (bird_x, bird_y))

    pygame.display.update()
    CLOCK.tick(FPS) # 以每秒30帧刷新屏幕


def game_window():
  SOUNDS['world_clear'].stop()
  SOUNDS['main'].play()
  score = 0

  floor_gap = IMAGES['floor'].get_width() - MAP_WIDTH # 地板间隙 336 - 288 = 48
  floor_x = 0

  # 小鸟位置
  bird_x = MAP_WIDTH * 0.2
  bird_y = MAP_HEIGHT * 0.5 - IMAGES['bird'][0].get_height() / 2
  bird = Bird(bird_x, bird_y)

  n_pair = round(MAP_WIDTH / PIPE_DISTANCE) # 四舍五入取整数 屏幕宽度/两个管道之间的距离 这个距离时候刷新第二个管道 2.4
  pipe_group = pygame.sprite.Group() # 是一个集合

  # 生成前面的管道
  pipe_x = MAP_WIDTH
  pipe_y = random.randint(PIPE_HEIGHT_RANGE[0], PIPE_HEIGHT_RANGE[1]) # 管道长度随机从153.6 到 358.4
  pipe1 = Pipe(pipe_x, pipe_y, upwards=True) # 创建一个管道对象
  pipe_group.add(pipe1) # 将对象添加到这个精灵集合里面
  pipe2 = Pipe(pipe_x, pipe_y - random.choice(PIPE_GAPS), upwards=False) # 翻转的管道
  pipe_group.add(pipe2)

  SOUNDS['flap'].play()

  while True:
    flap = False

    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        quit()
      elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: # 空格拍翅膀
        SOUNDS['flap'].play()
        flap = True

    bird.update(flap)

    if floor_x <= -floor_gap: # 当地板跑到最大间隔的时候
      floor_x = floor_x + floor_gap # 刷新地板的x轴
    else:
      floor_x -= 4 # 地板 x轴的移动速度

    # 生成最后一个管道
    if len(pipe_group) / 2 < n_pair: # 当管道组长度<2.4 时 意思就是两个半管道的时候
      # sprites()将管道组返回成列表
      last_pipe = pipe_group.sprites()[-1]
      pipe_x = last_pipe.rect.right + PIPE_DISTANCE
      pipe_y = random.randint(PIPE_HEIGHT_RANGE[0], PIPE_HEIGHT_RANGE[1])
      pipe1 = Pipe(pipe_x, pipe_y, upwards=True)
      pipe_group.add(pipe1)
      pipe2 = Pipe(pipe_x, pipe_y - random.choice(PIPE_GAPS), upwards=False)
      pipe_group.add(pipe2)

    pipe_group.update()
    # 鸟的矩形y坐标如果大于地板的高度 就死亡
    # pygame.sprite.spritecollideany 碰撞函数 如果bird和pipe_group碰撞了 就死亡
    if bird.rect.y > FLOOR_H or bird.rect.y < 0 or pygame.sprite.spritecollideany(bird, pipe_group):
      SOUNDS['score'].stop()
      SOUNDS['main'].stop()
      SOUNDS['hit'].play()
      SOUNDS['die'].play()
      SOUNDS['death'].play()
      # 保存死亡时的鸟儿 分数 管道 继续显示在结束窗口
      result = {'bird': bird, 'score': score, 'pipe_group': pipe_group}
      return result

    # 当小鸟左边大于 管道右边就得分
    if pipe_group.sprites()[0].rect.left == 0:
      SOUNDS['score'].play()
      score += 1

    SCREEN.blit(IMAGES['bgpic'], (0, 0))
    pipe_group.draw(SCREEN)
    SCREEN.blit(IMAGES['floor'], (floor_x, FLOOR_H))
    SCREEN.blit(bird.image, bird.rect)
    show_score(score)
    pygame.display.update()
    CLOCK.tick(FPS)


def end_window(result):
  # 显示gameover的图片
  gameover_x = MAP_WIDTH * 0.5 - IMAGES['gameover'].get_width() / 2
  gameover_y = MAP_HEIGHT * 0.4
  bird = result['bird']
  pipe_group = result['pipe_group']

  while True:
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        quit()
      elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE and bird.rect.y > FLOOR_H:
        SOUNDS['death'].stop()
        return

    # 使用类go_die方法 鸟儿撞墙后 旋转往下
    bird.go_die()
    SCREEN.blit(IMAGES['bgpic'], (0, 0))
    pipe_group.draw(SCREEN)
    SCREEN.blit(IMAGES['floor'], (0, FLOOR_H))
    SCREEN.blit(IMAGES['gameover'], (gameover_x, gameover_y))
    show_score(result['score'])
    SCREEN.blit(bird.image, bird.rect)
    pygame.display.update()
    CLOCK.tick(FPS)


# 显示得分
def show_score(score):
  score_str = str(score)
  w = IMAGES['numbers'][0].get_width()
  x = MAP_WIDTH / 2 - 2 * w / 2
  y = MAP_HEIGHT * 0.1
  for number in score_str: # IMAGES['numbers'] = [pygame.image.load(number) for number in NUMBERS]
    SCREEN.blit(IMAGES['numbers'][int(number)], (x, y))
    x += w


class Bird(pygame.sprite.Sprite):
  def __init__(self, x, y):
    # super(Bird, self).__init__(x, y)
    pygame.sprite.Sprite.__init__(self)
    self.frames = IMAGES['bird'] # 鸟儿框架
    self.frame_list = [0] * 5 + [1] * 5 + [2] * 5 + [1] * 5 # 控制小鸟翅膀运动上中下
    self.frame_index = 0
    self.image = self.frames[self.frame_list[self.frame_index]] # 和菜单界面小鸟扇翅膀一个原理
    self.rect = self.image.get_rect() # 鸟儿的矩形
    self.rect.x = x
    self.rect.y = y
    self.gravity = 1 # 重力
    self.flap_acc = -10 # 翅膀拍打往上飞 y坐标-10
    self.y_vel = -10 # y坐标的速度
    self.max_y_vel = 15 # y轴下落最大速度
    self.rotate = 0 # 脑袋朝向
    self.rotate_vel = -3 # 转向速度
    self.max_rotate = -30 # 最大转向速度
    self.flap_rotate = 45 # 按了空格只会脑袋朝向上30度

  def update(self, flap=False):
    if flap:
      self.y_vel = self.flap_acc # 拍打翅膀 则y速度-10向上
      self.rotate = self.flap_rotate
    else:
      self.rotate = self.rotate + self.rotate_vel

    self.y_vel = min(self.y_vel + self.gravity, self.max_y_vel)
    self.rect.y += self.y_vel # 小鸟向上移动的距离
    self.rorate = max(self.rotate + self.rotate_vel, self.max_rotate)

    self.frame_index += 1 # 扇翅膀的速率
    self.frame_index %= len(self.frame_list) # 0~20
    self.image = self.frames[self.frame_list[self.frame_index]]
    self.image = pygame.transform.rotate(self.image, self.rotate) # transform变形方法 旋转

  def go_die(self):
    if self.rect.y < FLOOR_H:
      self.y_vel = self.max_y_vel
      self.rect.y += self.y_vel
      self.rotate = -90
      self.image = self.frames[self.frame_list[self.frame_index]]
      self.image = pygame.transform.rotate(self.image, self.rotate)


# 管道类
class Pipe(pygame.sprite.Sprite):
  def __init__(self, x, y, upwards=True):
    pygame.sprite.Sprite.__init__(self)
    self.x_vel = -4 # 管道移动速度
    # 默认属性为真 则是正向管道
    if upwards:
      self.image = IMAGES['pipe'][0]
      self.rect = self.image.get_rect()
      self.rect.x = x
      self.rect.top = y
    # 利用flip方法 旋转管道成为反向管道
    else:
      self.image = IMAGES['pipe'][1]
      self.rect = self.image.get_rect()
      self.rect.x = x
      self.rect.bottom = y

  def update(self):
    self.rect.x += self.x_vel # 管道x轴加移动速度
    if self.rect.right < 0:
      self.kill()


if __name__ == '__main__':
  main()

到此这篇关于pygame面向对象的飞行小鸟实现(Flappy bird)的文章就介绍到这了,更多相关pygame 飞行小鸟内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python计算程序开始到程序结束的运行时间和程序运行的CPU时间
Nov 28 Python
pycharm 使用心得(五)断点调试
Jun 06 Python
python web框架学习笔记
May 03 Python
Python selenium 三种等待方式详解(必会)
Sep 15 Python
Python使用正则表达式过滤或替换HTML标签的方法详解
Sep 25 Python
django 将model转换为字典的方法示例
Oct 16 Python
Pandas 按索引合并数据集的方法
Nov 15 Python
django Admin文档生成器使用详解
Jul 22 Python
详细整理python 字符串(str)与列表(list)以及数组(array)之间的转换方法
Aug 30 Python
通过 Python 和 OpenCV 实现目标数量监控
Jan 05 Python
Python终端输出彩色字符方法详解
Feb 11 Python
python实现三次密码验证的示例
Apr 29 Python
如何用python插入独创性声明
python OpenCV学习笔记
python基于OpenCV模板匹配识别图片中的数字
Python insert() / append() 用法 Leetcode实战演示
Mar 31 #Python
tensorflow学习笔记之tfrecord文件的生成与读取
Mar 31 #Python
Python中快速掌握Data Frame的常用操作
Mar 31 #Python
pycharm无法导入lxml的解决办法
You might like
使用字符串函数输出整数化的PHP版本号
2006/10/09 PHP
浅析PHP substr,mb_substr以及mb_strcut的区别和用法
2013/06/21 PHP
PHP处理JSON字符串key缺少双引号的解决方法
2014/09/16 PHP
WordPress的主题编写中获取头部模板和底部模板
2015/12/28 PHP
thinkPHP自定义类实现方法详解
2016/11/30 PHP
如何直接访问php实例对象中的private属性详解
2017/10/12 PHP
PHP多个图片压缩成ZIP的方法
2020/08/18 PHP
学习YUI.Ext第五日--做拖放Darg&amp;Drop
2007/03/10 Javascript
JavaScript是否可实现多线程  深入理解JavaScript定时机制
2009/12/22 Javascript
关于firefox的ElementTraversal 接口 使用说明
2010/11/11 Javascript
js TextArea的选中区域处理
2010/12/28 Javascript
Javascript实现滑块滑动改变值的实现代码
2013/04/12 Javascript
JS 退出系统并跳转到登录界面的实现代码
2013/06/29 Javascript
jQuery实现用户注册的表单验证示例
2013/08/28 Javascript
JavaScript中字符串分割函数split用法实例
2015/04/07 Javascript
JS+CSS实现自适应选项卡宽度的圆角滑动门效果
2015/09/15 Javascript
基于Bootstrap实现下拉菜单项和表单导航条(两个菜单项,一个下拉菜单和登录表单导航条)
2016/07/22 Javascript
React利用插件和不用插件实现双向绑定的方法详解
2017/07/03 Javascript
js获取文件里面的所有文件名(实例)
2017/10/17 Javascript
d3.js实现自定义多y轴折线图的示例代码
2018/05/30 Javascript
Angular6 写一个简单的Select组件示例
2018/08/20 Javascript
JS中超越现实的匿名函数用法实例分析
2019/06/21 Javascript
vue模块移动组件的实现示例
2020/05/20 Javascript
浅谈js数组splice删除某个元素爬坑
2020/10/14 Javascript
VUE项目实现主题切换的多种方法
2020/11/26 Vue.js
Python全局变量操作详解
2015/04/14 Python
详细讲解用Python发送SMTP邮件的教程
2015/04/29 Python
Python Django框架单元测试之文件上传测试示例
2019/05/17 Python
Python 日期的转换及计算的具体使用详解
2020/01/16 Python
Django 允许局域网中的机器访问你的主机操作
2020/05/13 Python
python性能测试工具locust的使用
2020/12/28 Python
香蕉共和国Banana Republic官网:美国GAP旗下偏贵族风格服饰品牌
2016/11/21 全球购物
给领导的检讨书
2014/02/16 职场文书
机关领导查摆四风思想汇报
2014/09/13 职场文书
硕士毕业答辩开场白
2015/05/27 职场文书
三好学生评选事迹材料(2016精选版)
2016/02/25 职场文书