利用python和pygame模块实现飞机大战
背景提要
我学习Python三天了,基础的语法和面向对象已经有了初步的了解,下一步急需一个实践项目来检验学习的成果,因此选择了很多人做过的飞机大战项目,这个项目我最终做完只写了200行不到的代码,在入门中算是比较轻松的了。
软硬件版本
win10
pycharm pro 2018 win版本
项目流程
初步框架准备
首先导入pygame模块,如果你还没有安装,可以在网上查一下如何安装
然后写出基本的main函数框架
import pygame
if __name__ == '__main__':
# 创建游戏对象
#TODO
# 开始游戏
#TODO
数字常量化
在导入模块后,添加一些全局变量,去替换之后要用到的一些魔法数字
# 屏幕大小的常量
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
# 刷新的帧率
UPDATE_FPS = 60
# 创建敌人飞机的事件
CREATE_ENEMY_EVENT = pygame.USEREVENT
游戏对象的框架
python的完全面向对象的语言,因此游戏本身也是一个对象,因此整个main函数中,先创建游戏对象,再调用该对象的开始游戏方法即可
首先定义一个游戏类,继承自祖先类,然后再明确初始化过程要做什么工作
重写父类的初始化方法,按照套路,先初始化python的所有模块,然后再把游戏窗口给整出来
# 定义一个游戏类
class GameClass(object):
# 游戏的初始化
def __init__(self):
pygame.init() # 初始化所有的 pygame 模块
self.__screen_init() # 窗口初始化
# 游戏窗口初始化
def __screen_init(self):
# 创建游戏窗口
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
pygame.display.set_caption("飞机大战")
值得一提的是,创建游戏窗口的时候,需要传入一个窗口的长*宽,它是一个元祖对象
另外就是可以设置窗口的名字“飞机大战”
实现窗口初始化函数后,接下来就是如何调用,让程序运行时生成一个游戏界面
再写一个开始游戏函数,这个函数中,用while True做游戏循环,保证程序不会运行完就关闭
# 启动游戏
def start_game(self):
while True:
pass
这样一个初步的框架就已经实现了,我们先看一下效果,运行后效果如下图,windows会提示游戏界面没有响应,要不要关闭,这是正常的,在pycharm的右上角点击红色的停止方块即可
背景的绘制
游戏界面已经有了,接下来需要我们把背景图片放进去
在一般的其它的游戏中,背景一般不是一成不变的,因此在这里,我们用图片的滚动,来实现即使飞机不动,你也会感觉一直往前飞的效果
实现的原理是用两张图片,在y轴上贴合到一起,组成一张长图片,在游戏开始的时候,一张图片完全与游戏框重合,另外一张在游戏框上面,当图片持续下移,最终有一张图片完全跑到游戏框下面的时候,我们立马把它放到游戏框的上方,这样用户就无法分辨其实背景其实是两张图片一直滚动播放了
整个游戏中的要显示的对象,有以下几种,背景、英雄飞机、敌人飞机、英雄子弹,(暂时没有做敌人子弹,因此实现起来稍微复杂一点) 他们的共同特征是,能移动有速度,需要加载图片,因此,先做一个父类,然后根据需求的不同,派生出不同的子类,再去创建背景、飞机对象
该类的代码如下,继承自pygame提供的精灵类,然后一定要先调用父类的init方法,然后加载指定路径的图片,通过get_rect函数,我们可以从加载的图片中获取它的尺寸数据
然后需要指定该对象的速度(默认值设置为1),然后实现它的更新方法,就是在y轴上,根据速度值,改变它的y坐标
# 定义一个游戏精灵类,继承自模块的精灵类
class GameSprite(pygame.sprite.Sprite):
# 重写init方法
def __init__(self, image_path, speed=1):
super().__init__() # 先调用父类的方法
self.image = pygame.image.load(image_path) # 加载指定路径的图片
self.rect = self.image.get_rect() # 获取图片的尺寸
self.speed = speed # 指定精灵对象的速度
# 更新方法
def update(self):
self.rect.y += self.speed # 每次更新时,精灵对象在y方向移动
接下来就要去实现一个背景类,该类负责实现背景的交替显示
先重写父类GameSprite的init方法,然后调用父类的方法,加载背景图片,然后根据是第几张图片,判断是放在游戏框之中,还是之上,然后重写更新方法,当背景对象的y值超过范围,也就是图片向下移动到场外时,需要及时移动到游戏画面最上面
# 定义一个背景类,继承自精灵类
class BackGround(GameSprite):
def __init__(self, isAlt = False):
# 调用父类方法完成加载图片、尺寸获取、速度指定
super().__init__("./image/background.png")
# 判断是不是第二张图片
if isAlt:
self.rect.y = -self.rect.height
# 通过两张图片的滚动播放实现动画效果
def update(self):
super().update()
if self.rect.y >= SCREEN_RECT.height: # y值移动到边界时
self.rect.y = -SCREEN_RECT.height # 让该图片重新回到游戏界面上方
实现了这两个类之后,就要在游戏初始化的方法中,实现背景初始化了
由于继承自pygame的精灵类,所以无论背景、还是飞机,都属于精灵类,因此,需要一个初始化函数,去显示这些精灵
在创建背景精灵后,将这两个背景精灵添加进背景精灵组,通过精灵组,可以对组内的对象统一操作
# 创建初始时的精灵
def __create_sprites(self):
# 创建背景精灵
self.bg1 = BackGround(False)
self.bg2 = BackGround(True)
# 创建精灵组
self.back_group = pygame.sprite.Group(self.bg1, self.bg2)
由于需要不断的滚动,因此还要实现更新位置的方法,通过精灵组,调用两个方法,即可实现位置的更新
# 更新精灵
def __update_sprites(self):
# 更新坐标
self.back_group.update()
# 绘制
self.back_group.draw(self.screen)
位置更新后,还要调用update方法,才能显示出来
# 启动游戏
def start_game(self):
while True:
# 更新位置
self.__update_sprites()
# 更新显示
pygame.display.update()
最终的代码如下:
import pygame
# 定义一个游戏精灵类,继承自模块的精灵类
class GameSprite(pygame.sprite.Sprite):
# 重写init方法
def __init__(self, image_path, speed=1):
super().__init__() # 先调用父类的方法
self.image = pygame.image.load(image_path) # 加载指定路径的图片
self.rect = self.image.get_rect() # 获取图片的尺寸
self.speed = speed # 指定精灵对象的速度
# 更新方法
def update(self):
self.rect.y += self.speed # 每次更新时,精灵对象在y方向移动
# 定义一个背景类,继承自精灵类
class BackGround(GameSprite):
def __init__(self, isAlt = False):
# 调用父类方法完成加载图片、尺寸获取、速度指定
super().__init__("./image/background.png")
# 判断是不是第二张图片
if isAlt:
self.rect.y = -self.rect.height
# 通过两张图片的滚动播放实现动画效果
def update(self):
super().update()
if self.rect.y >= SCREEN_RECT.height: # y值移动到边界时
self.rect.y = -SCREEN_RECT.height # 让该图片重新回到游戏界面上方
# 定义一个游戏类
class GameClass(object):
# 游戏的初始化
def __init__(self):
pygame.init() # 初始化所有的 pygame