欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python像素鸟游戏

程序员文章站 2024-03-18 14:06:28
...

1、引言

本次论文主要是基于对像素鸟游戏的研究。
像素鸟是一款风靡一时的小游戏,这款游戏架构简单,可以通过不同的编程语言去实现。近段时间学了python这门编程语言。于是突发奇想用python去实现这款游戏。
该程序主要实现的功能有:双击运行程序,播放背景音乐。出现游戏界面,通过不断按下键盘上面的 ↑ 键,使得小鸟飞行。每次按下 ↑ 键,就会播放小鸟翅膀挥舞的声音。通过小鸟的飞行,越过障碍物(管子),可以得到分数。每越过一个障碍物就可以得到一分。如果小鸟与障碍物发生碰撞,或者小鸟飞出游戏界面,那么就会播放gameover的声音,同时停止游戏,出现游戏结束界面。在游戏界面界面,你会看到历史最高分和你的分数,如果你的分数高于历史最高分,将会被记录在record.txt文件中。同时,在游戏结束界面有两个图片按钮,你可以通过点击鼠标左键来选择重新开始游戏或者是结束游戏。

2、系统结构

2.1 总体结构

python像素鸟游戏
本程序主要分成了3个模块,main模块是主函数模块,Bird模块是小鸟对象模块继承了(sprite),Tubing模块是管子对象模块也继承了(sprite)

2.2 局部结构

2.2.1 main模块结构

python像素鸟游戏
main模块中主要实现加载背景图片,加载游戏中需要的音乐。判断小鸟精灵和管子组精灵是否发生碰撞,实现小鸟按键飞行,播放音乐。绘制游戏结束后的界面以及按键事件、鼠标点击事件、与退出事件。

2.2.2 Bird模块结构

python像素鸟游戏
Bird模块主要实现关于小鸟对象的图片加载(__init__函数),小鸟显示的位置(__init__函数),小鸟的飞翔(fly函数),小鸟的下降(drop函数)

2.2.3 Tubing模块结构

python像素鸟游戏
Tubing模块主要实现了上面的管子(Tubing_up类)和下面的管子(Tubing_down类)的图片加载(__init__函数)、管子显示的位置(__init__函数)、管子的移动(move函数)

3、代码实现

先导入pygame模块,初始化pygame

import pygame
pygame.init()

创建游戏窗口,设置游戏窗口标题

bg_size = width,height = 903,495
view = pygame.display.set_mode(bg_size)
pygame.display.set_caption("像素鸟小游戏")

加载背景图片、背景音乐、绘制背景图片在游戏窗口上显示、播放背景音乐

import sys

#初始化pygame的扩音器
pygame.mixer.init()

#加载背景音乐
pygame.mixer.music.load("music/主界面BGM.mp3")

#加载背景图片
background = pygame.image.load("image/BGM.png").convert()

def main():

    #finish定义游戏是否结束(结束则为True)
    finish = False

    #开始循环播放背景音乐
    pygame.mixer.music.play(-1)
    while True:
        
        #如果点击了关闭事件就退出程序
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        #如果游戏还没结束
        if finish == False:

            #绘制背景图片
            view.blit(background,(0,0))

        #如果游戏已经结束,停止背景音乐的播放,绘制游戏结束后的界面
        if finish == True:
            pygame.mixer.music.stop()

        #更新界面
        pygame.display.flip()
        #延迟10毫秒
        pygame.time.delay(10)


if __name__ == "__main__":
    main()

新建一个模块实现小鸟对象

import pygame

#继承Sprite精灵类,方便用于碰撞检测
class Bird(pygame.sprite.Sprite):
        def __init__(self,bg_size):
                pygame.sprite.Sprite.__init__(self)

                #加载图片
                self.img1 = pygame.image.load("image/bird1.png").convert_alpha()
                self.img2 = pygame.image.load("image/bird2.png").convert_alpha()
                self.img3 = pygame.image.load("image/bird3.png").convert_alpha()

                #设置小鸟的初始位置为窗口1/4、高度1/2的位置
                self.width, self.height = bg_size[0], bg_size[1]
                self.rect = self.img1.get_rect()
                self.rect.left, self.rect.top = self.width // 4, self.height // 2

                #设置小鸟的初速度、加速度、路程、时间
                self.v = 20
                self.g = 2
                self.s = 0
                self.t = 0.2

        #小鸟向下飞行的方法
        def drop(self):
                self.v = self.v-self.g*self.t
                self.s = self.v*self.t - 0.5*self.g*self.t*self.t
                self.rect.top = self.rect.top-self.s

        #小鸟向上飞行的方法
        def fly(self):
                self.v = 10

在主函数里面绘制小鸟,并实现小鸟的下落和按键后小鸟飞翔,以及小鸟飞翔挥动翅膀的声音

import Bird

#加载小鸟挥动翅膀的声音、小鸟撞击的声音
wings_sound = pygame.mixer.Sound("music/翅膀声.wav")
crash_sound = pygame.mixer.Sound("music/me_down.wav")

def main():
    #设置小鸟飞行挥动翅膀图片的切换flag
    change = 1
    #创建一个小鸟对象
    bird = Bird.Bird(bg_size)

    while True:

        #如果游戏还没结束
        if finish == False:
            
            #如果小鸟飞行超过上边界或者超过下边界,就播放撞击音乐,把finish置为True,表示程序结束
            if bird.rect.top < 0 or bird.rect.bottom > width:
                crash_sound.play()
                finish = True

            #切换小鸟飞行图片,以达到小鸟挥动翅膀的效果
            if change % 3 == 0:
                view.blit(bird.img1,bird.rect)  #绘制小鸟图片
            elif change % 3 == 1:
                view.blit(bird.img2,bird.rect)
            else:
                view.blit(bird.img3,bird.rect)
                
            change = change +1

            #调用小鸟对象的下落方法
            bird.drop()

        #如果键盘上面的↑按键被按下,就调用小鸟对象的向上飞行的方法,同时播放小鸟挥动翅膀的声音
        key_pressed = pygame.key.get_pressed()
        if key_pressed[pygame.K_UP]:
            wings_sound.play()
            bird.fly()
        
        #更新界面
        pygame.display.flip()
        #延迟10毫秒
        pygame.time.delay(10)


if __name__ == "__main__":
    main()

新建一个Tubing模块实现管子对象

import pygame
import random

#检测Sprite精灵类,方便用于碰撞检测
class Tubing_down(pygame.sprite.Sprite):
    def __init__(self):
    	pygame.sprite.Sprite.__init__(self)

        #加载图片,设置下面管子的显示位置
    	self.pipe = pygame.image.load("image/管子.png").convert_alpha()
    	self.rect = self.pipe.get_rect()
    	self.rect.left = 903
    	self.rect.top = random.randint(120,495-40)

    #管子的移动方法
    def move(self):
    	self.rect.left = self.rect.left - 1


class Tubing_up(pygame.sprite.Sprite):
	def __init__(self,down_pipe):
                #加载图片,设置下面管子的显示位置,根据下面的管子来设置上面管子显示的位置,预留一些空间给小鸟飞行
		self.pipe = pygame.image.load("image/管子.png").convert_alpha()
		self.rect = self.pipe.get_rect()
		self.rect.left = 903
		self.rect.top = -(640-down_pipe.rect.top+120)

        #管子的移动方法
	def move(self):
		self.rect.left = self.rect.left - 1

在main模块里面实现管子的产生、绘制以及小鸟和管子发生碰撞的一系列事件

import Tubing 
from pygame.locals import *

#定义存放管子的数组,tubeUp_array代表存放上面的那条管子,tubeDown_array代表存放下面的那条管子
tubeUp_array = []
tubeDown_array = []

def main():
    #设置开始时间,用于每隔3秒就产生一对管子
    start_time = 0

    while True:

        #如果游戏还没结束
        if finish == False:

            #每3秒产生一对管子,一上一下,分别存放在ubeUp_array、tubeDown_array列表中
            end_time = pygame.time.get_ticks()
            if end_time - start_time >= 3000:
                tubeDown = Tubing.Tubing_down()
                tubeUp = Tubing.Tubing_up(tubeDown)
                tubeDown_array.append(tubeDown)
                tubeUp_array.append(tubeUp)
                start_time = end_time

            #判断小鸟精灵和管子精灵组有没有发生碰撞,如果发生了碰撞就播放碰撞音乐,同时将finish置为True
            Up_crash = pygame.sprite.spritecollide(bird, tubeUp_array, False)
            Down_crash = pygame.sprite.spritecollide(bird, tubeDown_array, False)
            if Up_crash or Down_crash:
                crash_sound.play()
                finish = True             

            #显示管子和移动管子
            for i in range(len(tubeDown_array)):
                view.blit(tubeDown_array[i].pipe,tubeDown_array[i].rect)
                view.blit(tubeUp_array[i].pipe,tubeUp_array[i].rect)
                tubeDown_array[i].move()
                tubeUp_array[i].move()

if __name__ == "__main__":
    main()

实现小鸟飞过一对管子就分数+1,同时把分数绘制显示出来

#设置分数显示的字体、
font = pygame.font.Font("font/font.ttf",35)

def main():
    #设置游戏的分数
score = 0

    while True:
        

        #如果游戏还没结束
        if finish == False:
            

            #小鸟每飞过一对管子分数就加1,显示管子和移动管子
            for i in range(len(tubeDown_array)):
                if bird.rect.right == tubeUp_array[i].rect.right:
                    score = score +1 
          
            #绘制分数
            font_view = font.render('score = %s' %str(score),True,(255,255,255))  
            view.blit(font_view,(10,5)) 

        
        #更新界面
        pygame.display.flip()
        #延迟10毫秒
        pygame.time.delay(10)


if __name__ == "__main__":
    main()

绘制游戏结束后的界面

from pygame.locals import *

#设置游戏结束之后显示最高分数等字的字体
gameover_font = pygame.font.Font("font/font.ttf", 48)

#加载游戏结束后重新开始按键图片、游戏结束后退出游戏按键图片
again_image = pygame.image.load("image/again.png").convert_alpha()
gameover_image = pygame.image.load("image/gameover.png").convert_alpha()

def main():

    #reader用于判断record.txt(存储最高分)的文件是否已经被打开(已经打开为True)
    reader = False

    #获取重新开始按键的方框、获取结束游戏按键的方框
    again_rect = again_image.get_rect()
    gameover_rect = gameover_image.get_rect()

    while True:
        
        #如果游戏已经结束,停止背景音乐的播放,绘制游戏结束后的界面
        if finish == True:

            #如果record.txt文件还未打开过,打开record.txt文件,将reader 置为 True
            if not reader:
                reader = True
                # 读取历史最高分
                with open("record.txt", "r") as f:
                    record_score = int(f.read())

                #判断当前分数是否高于历史最高分,如果是,则写入record.txt文件中,同时将record_score赋值为最高分
                if score > record_score:
                    with open("record.txt", "w") as f:
                        record_score = score
                        f.write(str(score))

            #绘制游戏结束后的历史最高分
            record_score_text = font.render("Best: %d" % record_score, True, (255,255,255))
            view.blit(record_score_text, (50, 50))

            #绘制游戏结束后的我的分数
            gameover_text1 = gameover_font.render("Your Score: ", True, (255,255,255))
            gameover_text1_rect = gameover_text1.get_rect()
            gameover_text1_rect.left, gameover_text1_rect.top = \
                (width - gameover_text1_rect.width) // 2, height // 3
            view.blit(gameover_text1, gameover_text1_rect)

            gameover_text2 = gameover_font.render(str(score), True, (255,255,255))
            gameover_text2_rect = gameover_text2.get_rect()
            gameover_text2_rect.left, gameover_text2_rect.top = \
                (width - gameover_text2_rect.width) // 2, \
                gameover_text1_rect.bottom + 10
            view.blit(gameover_text2, gameover_text2_rect)

            #绘制重新开始按钮
            again_rect.left, again_rect.top = \
                (width - again_rect.width) // 2, \
                gameover_text2_rect.bottom + 50
            view.blit(again_image, again_rect)

            #绘制结束游戏按钮
            gameover_rect.left, gameover_rect.top = \
                (width - again_rect.width) // 2, \
                again_rect.bottom + 6
            view.blit(gameover_image, gameover_rect)
            
            # 检测用户的鼠标操作
            # 如果用户按下鼠标左键
        if pygame.mouse.get_pressed()[0]:
            pos = pygame.mouse.get_pos()
            if again_rect.left < pos[0] < again_rect.right and \
                    again_rect.top < pos[1] < again_rect.bottom:
                tubeUp_array.clear()
                tubeDown_array.clear()
                main()
            elif gameover_rect.left < pos[0] < gameover_rect.right and \
                    gameover_rect.top < pos[1] < gameover_rect.bottom:
                pygame.quit()
                sys.exit()

        #更新界面
        pygame.display.flip()
        #延迟10毫秒
        pygame.time.delay(10)


if __name__ == "__main__":
    main()

main模块的总体集合代码如下

import pygame
import sys
import traceback
import Tubing 
import random
import Bird
from pygame.locals import *

#初始化pygame 初始化pygame的扩音器
pygame.init()
pygame.mixer.init()

#定义存放管子的数组,tubeUp_array代表存放上面的那条管子,tubeDown_array代表存放下面的那条管子
tubeUp_array = []
tubeDown_array = []

#定义窗口的大小,也是背景图片的大小
bg_size = width,height = 903,495

#加载背景音乐、小鸟挥动翅膀的声音、小鸟撞击的声音
pygame.mixer.music.load("music/主界面BGM.mp3")
wings_sound = pygame.mixer.Sound("music/翅膀声.wav")
crash_sound = pygame.mixer.Sound("music/me_down.wav")

#设置分数显示的字体、游戏结束之后显示最高分数等字的字体
font = pygame.font.Font("font/font.ttf",35)
gameover_font = pygame.font.Font("font/font.ttf", 48)

#设置游戏窗口的大小和标题
view = pygame.display.set_mode(bg_size)
pygame.display.set_caption("像素鸟小游戏")

#加载背景图片、游戏结束后重新开始按键图片、游戏结束后退出游戏按键图片
background = pygame.image.load("image/BGM.png").convert()
again_image = pygame.image.load("image/again.png").convert_alpha()
gameover_image = pygame.image.load("image/gameover.png").convert_alpha()

def main():
    #设置开始时间,用于每隔3秒就产生一对管子
    start_time = 0
    #设置游戏的分数
    score = 0
    #设置小鸟飞行挥动翅膀图片的切换flag
    change = 1

    #finish定义游戏是否结束(结束则为True),reader用于判断record.txt(存储最高分)的文件是否已经被打开(已经打开为True)
    finish = False
    reader = False

    #获取重新开始按键的方框、获取结束游戏按键的方框
    again_rect = again_image.get_rect()
    gameover_rect = gameover_image.get_rect()

    #创建一个小鸟对象
    bird = Bird.Bird(bg_size)

    #开始循环播放背景音乐
    pygame.mixer.music.play(-1)
    while True:
        
        #如果点击了关闭事件就退出程序
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        #如果游戏还没结束
        if finish == False:
            
            #如果小鸟飞行超过上边界或者超过下边界,就播放撞击音乐,把finish置为True,表示程序结束
            if bird.rect.top < 0 or bird.rect.bottom > width:
                crash_sound.play()
                finish = True

            #绘制背景图片
            view.blit(background,(0,0))

            #切换小鸟飞行图片,以达到小鸟挥动翅膀的效果
            if change % 3 == 0:
                view.blit(bird.img1,bird.rect)  #绘制小鸟图片
            elif change % 3 == 1:
                view.blit(bird.img2,bird.rect)
            else:
                view.blit(bird.img3,bird.rect)
                
            change = change +1

            #每3秒产生一对管子,一上一下,分别存放在ubeUp_array、tubeDown_array列表中
            end_time = pygame.time.get_ticks()
            if end_time - start_time >= 3000:
                tubeDown = Tubing.Tubing_down()
                tubeUp = Tubing.Tubing_up(tubeDown)
                tubeDown_array.append(tubeDown)
                tubeUp_array.append(tubeUp)
                start_time = end_time

            #判断小鸟精灵和管子精灵组有没有发生碰撞,如果发生了碰撞就播放碰撞音乐,同时将finish置为True
            Up_crash = pygame.sprite.spritecollide(bird, tubeUp_array, False)
            Down_crash = pygame.sprite.spritecollide(bird, tubeDown_array, False)
            if Up_crash or Down_crash:
                crash_sound.play()
                finish = True             

            #小鸟每飞过一对管子分数就加1,显示管子和移动管子
            for i in range(len(tubeDown_array)):
                if bird.rect.right == tubeUp_array[i].rect.right:
                    score = score +1 
                view.blit(tubeDown_array[i].pipe,tubeDown_array[i].rect)
                view.blit(tubeUp_array[i].pipe,tubeUp_array[i].rect)
                tubeDown_array[i].move()
                tubeUp_array[i].move()
          
            #绘制分数
            font_view = font.render('score = %s' %str(score),True,(255,255,255))  
            view.blit(font_view,(10,5)) 

            #调用小鸟对象的下落方法
            bird.drop()

        #如果游戏已经结束,停止背景音乐的播放,绘制游戏结束后的界面
        if finish == True:
            pygame.mixer.music.stop()

            #如果record.txt文件还未打开过,打开record.txt文件,将reader 置为 True
            if not reader:
                reader = True
                # 读取历史最高分
                with open("record.txt", "r") as f:
                    record_score = int(f.read())

                #判断当前分数是否高于历史最高分,如果是,则写入record.txt文件中,同时将record_score赋值为最高分
                if score > record_score:
                    with open("record.txt", "w") as f:
                        record_score = score
                        f.write(str(score))

            #绘制游戏结束后的历史最高分
            record_score_text = font.render("Best: %d" % record_score, True, (255,255,255))
            view.blit(record_score_text, (50, 50))

            #绘制游戏结束后的我的分数
            gameover_text1 = gameover_font.render("Your Score: ", True, (255,255,255))
            gameover_text1_rect = gameover_text1.get_rect()
            gameover_text1_rect.left, gameover_text1_rect.top = \
                (width - gameover_text1_rect.width) // 2, height // 3
            view.blit(gameover_text1, gameover_text1_rect)

            gameover_text2 = gameover_font.render(str(score), True, (255,255,255))
            gameover_text2_rect = gameover_text2.get_rect()
            gameover_text2_rect.left, gameover_text2_rect.top = \
                (width - gameover_text2_rect.width) // 2, \
                gameover_text1_rect.bottom + 10
            view.blit(gameover_text2, gameover_text2_rect)

            #绘制重新开始按钮
            again_rect.left, again_rect.top = \
                (width - again_rect.width) // 2, \
                gameover_text2_rect.bottom + 50
            view.blit(again_image, again_rect)

            #绘制结束游戏按钮
            gameover_rect.left, gameover_rect.top = \
                (width - again_rect.width) // 2, \
                again_rect.bottom + 6
            view.blit(gameover_image, gameover_rect)
            # 检测用户的鼠标操作
            # 如果用户按下鼠标左键
        if pygame.mouse.get_pressed()[0]:
            pos = pygame.mouse.get_pos()
            if again_rect.left < pos[0] < again_rect.right and \
                    again_rect.top < pos[1] < again_rect.bottom:
                tubeUp_array.clear()
                tubeDown_array.clear()
                main()
            elif gameover_rect.left < pos[0] < gameover_rect.right and \
                    gameover_rect.top < pos[1] < gameover_rect.bottom:
                pygame.quit()
                sys.exit()

        #如果键盘上面的↑按键被按下,就调用小鸟对象的向上飞行的方法,同时播放小鸟挥动翅膀的声音
        key_pressed = pygame.key.get_pressed()
        if key_pressed[pygame.K_UP]:
            wings_sound.play()
            bird.fly()
        
        #更新界面
        pygame.display.flip()
        #延迟10毫秒
        pygame.time.delay(10)


if __name__ == "__main__":
    main()

4、实验

实验运行结果如下:
python像素鸟游戏
python像素鸟游戏

5、总结和展望

不足之处:
1、没有实现小鸟向上飞行的时候,头朝上。往下坠落的时候,头往下
参考文献:
[1] 小甲鱼的飞机大战视频