自学Python第十一天- 一些有用的模块:pygame (一)


pygame 是一个适合编写游戏的模块,需要安装后再使用。

安装 pygame

pygame 的安装和其他模块一样,使用 pip install pygame 即可

导入 pygame

和其他模块一样,使用 import pygame 进行导入

初始化 pygame

使用 pygame 时,需先将 pygame 显式初始化,然后才能使用其中的方法等功能(部分函数不需要初始化)

import pygame
pygame.init()

使用 pygame

pygame 经常用于制作游戏,这里以制作微信小游戏“飞机大战”为例研究如何使用 pygame。

游戏资源地址:
提取码:r10d

游戏制作的大致流程及思路

使用 pygame 制作游戏的大致游戏开发步骤为:

  1. 创建主窗口
  2. 绘制各元素
  3. 渲染呈现
  4. 实现动画
  5. 动态监听用户事件
  6. 对元素行为进行处理

游戏制作第一步:创建窗口、加载图形并绘制元素、渲染呈现

创建主窗口

pygame 使用 pygame.display 来创建和管理窗口,其使用方法为:
pygame.display.set_mode(resolution=(0,0),flags=0,depth=0) -> Surface

  • resolution :指定屏幕的宽和高,默认和屏幕大小一致
  • flags :指定屏幕的附加选项,例如是否全屏
  • depth :指定颜色的位数,默认自动匹配
  • Surface :返回 Surface 对象,即游戏窗口对象,所有元素都要绘制到此对象上
screen = pygame.display.set_mode((600, 900))	# 创建窗口对象

可以使用 pygame.display.set_caption(string) 来设置窗口的标题

  • string :标题字符串
pygame.display.set_caption('飞机大战')  # 设置窗口标题

可以使用 pygame.display.set_icon(Surface) 来设置窗口的图标

  • Surface :pygame 图形对象
icon = pygame.image.load('./plane_war_resources/ic_launcher.png')  # 加载窗口图标
pygame.image.load('./plane_war_resources/ic_launcher.png')  # 加载窗口图标

绘制元素

可以使用 pygame.Surface.blit 方法来绘制元素到窗口上,其使用方法为:
pygame.Surface.blit(image, resolution)

  • image :绘制的元素的图形
  • resolution :绘制的元素的位置
screen.blit(bg, (0, 0))		# 绘制背景图到坐标(0, 0)

这里的两个参数通常使用图形对象和位置对象:

screen.blit(bg, bg_rect)	# 绘制背景图,使用位置对象

元素的图形

元素的图形对象使用 pygame.image.load 方法来读取图形文件,其使用方法为:
pygame.image.load(file) -> Surface

  • file :需要绘制的元素的图形文件
bg = pygame.image.load('./plane_war_resources/img/bg.jpg.png')		# 加载背景图片

注:可以在加载时使用 convertconvert_alpha 方法提升加载速度,二者区别在于 convert_alpha 用于背景透明的图形:

bg = pygame.image.load('./plane_war_resources/img/bg.jpg.png').convert_alpha()		# 加载背景图片

另:使用 pygame.image.load 只能读取静态图,即便是 gif 格式的动画图形。

如果图形资源是在一个文件中,则只需要使用一部分即可。这种方式可以一次将大量资源从文件读取到内存中使用,减少了读取次数,可以提高调用速度。

图形操作:子图

使用 Surface.subsurface 方法即可获取图形中的一部分。
Surface.subsurface(rect) -> Surface

  • rect :子图在父图中的位置及大小
image = pygame.image.load('./plane_war_resources/plist/plane.png').convert_alpha()  # 获取全部图形资源文件
enemy1 = image.subsurface((1251, 840, 39, 51))  # 获取敌机图形
图形操作:翻转和旋转

有时候在资源文件中的图像并不是想要的,而是有一定角度或翻转的,就要对图像进行一下处理。

  • pygame.transform.flip(old_img, xbool, ybool) -> Surface 翻转图像
    old_img :待翻转的 Surface 对象
    xbool :是否水平翻转
    ybool :是否上下翻转
  • pygame.transform.rotate(old_img, angle) -> Surface 旋转图像
    old_img :待旋转的 Surface 对象
    angle :旋转角度,单位是度,方向为逆时针(负值则顺时针)
enemy1 = pygame.transform.rotate(enemy1, 90)  # 旋转敌机图形
图形操作:缩放

如果取的资源图像大小和期望不一样,则可以进行缩放
pygame.transform.scale(old_img, (width, height), DestSurface=None) -> Surface

  • old_img :待缩放的 Surface 对象
  • (width, height) :缩放后的宽高,单位像素
  • DestSurface :目标 Surface 对象,默认无。当指定了 Surface 对象时,将返回对象直接放入其中,能增加程序速度
enemy1 = pygame.transform.scale(enemy1, (51 * 2, 39 * 2))   # 缩放图像
图形操作:缩放的其他形式
  • 如果想放大2倍,可以写成:
new_img= pygame.transform.scale2x(old_img)
  • 如果放大的位数比较大的时候为了避免图像出现的锯齿太过明显.可以用平滑模式
new_img = pygame.transform.smoothscale(old_img, (width, height), DestSurface = None)
  • 旋转时放缩
new_img = pygame.transform.rotozoom(old_img, angle, scale)

元素的位置

python 中图形的绘制以坐标系形式,定义图形左上角为原点(0, 0)。元素的位置对象使用 pygame.rect.Rect 对象来创建,其使用方法为:
pygame.rect.Rect(left, top, width, height) -> Rectpygame.rect.Rect((left, top), (width, height)) -> Rect

  • left :元素原点离窗口左侧的距离,即元素的 x 轴坐标,单位像素
  • top :元素原点离窗口上方的距离,即元素的 y 轴坐标,单位像素
  • width :元素的宽,单位像素
  • height:元素的高,单位像素
bg_rect = pygame.rect.Rect(0, 0, 600, 900)		# 设置背景图大小和位置

绘制其他元素

重复步骤即可绘制其他元素:

  1. 加载元素图形到 Surface 对象
  2. 创建元素位置及大小 Rect 对象
  3. 使用 blit 方法绘制到窗口对象中

这里需要注意的是绘制顺序,pygame 采用的是后绘制图像覆盖先绘制图像,所以必须先绘制背景,否则各元素都会被背景图像覆盖。元素中如需使用透明部分,则可以使用 png 等格式将部分透明化。

绘制文本元素

pygame 没有提供直接的方法在⼀个现有的 Surface 对象上绘制文本,取而代之的方法是:使用 pygame.font.Font.render() 函数创建⼀个渲染了文本的图像(Surface 对象),然后将这个图像绘制到目标 Surface 对象上。使用时需先设置字体类 Font ,然后使用方法 render 绘制成图像,最后将图像绘制到窗口中。

  1. pygame.font.SysFont(name, size, bold=False, italic=False) -> Font
    name :字体名称,字符串形式
    size :字体大小,整形
    bold :是否加粗
    italic :是否斜体
  2. pygame.font.Font.render(text, antialias, color, background=None) -> Surface
    text :显示文本
    antialias :是否抗锯齿
    color :文本颜色
    background :背景颜色,默认为空,即透明
  3. 使用 blit 绘制到窗体中
font = pygame.font.SysFont('simhei', 18)  # 设置字体及大小
text = font.render('英雄飞机', True, (255, 0, 0))  # 绘制文本图形
screen.blit(text, (250, 830))

需要注意的是, render 方法仅支持渲染一行文本。如果使用 \n 则会输出一个矩形(未知字符),所以换行需要自行处理。

另抗锯齿功能和背景可能会影响产生的 Surface 对象,可以通过具体设置来优化性能。如果只是少量的文本进行渲染,antialias 使用 True 即可。

渲染并呈现

图像绘制完成后,需要刷新显示窗口,进行渲染和呈现

pygame.display.update()

查看静态效果

渲染呈现后,已经可以看到目前的效果了。但是因为都是用图像呈现的,所以还是一个静态效果。

程序运行时会一闪而过,因为运行后很快就结束了。这时候可以增加一个死循环让程序定住,来观看效果。整体代码如下:

import pygame

pygame.init()
# 设置窗口
screen = pygame.display.set_mode((600, 900))
pygame.display.set_caption('飞机大战')  # 设置窗口标题
icon = pygame.image.load('./plane_war_resources/ic_launcher.png')  # 加载窗口图标
pygame.display.set_icon(icon)  # 设置窗口图标

# 绘制背景
bg = pygame.image.load('./plane_war_resources/img/bg.jpg.png').convert_alpha()
screen.blit(bg, (0, 0))

# 绘制英雄
hero_rect = pygame.rect.Rect(250, 700, 100, 124)
hero = pygame.image.load('./plane_war_resources/img/hero1.png').convert_alpha()
screen.blit(hero, hero_rect)

# 绘制敌机
image = pygame.image.load('./plane_war_resources/plist/plane.png').convert_alpha()  # 获取全部图形资源文件
enemy1 = image.subsurface((1251, 840, 39, 51))  # 获取敌机图形
enemy1 = pygame.transform.rotate(enemy1, 90)  # 旋转敌机图形
enemy1 = pygame.transform.smoothscale(enemy1, (51 * 2, 39 * 2))   # 缩放图像
screen.blit(enemy1, (100, 100))

# 显示文字
font = pygame.font.SysFont('simhei', 18)  # 设置字体及大小
text = font.render('英雄飞机', True, (255, 0, 0))  # 绘制文本图形
screen.blit(text, (250, 800))

# 渲染呈现
pygame.display.update()

while True:
    pass

游戏制作第二步:动画效果

动画其实就是一幅幅静止的图像在短时间内的微小移动,利用视觉暂留效果形成的。通常用刷新率表示每秒呈现的图像数量,单位为,即FPS

刷新率及刷新图像

在 pygame 中,可以使用各种方法实现,通常有两种方法,不过都是控制程序的主循环体来实现的:

  1. 在程序主循环体尾部使用延时函数来控制下一次循环的开始时间。缺点是不方便把握循环体的执行时间,也就无法精确控制刷新时间。
while True:
	pass
    time.sleep(0.1)
  1. 使用 pygame 的时钟对象,并在主循环体开始时设置循环刷新率。一般会采用这个方式,也方便之后使用时间戳功能。
clock = pygame.time.Clock	# 创建时钟对象
while True:
	clock.tick(60)			# 设置刷新率
	pass

完成刷新率的设置后,记得每次循环结束前渲染并刷新图像,就实现了每帧画面的呈现。

# 创建时钟对象
clock = pygame.time.Clock()

while True:
    clock.tick(60)  # 设置刷新率
    pygame.display.update()     # 渲染并刷新图像

这时候,只需要将需要的元素移动位置,则画面就会动起来了。

元素的运动

元素的运动有3种方式可以实现:

  1. 修改元素 Rect 的坐标
  2. 使用 move 方法进行移动
  3. 使用 move_ip 方法进行移动

三种方法中,第二种方法会不断生成新的 Rect 对象,尽管旧对象不再使用可以回收,但是还是会影响运行速度和资源占用。所以除非需要增加第二个元素对象,否则不推荐使用。

因为都是通过 Rect 实现运动,所以需要移动的元素对象必须有 Rect。之前设置的背景是直接指定坐标渲染的,没有 Rect 对象,所以需要先创建 Rect 对象。

通过 Surface 对象创建 Rect 对象

之前可以通过直接指定坐标及大小来创建 Rect 对象,也可以通过加载的 Surface 对象来获取 Rect 对象(只能获得大小),使用 Surface.get_rect 方法来创建 Rect 对象:

bg_rect = bg.get_rect()     # 创建Rect对象

修改元素 Rect 对象的坐标

可以直接修改元素 Rect 对象的 x 及 y 坐标实现元素的移动:

bg_rect.y += 1  # 背景的y轴移动

使用 move 方法移动元素

使用 move 方法可以移动元素的 Rect 对象,但是需注意的是此方法会返回一个新的 Rect 对象。所以应该连续的赋值才能形成动画。

bg_rect = bg_rect.move(0, 1)  # 背景的移动

使用 move_ip 方法移动元素

和 move 方法不同的是,move_ip 方法直接移动元素,而不会生成新的 Rect 对象。类似于直接修改 Rect 对象坐标。

bg_rect.move_ip(0, 1)  # 背景的移动

运动后的绘制及呈现

无论哪种方法修改了位置信息后,需要重新绘制到窗体,并渲染刷新。

while True:
    clock.tick(60)  # 设置刷新率
    bg_rect.move_ip(0, 1)  # 背景的移动
    screen.blit(bg, bg_rect)  # 重新绘制背景

    pygame.display.update()  # 渲染并刷新图像

此时就能看到背景图动起来了。需注意的是,除了背景外其他元素没有了,是因为背景图不断的绘制覆盖了其他元素,所以后面需要再次重新绘制其他元素。

循环动画

此例中运行后会发现背景图移动出窗体后就没了,所以需要让其循环呈现。思路是在画面外增加一个背景元素一起移动。当画面内的背景元素移出窗体时,原窗体外的画面元素就出现在了窗体内。此时将移出窗体的元素重新放回旧窗体外背景元素的位置,跟随旧元素一起移动,即形成了循环动画。

# 绘制背景
bg = pygame.image.load('./plane_war_resources/img/bg.jpg.png').convert_alpha()
bg_rect = bg.get_rect()  # 创建Rect对象
screen.blit(bg, (0, 0))
# 窗体外用于循环的背景
bg1 = pygame.image.load('./plane_war_resources/img/bg.jpg.png').convert_alpha()
bg1_rect = bg1.get_rect()  # 创建Rect对象
bg1_rect.x, bg1_rect.bottom = 0, bg_rect.top      # 设置窗体外背景的位置,x轴为0,元素底部和窗体内背景元素顶部重合
screen.blit(bg1, bg1_rect)  # 绘制窗体外的背景图

# 创建时钟对象
clock = pygame.time.Clock()

# 创建时钟对象
clock = pygame.time.Clock()

while True:
    clock.tick(60)  # 设置刷新率

    bg_rect.move_ip(0, 1)  # 背景的移动
    if bg_rect.top == screen.get_height():  # 如果背景图像从下方移出窗口
        bg_rect.bottom = 0  # 移动到窗口上方外
    screen.blit(bg, bg_rect)  # 重新绘制背景
    bg1_rect.move_ip(0, 1)  # 窗口外的背景元素跟随移动
    if bg1_rect.top == screen.get_height():  # 如果背景图像从下方移出窗口
        bg1_rect.bottom = 0  # 移动到窗口上方外
    screen.blit(bg1, bg1_rect)

其他元素的重绘,各自进行移动

每次渲染前,将其他元素进行移动和重新绘制,则各元素都动了起来。

while True:
    clock.tick(60)  # 设置刷新率
    
    bg_rect.move_ip(0, 1)  # 背景的移动
    if bg_rect.top == screen.get_height():  # 如果背景图像从下方移出窗口
        bg_rect.bottom = 0  # 移动到窗口上方外
    screen.blit(bg, bg_rect)  # 重新绘制背景
    bg1_rect.move_ip(0, 1)  # 窗口外的背景元素跟随移动
    if bg1_rect.top == screen.get_height():  # 如果背景图像从下方移出窗口
        bg1_rect.bottom = 0  # 移动到窗口上方外
    screen.blit(bg1, bg1_rect)

    # 绘制英雄
    hero_rect.move_ip(0, -1)
    screen.blit(hero, hero_rect)

    # 绘制敌机
    enemy1_rect.move_ip(1, 1)
    screen.blit(enemy1, enemy1_rect)

    pygame.display.update()  # 渲染并刷新图像

触底改变方向

增加判断,当碰触到屏幕边缘则改变运动方向

# 绘制英雄
hero_rect = pygame.rect.Rect(250, 700, 100, 124)
hero = pygame.image.load('./plane_war_resources/img/hero1.png').convert_alpha()
screen.blit(hero, hero_rect)
hero_speed = -1  # 设置英雄移动速度和初始移动方向

# 绘制敌机
image = pygame.image.load('./plane_war_resources/plist/plane.png').convert_alpha()  # 获取全部图形资源文件
enemy1 = image.subsurface((1251, 840, 39, 51))  # 获取敌机图形
enemy1 = pygame.transform.rotate(enemy1, 135)  # 旋转敌机图形
enemy1_rect = enemy1.get_rect()
enemy1_rect.x, enemy1_rect.y = 100, 100  # 设置起始位置
screen.blit(enemy1, enemy1_rect)
enemy1_speed = [1, 1]  # 设置敌机初始速度及方向

# 创建时钟对象
clock = pygame.time.Clock()

while True:
    clock.tick(60)  # 设置刷新率

    bg_rect.move_ip(0, 1)  # 背景的移动
    if bg_rect.top == screen.get_height():  # 如果背景图像从下方移出窗口
        bg_rect.bottom = 0  # 移动到窗口上方外
    screen.blit(bg, bg_rect)  # 重新绘制背景
    bg1_rect.move_ip(0, 1)  # 窗口外的背景元素跟随移动
    if bg1_rect.top == screen.get_height():  # 如果背景图像从下方移出窗口
        bg1_rect.bottom = 0  # 移动到窗口上方外
    screen.blit(bg1, bg1_rect)

    # 绘制英雄
    hero_rect.move_ip(0, hero_speed)  # 移动英雄
    if hero_rect.top <= 0 or hero_rect.bottom >= screen.get_height():  # 如果碰到窗体上方或碰到窗体下方
        hero = pygame.transform.flip(hero, False, True)  # 上下翻转图像
        hero_speed *= -1  # 翻转移动方向
    screen.blit(hero, hero_rect)

    # 绘制敌机
    enemy1_rect.move_ip(enemy1_speed[0], enemy1_speed[1])  # 移动敌机
    if enemy1_rect.top <= 0 or enemy1_rect.bottom >= screen.get_height():  # 碰到窗体上方或下方
        enemy1 = pygame.transform.flip(enemy1, False, True)  # 上下翻转图像
        enemy1_speed[1] *= -1  # 翻转y轴方向

    if enemy1_rect.left <= 0 or enemy1_rect.right >= screen.get_width():  # 碰到窗体左右两侧
        enemy1 = pygame.transform.flip(enemy1, True, False)  # 左右翻转图像
        enemy1_speed[0] *= -1  # 翻转x轴方向
    screen.blit(enemy1, enemy1_rect)  # 绘制敌机

    pygame.display.update()  # 渲染并刷新图像

面向对象,精灵及精灵组的使用

在实现了元素的移动效果后,就会发现代码非常的复杂和乱了。因为敌机会随着时间的增加越来越多,还有英雄飞机发射子弹,英雄飞机自身的动画效果、子弹击中敌机后的敌机死亡动画等等,使用面向过程的编程方式会有很多东西要处理和考虑。这时候就可以使用面向对象的方式,将各元素封装成一个个对象,类似的行为统一处理,会方便很多且代码也会简洁很多。

pygame 的精灵类

在 pygame 库中提供了一个精灵类,已经封装了很多有用的方法。我们可以将其作为基类进行派生。

class GameSprite(pygame.sprite.Sprite):
	"""自定义精灵基类,派生自 pygame 的精灵类"""
	pass

精灵类在初始化时,可以定义各自的各种属性。可以将通用的属性写在基类中

class GameSprite(pygame.sprite.Sprite):
    """自定义精灵基类,派生自 pygame 的精灵类"""

    def __init__(self, image_name, speed=1):	# 初始化时必须传入元素图像
        super().__init__()	    # 调用父类初始化函数
        # 定义对象的属性
        # 加载图像文件
        self.image = pygame.image.load(image_name).convert_alpha()
        # 确定元素大小
        self.rect = self.image.get_rect()
        # 定义移动速度
        self.speed = speed

然后定义各种元素精灵类,例如背景:

class Background(GameSprite):
    """背景精灵"""

    def __init__(self):
        super().__init__('./plane_war_resources/img/bg.jpg.png')

因为背景会生成2个实例,其起始位置不同且固定,所以可以写入背景精灵的初始化函数中。

class Background(GameSprite):
    """背景精灵"""

    def __init__(self, is_alt=False):	# 传入参数是否处于屏幕外
        super().__init__('./plane_war_resources/img/bg.jpg.png')
        if is_alt:	# 如果是屏幕外的背景
            self.rect.bottom = 0

使用的时候进行实例化,即创建两个背景实例

# 背景对象实例化
bg1 = Background()
bg2 = Background(True)

pygame 的精灵组

pygame 提供了精灵组类,作为统一管理相似的精灵的工具。并且精灵组提供了 update 和 draw 两个方法,可以方便快捷的操作精灵族内的成员。

创建精灵组并加入精灵

使用 pygame.sprite.Group(*Sprite) -> Group 来创建精灵组并将精灵加入其中

back_group = pygame.sprite.Group(bg1, bg2)  # 创建背景精灵组

精灵组的 update 方法

精灵组提供 update 方法,调用时,执行所有成员精灵的 update 方法。所以可以在成员精灵的 update 方法中分别写入其各自的行为。

class GameSprite(pygame.sprite.Sprite):
    """自定义精灵基类,派生自 pygame 的精灵类"""

    def __init__(self, image_name, speed=1):  # 初始化时必须传入元素图像
        super().__init__()  # 调用父类初始化函数
        # 定义对象的属性
        # 加载图像文件
        self.image = pygame.image.load(image_name).convert_alpha()
        # 确定元素大小
        self.rect = self.image.get_rect()
        # 定义移动速度
        self.speed = speed

    # 更新位置
    def update(self):
        self.rect.y += self.speed


class Background(GameSprite):
    """背景精灵"""

    def __init__(self, is_alt=False):  # 传入参数是否处于屏幕外
        super().__init__('./plane_war_resources/img/bg.jpg.png')
        if is_alt:  # 如果是屏幕外的背景
            self.rect.bottom = 0

    def update(self):
        # 获取父类的update,背景向下移动
        super().update()
        # 判断是否移出屏幕,如果是,则移动到屏幕上方
        if self.rect.y >= screen.get_height():
            self.rect.bottom = 0

精灵组的 draw 方法

精灵组的 draw 方法被调用时,则会将各自绘制在窗体内。图像、位置则使用其成员 image 和 rect 。

back_group.draw(screen)

使用精灵和精灵组创建当前的元素

将当前使用的英雄飞机及敌机写成类,且各实例化一个,放入精灵组。查看下效果。

import pygame


class GameSprite(pygame.sprite.Sprite):
    """自定义精灵基类,派生自 pygame 的精灵类"""

    def __init__(self, image, speed=[0, 1]):  # 初始化时必须传入元素图像
        super().__init__()  # 调用父类初始化函数
        # 定义对象的属性
        self.image = image
        # 确定元素大小
        self.rect = self.image.get_rect()
        # 定义移动速度
        self.speed = speed

    # 更新位置
    def update(self):
        self.rect.x += self.speed[0]
        self.rect.y += self.speed[1]


class Background(GameSprite):
    """背景精灵"""

    def __init__(self, is_alt=False):  # 传入参数是否处于屏幕外
        # 通过图形资源获取背景图片对象
        image = image_ALL.subsurface((1, 1, 1136, 640))
        image = pygame.transform.rotate(image, 90)
        super().__init__(image)
        if is_alt:  # 如果是屏幕外的背景
            self.rect.bottom = 0

    def update(self):
        # 获取父类的update,背景向下移动
        super().update()
        # 判断是否移出屏幕,如果是,则移动到屏幕上方
        if self.rect.y >= screen.get_height():
            self.rect.bottom = 0


class Hero(GameSprite):
    """英雄精灵"""

    def __init__(self):
        # 通过图形资源获取背景图片对象
        image = image_ALL.subsurface((1139, 517, 100, 124))
        super().__init__(image, [0, -1])  # 设置英雄飞机图像及初始速度
        # 英雄精灵初始位置
        self.rect.bottom = screen_rect.height - 70
        self.rect.centerx = screen_rect.centerx

    def update(self):
        super().update()
        # 判断碰触到窗口边框则返回
        if self.rect.top <= 0 or self.rect.bottom >= screen.get_height():  # 如果碰到窗体上方或碰到窗体下方
            self.image = pygame.transform.flip(self.image, False, True)  # 上下翻转图像
            self.speed[1] *= -1  # 翻转移动方向


class Enemy1(GameSprite):
    """敌机1"""

    def __init__(self):
        image = image_ALL.subsurface((1251, 840, 39, 51))  # 设置敌机图像
        image = pygame.transform.rotate(image, 135)  # 选择图像
        super().__init__(image)
        # 设置敌机1的初始速度和位置
        self.rect.x, self.rect.y = 100, 100
        self.speed = [1, 1]

    def update(self):
        super().update()
        if self.rect.top <= 0 or self.rect.bottom >= screen.get_height():  # 碰到窗体上方或下方
            self.image = pygame.transform.flip(self.image, False, True)  # 上下翻转图像
            self.speed[1] *= -1  # 翻转y轴方向

        if self.rect.left <= 0 or self.rect.right >= screen.get_width():  # 碰到窗体左右两侧
            self.image = pygame.transform.flip(self.image, True, False)  # 左右翻转图像
            self.speed[0] *= -1  # 翻转x轴方向


pygame.init()
# 设置窗口
screen_rect = pygame.Rect(0, 0, 640, 900)  # 设置屏幕位置类
screen = pygame.display.set_mode(screen_rect.size)
pygame.display.set_caption('飞机大战')  # 设置窗口标题
icon = pygame.image.load('./plane_war_resources/ic_launcher.png')  # 加载窗口图标
pygame.display.set_icon(icon)  # 设置窗口图标

image_ALL = pygame.image.load('./plane_war_resources/plist/plane.png').convert_alpha()  # 获取全部图形资源文件

# 背景对象实例化
bg1 = Background()
bg2 = Background(True)
back_group = pygame.sprite.Group(bg1, bg2)  # 创建背景精灵组

# 英雄对象实例化
hero = Hero()
hero_group = pygame.sprite.Group(hero)  # 创建英雄精灵组

# 敌机对象实例化
enemy1 = Enemy1()
enemy_group = pygame.sprite.Group(enemy1)  # 创建敌机精灵组

# 创建时钟对象
clock = pygame.time.Clock()

while True:
    clock.tick(60)  # 设置刷新率
    # 更新并绘制背景
    back_group.update()
    back_group.draw(screen)
    # 更新并绘制英雄
    hero_group.update()
    hero_group.draw(screen)
    # 更新并绘制敌机
    enemy_group.update()
    enemy_group.draw(screen)

    pygame.display.update()  # 渲染并刷新图像

程序对象的设计

面向对象编程不仅可以将各元素对象化,也可以将主程序实例化。将各初始设定放在初始化函数中,主循环体作为函数方法显式调用。一些功能类似的语句封装到同一个方法中去,则可以使程序简单明了。

import pygame


class GameSprite(pygame.sprite.Sprite):
    """自定义精灵基类,派生自 pygame 的精灵类"""

    def __init__(self, image, speed=[0, 1]):  # 初始化时必须传入元素图像
        super().__init__()  # 调用父类初始化函数
        # 定义对象的属性
        self.image = image
        # 确定元素大小
        self.rect = self.image.get_rect()
        # 定义移动速度
        self.speed = speed

    # 更新位置
    def update(self):
        self.rect.x += self.speed[0]
        self.rect.y += self.speed[1]


class Background(GameSprite):
    """背景精灵"""

    def __init__(self, image_all, is_alt=False):  # 传入参数是否处于屏幕外
        # 通过图形资源获取背景图片对象
        image = image_all.subsurface((1, 1, 1136, 640))
        image = pygame.transform.rotate(image, 90)
        super().__init__(image)
        if is_alt:  # 如果是屏幕外的背景
            self.rect.bottom = 0

    def update(self):
        # 获取父类的update,背景向下移动
        super().update()
        # 判断是否移出屏幕,如果是,则移动到屏幕上方
        if self.rect.y >= SCREEN_RECT.height:
            self.rect.bottom = 0


class Hero(GameSprite):
    """英雄精灵"""

    def __init__(self, image_all):
        # 通过图形资源获取背景图片对象
        image = image_all.subsurface((1139, 517, 100, 124))
        super().__init__(image, [0, -1])  # 设置英雄飞机图像及初始速度
        # 英雄精灵初始位置
        self.rect.bottom = SCREEN_RECT.height - 70
        self.rect.centerx = SCREEN_RECT.centerx

    def update(self):
        super().update()
        # 判断碰触到窗口边框则返回
        if self.rect.top <= 0 or self.rect.bottom >= SCREEN_RECT.height:  # 如果碰到窗体上方或碰到窗体下方
            self.image = pygame.transform.flip(self.image, False, True)  # 上下翻转图像
            self.speed[1] *= -1  # 翻转移动方向


class Enemy1(GameSprite):
    """敌机1"""

    def __init__(self, image_all):
        image = image_all.subsurface((1251, 840, 39, 51))  # 设置敌机图像
        image = pygame.transform.rotate(image, 135)  # 选择图像
        super().__init__(image)
        # 设置敌机1的初始速度和位置
        self.rect.x, self.rect.y = 100, 100
        self.speed = [1, 1]

    def update(self):
        super().update()
        if self.rect.top <= 0 or self.rect.bottom >= SCREEN_RECT.height:  # 碰到窗体上方或下方
            self.image = pygame.transform.flip(self.image, False, True)  # 上下翻转图像
            self.speed[1] *= -1  # 翻转y轴方向

        if self.rect.left <= 0 or self.rect.right >= SCREEN_RECT.width:  # 碰到窗体左右两侧
            self.image = pygame.transform.flip(self.image, True, False)  # 左右翻转图像
            self.speed[0] *= -1  # 翻转x轴方向


class PlaneGame:
    """飞机大战"""

    def __init__(self):
        """游戏初始化"""
        # 设置窗口
        self.screen = pygame.display.set_mode(SCREEN_RECT.size)
        pygame.display.set_caption('飞机大战')  # 设置窗口标题
        icon = pygame.image.load('./plane_war_resources/ic_launcher.png')  # 加载窗口图标
        pygame.display.set_icon(icon)  # 设置窗口图标
        # 创建初始精灵和精灵组
        self.__create_sprites()
        # 创建时钟对象
        self.clock = pygame.time.Clock()

    def __create_sprites(self):
        """创建初始精灵和精灵组"""
        # 获取全部图形资源文件
        self.image_all = pygame.image.load('./plane_war_resources/plist/plane.png').convert_alpha()
        # 背景对象实例化
        bg1 = Background(self.image_all)
        bg2 = Background(self.image_all, True)
        self.back_group = pygame.sprite.Group(bg1, bg2)  # 创建背景精灵组

        # 创建英雄精灵和精灵组
        self.hero = Hero()
        self.hero_group = pygame.sprite.Group(self.hero)

        # 敌机对象实例化
        enemy1 = Enemy1(self.image_all)
        self.enemy_group = pygame.sprite.Group(enemy1)  # 创建敌机精灵组

    def start_game(self):
        """游戏开始"""
        while True:
            self.clock.tick(60)  # 设置刷新率
            # 更新绘制精灵组
            self.__update_sprites()
            # 渲染并刷新图像
            pygame.display.update()

    def __update_sprites(self):
        """更新并绘制各精灵组"""
        # 更新并绘制背景
        self.back_group.update()
        self.back_group.draw(self.screen)
        # 更新并绘制英雄
        self.hero_group.update()
        self.hero_group.draw(self.screen)
        # 更新并绘制敌机
        self.enemy_group.update()
        self.enemy_group.draw(self.screen)


if __name__ == '__main__':
    """程序主入口"""
    # 定义常量
    SCREEN_RECT = pygame.Rect(0, 0, 640, 900)  # 屏幕矩形对象
    # 初始化pygame
    pygame.init()
    # 实例化游戏对象
    game = PlaneGame()
    # 游戏开始
    game.start_game()
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值