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用于深度克隆,避免不必要的错误

import pygame
from pygame.locals import *
import random as rd
import sys
import copy

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

path="resources/"
COUNTDOWN=USEREVENT+1
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方法

class Game:
    def __init__(self):
        pygame.init()
        self.W,self.H=800,800
        self.screen=pygame.display.set_mode((self.W,self.H))
        pygame.display.set_caption("FlappyBird Classic")
        pygame.display.set_icon(pygame.image.load(path+"0.png"))

        self.bg=pygame.transform.smoothscale(pygame.image.load(path+"bg_day.png"),(self.W,self.H))
        self.player=Player()
        self.tubes=[]
        self.tubeWidth=45
        self.counter=0
        self.toUpdate=2
        self.tubeSpeed=2
        self.state="countdown"
        self.countdown=3
        self.score=0
        pygame.time.set_timer(COUNTDOWN,1000)

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

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

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

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

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

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

    def listen(self):
        for event in pygame.event.get():
            if event.type==QUIT:
                sys.exit()
            if event.type==KEYDOWN:
                if event.key==K_SPACE and self.state=="play":
                    self.player.jump()
            if event.type==COUNTDOWN:
                self.countdown-=1
                if self.countdown<=0:
                    self.state="play"
                    pygame.time.set_timer(COUNTDOWN,0)
                    pygame.time.set_timer(NEWTUBE,2200)
            if event.type==NEWTUBE:
                self.newTube()

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

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

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

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

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

    def draw(self):
        self.screen.blit(self.bg,(0,0))
        if self.state=="lose":
            t=self.print_text("simhei",128,str(self.score),(255,0,0))
            tr=t.get_rect()
            tr.center=self.W/2,self.H/2
            self.screen.blit(t,tr)
        if self.state=="play":
            self.player.update()
        self.screen.blit(self.player.image,self.player.rect)
        if self.state=="play":
            self.updateTube()
            for tube in self.tubes:
                x,y=tube[0],tube[1]
                height=tube[2]
                rect=pygame.draw.rect(self.screen,(0,255,0),(x,y,self.tubeWidth,height))
                if self.player.rect.colliderect(rect):
                    self.state="lose"
                    while self.player.rect.top<self.H:
                        self.screen.blit(self.bg,(0,0))
                        self.player.update()
                        self.screen.blit(self.player.image,self.player.rect)
                        pygame.display.update()
                    return
            t=self.print_text("simhei",35,str(self.score),(255,0,0))
            self.screen.blit(t,(25,25))
            self.check()
        if self.state=="countdown":
            t=self.print_text("simhei",72,str(self.countdown),(255,0,0))
            tr=t.get_rect()
            tr.center=self.W/2,self.H/2
            self.screen.blit(t,tr)

检查是否撞到管道

    def check(self):
        if self.player.rect.bottom>=self.H or self.player.rect.top<=0:
            self.state="lose"
            while self.player.rect.top<self.H:
                self.screen.blit(self.bg,(0,0))
                self.player.update()
                self.screen.blit(self.player.image,self.player.rect)
                pygame.display.update()

更新管道位置函数

    def updateTube(self):
        self.counter+=1
        if self.counter>=self.toUpdate:
            for i,tube in enumerate(self.tubes):
                tube[0]-=self.tubeSpeed
            self.counter=0
            for i,tube in enumerate(copy.copy(self.tubes)):
                if tube[0]+self.tubeWidth<=0:
                    self.tubes.pop(i)
                    if tube[1]<1:
                        self.score+=1
                    break

主循环

    def run(self):
        while True:
            self.listen()
            self.draw()
            pygame.display.update()

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

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

创建新管道的方法

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

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

class Player:
    def __init__(self):
        self.images=[pygame.image.load(path+"0.png"),
                     pygame.image.load(path+"1.png"),
                     pygame.image.load(path+"2.png")]
        self.item=0
        self.image=self.images[self.item]
        self.rect=self.image.get_rect()
        try:
            self.rect.center=90,game.H/2
        except NameError:
            self.rect.center=90,800/2
        self.fallSpeed=0
        self.counter=0
        self.toUpdate=4

    def jump(self):
        self.fallSpeed=-10

    def fall(self):
        self.rect.y+=self.fallSpeed
        self.fallSpeed+=1

    def change(self):
        self.item+=1
        if self.item>2:
            self.item=0
        self.image=self.images[self.item]

    def update(self):
        self.counter+=1
        if self.counter>=self.toUpdate:
            self.fall()
            self.change()
            self.counter=0

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


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

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

这里附上最终的代码

import pygame
from pygame.locals import *
import random as rd
import sys
import copy

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

class Game:
    def __init__(self):
        pygame.init()
        self.W,self.H=800,800
        self.screen=pygame.display.set_mode((self.W,self.H))
        pygame.display.set_caption("FlappyBird Classic")
        pygame.display.set_icon(pygame.image.load(path+"0.png"))

        self.bg=pygame.transform.smoothscale(pygame.image.load(path+"bg_day.png"),(self.W,self.H))
        self.player=Player()
        self.tubes=[]
        self.tubeWidth=45
        self.counter=0
        self.toUpdate=2
        self.tubeSpeed=2
        self.state="countdown"
        self.countdown=3
        self.score=0
        pygame.time.set_timer(COUNTDOWN,1000)
    
    def listen(self):
        for event in pygame.event.get():
            if event.type==QUIT:
                sys.exit()
            if event.type==KEYDOWN:
                if event.key==K_SPACE and self.state=="play":
                    self.player.jump()
            if event.type==COUNTDOWN:
                self.countdown-=1
                if self.countdown<=0:
                    self.state="play"
                    pygame.time.set_timer(COUNTDOWN,0)
                    pygame.time.set_timer(NEWTUBE,2200)
            if event.type==NEWTUBE:
                self.newTube()

    def draw(self):
        self.screen.blit(self.bg,(0,0))
        if self.state=="lose":
            t=self.print_text("simhei",128,str(self.score),(255,0,0))
            tr=t.get_rect()
            tr.center=self.W/2,self.H/2
            self.screen.blit(t,tr)
        if self.state=="play":
            self.player.update()
        self.screen.blit(self.player.image,self.player.rect)
        if self.state=="play":
            self.updateTube()
            for tube in self.tubes:
                x,y=tube[0],tube[1]
                height=tube[2]
                rect=pygame.draw.rect(self.screen,(0,255,0),(x,y,self.tubeWidth,height))
                if self.player.rect.colliderect(rect):
                    self.state="lose"
                    while self.player.rect.top<self.H:
                        self.screen.blit(self.bg,(0,0))
                        self.player.update()
                        self.screen.blit(self.player.image,self.player.rect)
                        pygame.display.update()
                    return
            t=self.print_text("simhei",35,str(self.score),(255,0,0))
            self.screen.blit(t,(25,25))
            self.check()
        if self.state=="countdown":
            t=self.print_text("simhei",72,str(self.countdown),(255,0,0))
            tr=t.get_rect()
            tr.center=self.W/2,self.H/2
            self.screen.blit(t,tr)

    def check(self):
        if self.player.rect.bottom>=self.H or self.player.rect.top<=0:
            self.state="lose"
            while self.player.rect.top<self.H:
                self.screen.blit(self.bg,(0,0))
                self.player.update()
                self.screen.blit(self.player.image,self.player.rect)
                pygame.display.update()

    def updateTube(self):
        self.counter+=1
        if self.counter>=self.toUpdate:
            for i,tube in enumerate(self.tubes):
                tube[0]-=self.tubeSpeed
            self.counter=0
            for i,tube in enumerate(copy.copy(self.tubes)):
                if tube[0]+self.tubeWidth<=0:
                    self.tubes.pop(i)
                    if tube[1]<1:
                        self.score+=1
                    break

    def run(self):
        while True:
            self.listen()
            self.draw()
            pygame.display.update()

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

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

class Player:
    def __init__(self):
        self.images=[pygame.image.load(path+"0.png"),
                     pygame.image.load(path+"1.png"),
                     pygame.image.load(path+"2.png")]
        self.item=0
        self.image=self.images[self.item]
        self.rect=self.image.get_rect()
        try:
            self.rect.center=90,game.H/2
        except NameError:
            self.rect.center=90,800/2
        self.fallSpeed=0
        self.counter=0
        self.toUpdate=4

    def jump(self):
        self.fallSpeed=-10

    def fall(self):
        self.rect.y+=self.fallSpeed
        self.fallSpeed+=1

    def change(self):
        self.item+=1
        if self.item>2:
            self.item=0
        self.image=self.images[self.item]

    def update(self):
        self.counter+=1
        if self.counter>=self.toUpdate:
            self.fall()
            self.change()
            self.counter=0

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

喜欢的话就点赞关注吧!

  • 5
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值