当前位置: 萬仟网 > IT编程>软件设计>面向对象 > pygame面向对象的飞行小鸟实现(Flappy bird)

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

2021年04月01日  | 萬仟网IT编程  | 我要评论
一些想法自学python已经有快三个月了 最近这段时间没有怎么写过python 很多东西反而又遗忘了 准备翻以前的笔记复习一下在博客上记录下来 自己也没能够做出什么厉害的东西 小鸟游戏算是目前自己写的

一些想法

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

基本游戏界面就是这样

在这里插入图片描述

分析需要的功能

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

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

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

  • 小鸟类
  • 管道类

因为是使用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 飞行小鸟内容请搜索萬仟网以前的文章或继续浏览下面的相关文章希望大家以后多多支持萬仟网!

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

  • 面向对象浅见

    面向对象浅见

    面向对象面向对象的概念面向过程和面向对象的区别对象类面向对象实战思路类的创建创建对象使用对象创建对象的过程OOP... [阅读全文]
  • Java代码优化实践

    1. 尽量指定类的final修饰符 带有final修饰符的类是不可派生的。指定一个类为final,则该类所有方法都是final。Java编译器会会找... [阅读全文]
  • 开发相关的几个概念

    计算机程序中涉及到的概念都比较抽象、专业。经常有初学者程序的人反应说,“别人说的什么名词性的东西,根本不明白是什么意思”。的确,掌握一些开发相关的概... [阅读全文]
  • java基础第十篇之异常

    1.1接口概念 类:具有相同属性和功能的事物集合 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”。 接口只描述所应该具备的方... [阅读全文]
  • 关于新手数组:样题:陶陶摘苹果

    关于新手数组:样题:陶陶摘苹果

    这道题,是集数组,for循环和if语句一体的一道水题,首先用数组及for循环输入10个苹果的高度,然后………… ... [阅读全文]
  • Hexo系列 - 升级NexT

    很久没有登录 NexT 官网了,这几天去官网查看,发现 NexT 已经升级到 NexT7 了。便想到了升级主题的... [阅读全文]
  • Java基础语法(总结篇)

    Java基础语法(总结篇)

    关键字&标识符 关键字的概念与特征 概念:Java关键字是事先定义好的对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序... [阅读全文]
  • Java面向对象之多态(来源于身边的案例)

    这个案例网上是没有的,属于无忌独创,当时在帮孩子辅导作业,小学科学,里面有一点内容是关于人的牙齿,说牙齿分为:门齿、犬齿、臼齿,问闺女,为什么这么分... [阅读全文]
  • 了解OpenStak基础架构

    了解OpenStak基础架构

    文章目录一、云计算服务模型二、OpenStack概述三、OpenStack优势四、OpenStack架构1、Op... [阅读全文]
  • xml 文件操作

    'XML添加 Public Sub Add(ID As String, RFSerialnumber As String, Mood As ... [阅读全文]
验证码:
Copyright © 2017-2021  萬仟网 保留所有权利. 粤ICP备17035492号-1
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com