xenli 发表于 2019-12-25 16:43:50

使用Pygame模块制作Flappy Bird游戏

本帖最后由 xenli 于 2019-12-25 16:46 编辑

源代码在二楼
1、游戏简介
      Flappy Bird 是一款鸟类 飞行游戏,由越南河内独立游戏开发者阮哈东(Dong Nguyen)开发。在      Flappy Bird这款游戏中,玩家只需要用一根手指来操控,单击触摸手机屏幕,小鸟就会往上飞。不断地单击屏幕,小鸟就会不断地往高处飞;放松手指,则会快速下降。玩家要控制小鸟一直向前飞行,并且注意躲避途中高低不平的管子。如果小鸟碰到了障碍物,游戏就会结束。每当小鸟飞过一组管道,玩家就会获得1分。

成品效果


2、游戏分析
      在Flappy Bird游戏中,主要有两个对象:小鸟和管道。可以创建Bird类和Pineline类来分别表示这两个对象。小鸟可以通过上下移动来躲避管道,所以在Bird类中创建一个birdUpdate()方法,实现小鸟的上下移动。为了体现小鸟向前飞行的特征,可以让管道一直向左侧移动,这样在窗口中就好像小鸟在向前飞行。所以,在Pineline类中也创建一个updatePipeline()方法,实现管道的向左移动。此外,还创建了3个函数:createMap()函数用于绘制地图;checkDead()函数用于判断小鸟的生命状态;getResult()函数用于获取最终分数。最后在主逻辑中,实例化类并调用相关方法,实现相应功能。

3、搭建主框架
      通过前面的分析,我们可以搭建起Flappy Bird游戏的主框架。Flappy Bird游戏有两个对象:小鸟和管道。先来创建这两个类,类中具体的方法可以先使用pass语句代替。然后创建一个绘制地图的函数createMap()。最后,在主逻辑中绘制背景图片。关键代码如下:
# -*- coding:utf-8 -*-
import pygame
import sys
import random
import os

class Bird(object):
      '''定义一个鸟类'''
      def __init__(self):
                '''定义初始化方法'''
                pass

      def birUpdate(self):
                pass

class Pipeline(object):
      '''定义一个管道类'''
      def __init__(self):
                '''定义初始化方法'''
                pass

      def updatePipeline(self):
                '''水平移动'''
                pass

def createMap():
      '''定义创建地图的方法'''
      screen.fill((255,255,255))                        #填充颜色
      screen.blit(background,(0,0))                #填入到背景
      pygame.display.update()                              #更新显示

if __name__ == '__main__':
      pygame.init()                                                      #初始化pygame
      os.environ["SDL_VIDEO_WINDOW_POS"] = "%d, %d" % (800, 200)
      size = width,height = 400,650                        #设置窗口
      screen = pygame.display.set_mode(size)      #显示窗口
      clock = pygame.time.Clock()                              #设置时钟
      Pipeline = Pipeline()                                        #实例化管道类
      Bird = Bird()                                                      #实例化鸟类
      while True:
                clock.tick(50)                                                #每秒执行50次
                # 轮询事件
                for event in pygame.event.get():
                        if event.type == pygame.QUIT:
                              sys.exit()

                background = pygame.image.load('assets/background.png')      #加载背景图片
                createMap()                                                      #绘制地图
      pygame.quit()                                                      #退出
运行效果如下图:


4、创建小鸟类
      下面来创建小鸟类。该类需要初始化很多参数,所以定义一个__init__() 方法,用来初始化各种参数,包括鸟飞行的几种状态、飞行的速度、跳跃的高度等。然后定义birdUpdate()方法,该方法用于实现小鸟的跳跃和坠落。接下来,在主逻辑的轮询事件中添加键盘按下事件或鼠标单击事件,如按下鼠标,使小鸟上升等。最后,在createMap()方法中,显示小鸟的图像。关键代码如下:
# -*- coding:utf-8 -*-
import pygame
import sys
import random
import os

class Bird(object):
      '''定义一个鸟类'''
      def __init__(self):
                '''定义初始化方法'''
                self.birdRect = pygame.Rect(65,50,50,50)      #鸟的矩形
                # 定义鸟的3种状态列表
                self.birdStatus = [pygame.image.load('assets/1.png'),
                                                      pygame.image.load('assets/2.png'),
                                                      pygame.image.load('assets/dead.png')]
                self.status = 0                        #默认飞行状态
                self.birdX = 120                #鸟所在的X轴坐标,即是向右飞行的速度
                self.birdY = 350                #鸟所在的Y轴坐标,即是上下飞行的高度
                self.jump = False                #默认情况小鸟自动降落
                self.jumpSpeed = 10          #跳跃高度
                self.gravity = 5               #重力
                self.dead = False                #默认小鸟生命状态为活着

      def birUpdate(self):
                if self.jump:
                        #小鸟跳跃
                        self.jumpSpeed -= 1      #速度递减,上升越来越慢
                        self.birdY -= self.jumpSpeed      #鸟的Y轴坐标减少,小鸟上升
                else:
                        #小鸟坠落
                        self.gravity += 0.2                                 #重力递增,下降越来越快
                        self.birdY += self.gravity                #鸟的Y轴坐标增加,小鸟下降
                self.birdRect = self.birdY                #更改Y轴位置

class Pipeline(object):
      '''定义一个管道类'''
      def __init__(self):
                '''定义初始化方法'''
                pass

      def updatePipeline(self):
                '''水平移动'''
                pass

def createMap():
      '''定义创建地图的方法'''
      screen.fill((255,255,255))                        #填充颜色
      screen.blit(background,(0,0))                #填入到背景
      #显示小鸟
      if Bird.dead:                                                #撞管道状态
                Bird.status = 2
      elif Bird.jump:                                                #起飞状态
                Bird.status = 1
      screen.blit(Bird.birdStatus,(Bird.birdX,Bird.birdY))      #设置小鸟的坐标
      Bird.birUpdate()                                        #鸟移动
      pygame.display.update()                              #更新显示

if __name__ == '__main__':
      '''主程序'''
      pygame.init()                                                      #初始化pygame
      os.environ["SDL_VIDEO_WINDOW_POS"] = "%d, %d" % (800, 200)
      size = width,height = 400,650                        #设置窗口
      screen = pygame.display.set_mode(size)      #显示窗口
      clock = pygame.time.Clock()                              #设置时钟
      Pipeline = Pipeline()                                        #实例化管道类
      Bird = Bird()                                                      #实例化鸟类
      while True:
                clock.tick(50)                                                #每秒执行50次
                # 轮询事件
                for event in pygame.event.get():
                        if event.type == pygame.QUIT:
                              sys.exit()
                        if (event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN) and not Bird.dead:
                              Bird.jump = True                        #跳跃
                              Bird.gravity = 5                              #重力
                              Bird.jumpSpeed = 10                        #跳跃速度

                background = pygame.image.load('assets/background.png')      #加载背景图片
                createMap()                                                      #绘制地图
      pygame.quit()
上述代码在Bird类中设置了birdStatus 属性,该属性是一个鸟类图片的列表,列表中显示鸟类3种飞行状态,根据小鸟的不同状态加载相应的图片。在birdUpdate()方法中,为了达到较好的动画效果,使jumpSpeed 和gravity两个属性逐渐变化。运行上述代码,在窗体内创建一只小鸟, 默认情况小鸟会一直下降。当单击一下鼠标或按一下键盘, 小鸟会跳跃一下,高度上升。运行效果如下图所示。



5、创建管道类
创建完鸟类后,接下来创建管道类。同样,在__init__()方法中初始化各种参数,包括设置管道的坐标,加载上下管道图片等。然后在updatePipeline()方法中,定义管道向左移动的速度,并且当管道移出屏幕时,重新绘制下一组管道。最后,在createMap()函数中显示管道。关键代码如下:
# -*- coding:utf-8 -*-
import pygame
import sys
import random
import os

class Bird(object):
      #省略部分代码……

class Pipeline(object):
      '''定义一个管道类'''
      def __init__(self):
                '''定义初始化方法'''
                self.wallx = 400                        #管道所在X轴坐标
                self.pineUp = pygame.image.load('assets/top.png')                #加载上管道图片
                self.pineDown = pygame.image.load('assets/bottom.png')      #加载下管道图片

      def updatePipeline(self):
                '''管道移动方法'''
                self.wallx -= 5                                 #管道X轴坐标递减,即管道向左移动
                # 当管道运行到一定位置,即小鸟飞跃管道,分数+1,并且重置管道
                if self.wallx < -80:
                        self.wallx = 400

def createMap():
      '''定义创建地图的方法'''
      screen.fill((255,255,255))                        #填充颜色
      screen.blit(background,(0,0))                #填入到背景

      #显示管道
      screen.blit(Pipeline.pineUp,(Pipeline.wallx,-300))      #上管道坐标位置
      screen.blit(Pipeline.pineDown,(Pipeline.wallx,500))      #下管道坐标位置
      Pipeline.updatePipeline()                        #管道移动

      #显示小鸟
      if Bird.dead:                                                #撞管道状态
                Bird.status = 2
      elif Bird.jump:                                                #起飞状态
                Bird.status = 1
      screen.blit(Bird.birdStatus,(Bird.birdX,Bird.birdY))      #设置小鸟的坐标
      Bird.birUpdate()                                        #鸟移动

      pygame.display.update()                              #更新显示

if __name__ == '__main__':
      #省略部分代码……
      while True:
                clock.tick(50)                                                #每秒执行50次
                # 轮询事件
                for event in pygame.event.get():
                        if event.type == pygame.QUIT:
                              sys.exit()
                        if (event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN) and not Bird.dead:
                              Bird.jump = True                        #跳跃
                              Bird.gravity = 5                        #重力
                              Bird.jumpSpeed = 10                        #跳跃速度

                background = pygame.image.load('assets/background.png')      #加载背景图片
                createMap()                                                      #绘制地图
      pygame.quit()                                                      #退出
上述代码中,在createMap()函数内,设置先显示管道,再显示小鸟。这样做的目的是为了当小鸟与管道图像重合时,小鸟的图像显示在上层,而管道的图像显示在底层。运行结果如下图:


6、计算得分
当小鸟飞过管道时,玩家得分+1.这里对于飞过管道的逻辑做了简化处理:当管道移动到窗体左侧一定距离后,默认为小鸟飞过管道,使分数+1,并显示在屏幕上。在updatePipeline()方法中已经实现该功能,关键代码如下:
# -*- coding:utf-8 -*-
import pygame
import sys
import random
import os

class Bird(object):
      #省略部分代码……

class Pipeline(object):
      #省略部分代码……
      def updatePipeline(self):
                '''管道移动方法'''
                self.wallx -= 5                                 #管道X轴坐标递减,即管道向左移动
                # 当管道运行到一定位置,即小鸟飞跃管道,分数+1,并且重置管道
                if self.wallx < -80:
                        global score
                        score += 1
                        self.wallx = 400

def createMap():
      #省略部分代码……
      #显示分数
      screen.blit(font.render('Score:'+str(score),-1,(255,255,255)),(100,50)) #设置颜色及坐标位置
      pygame.display.update()                              #更新显示

if __name__ == '__main__':
      #省略部分代码……
      while True:
      #省略部分代码……
      pygame.quit()                                                      #退出
运行效果如下图:


7、碰撞检测
当小鸟与管道相撞时,小鸟颜色变为灰色,游戏结束,并且显示总分数。在checkDead()函数中通过pygame.Rect()可以分别获取小鸟的矩形区域对象和管道的矩形区域对象,该对象有一 个onlliderect()方法可以判断两个矩形区域是否相撞。如果相撞,设置Bird.dead属性为True。 此外,当小鸟飞出窗体时,也设置Bird.dead属性为True。 最后,用两行文字显示游戏得分。关键代码如下:
# -*- coding:utf-8 -*-
import pygame
import sys
import random
import os

class Bird(object):
      #省略部分代码……

class Pipeline(object):
      #省略部分代码……

def createMap():
      #省略部分代码……
def checkDead():
      #上方管子的矩形位置
      upRect = pygame.Rect(Pipeline.wallx,-300,
                                                 Pipeline.pineUp.get_width() - 10,
                                                 Pipeline.pineUp.get_height())
      #下方管子的矩形位置
      downRect = pygame.Rect(Pipeline.wallx,500,
                                                 Pipeline.pineDown.get_width() - 10,
                                                 Pipeline.pineDown.get_height())
      #检测小鸟与上下方管子是否碰撞
      if upRect.colliderect(Bird.birdRect) or downRect.colliderect(Bird.birdRect):
                Bird.dead = True
      #检测小鸟是否飞出上下边界
      if not 0 < Bird.birdRect < height:
                Bird.dead = True
                return True
      else:
                return False

def getResutl():
      final_text1 = 'Game Over'
      final_text2 = 'Your final score is ' + str(score)
      ft1_font = pygame.font.SysFont('Arial',70)                        #设置第一行文字字体
      ft1_surf = font.render(final_text1,1,(242,3,36))      #设置第一行文字颜色
      ft2_font = pygame.font.SysFont('Arial',50)                        #设置第二行文字字体
      ft2_surf = font.render(final_text2,1,(253,177,6))      #设置第二行文字颜色      
      #设置第一行文字显示位置
      screen.blit(ft1_surf,)
      #设置第二行文字显示位置
      screen.blit(ft2_surf,)      
      pygame.display.flip()                        #更新整个待显示的Surface对象到屏幕上

if __name__ == '__main__':
      #省略部分代码……
      while True:
      #省略部分代码……
                background = pygame.image.load('assets/background.png')      #加载背景图片
                if checkDead():                                                #检测小鸟生命状态
                        getResutl()                                                #如果小鸟死亡,显示游戏总分
                else:
                        createMap()                                                #绘制地图      
      pygame.quit()                                                      #退出

上述代码的checkDead()方法中,upRect.colliderect(Bird.birdRect)用于检测小鸟的矩形区域是否与上面的管道的矩形区域相撞, colliderect()函数的参数是另一个矩形区域对象。

说明:本实例已经实现了Flappy Bird游戏的基本功能,但还有很多需要完善的地方,如设置游戏的难度,包括设置管道的高度、小鸟的飞行速度等,各位鱼油可以尝试完善该游戏。

xenli 发表于 2019-12-25 17:00:44

源代码回复可见
**** Hidden Message *****

9946685 发表于 2019-12-25 18:24:21

看看学习

lkllkl 发表于 2020-2-12 11:17:31

学习一下

123飞龙在天321 发表于 2020-3-15 10:30:37

学习

123飞龙在天321 发表于 2020-3-16 16:56:26

本帖最后由 123飞龙在天321 于 2020-3-16 16:58 编辑

楼主做的还不错{:10_256:}

我就是猛辣 发表于 2020-3-17 19:06:58

学习

凌绝顶 发表于 2020-4-11 17:36:22

谢谢大佬

hax1994 发表于 2020-4-11 20:26:21

1

JonasPP 发表于 2020-4-13 17:38:02

学习一下

一年山河成 发表于 2020-4-13 18:12:32

学习学习

SeanNate 发表于 2020-4-13 18:16:04

学习学习

昆山碎玉 发表于 2020-4-14 18:58:42

学习

一夜湘君白发多 发表于 2020-4-14 19:21:53

认真学习

同顺街一霸 发表于 2020-4-15 21:43:58

6

靳子轩 发表于 2020-6-11 20:55:23

O    O
   O

hanliyi 发表于 2020-6-27 10:09:48

真的好

Qianm 发表于 2020-7-8 09:28:25

{:5_106:}

李浩田 发表于 2020-7-21 18:39:30

谢谢

tcfpy 发表于 2020-7-21 20:19:20

求代码
页: [1] 2
查看完整版本: 使用Pygame模块制作Flappy Bird游戏