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
检查是否撞到管道
def check(self):
if self.player.rect.bottom>=self.H or self.player.rect.top<=0:
self.state="lose"
while self.player.rect.top
更新管道位置函数
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 or self.player.rect.top<=0:
self.state="lose"
while self.player.rect.top=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()
喜欢的话就点赞关注吧!