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()
喜欢的话就点赞关注吧!