pygame 第三方库

第一章、概述

一、pygame介绍

Pygame是一个利用SDL库写的游戏库,SDL全名:Simple DirectMedia Layer,是一位叫做Sam Lantinga的大牛写的。

SDL是用C写的,不过它也可以使用C++进行开发,当然还有很多其他的语言,Pygame就是python中使用它的一个库

二、主要功能

pygame 是一个用于开发 2D 游戏的 Python 第三方库。它提供了许多功能强大的工具和功能,使得创建游戏变得更加容易。以下是关于 pygame 的一些详细信息:
功能特性:

1、图形绘制

pygame 提供了用于绘制基本形状、图片、文本等的功能,使得游戏界面的设计变得简单。

2、事件处理

它支持对键盘、鼠标等用户输入事件的监听和处理,让开发者可以轻松地实现用户交互。

3、声音和音乐

pygame 具备处理声音和音乐的能力,能够加载、播放和控制游戏中的音频资源。

4、碰撞检测

提供了碰撞检测的功能,方便处理游戏中对象之间的碰撞情况。

5、动画与定时器

支持动画制作和定时器功能,让开发者能够实现游戏中的动态效果。

6、跨平台性

pygame 是跨平台的,可以在多个操作系统上运行。

三、官方资源:

第二章、安装库

一、安装

pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple

在pycharm的虚拟环境中安装pygame不成功的话,则先在系统环境中安装,从系统环境中直接将包拷贝到pycharm的虚拟环境中。

二、验证安装

python -m pygame.examples.aliens

若pygame中内置游戏aliens能够正常启动,则说明pygame模块已经被正确安装且能正常使用了

第三章、游戏初始化

游戏的第一印象:

1、把一些 静止的图像 绘制到 游戏窗口

2、根据 用户的交互 或其他情况,移动 这些图像,产生动画效果

3、根据 图像之间 是否发生重叠,判断 敌机是否被摧毁 等其他情况

可以将图片素材 绘制 到游戏的窗口上,开发游戏之前需要先知道 如何建立游戏窗口!

一、游戏的初始化和退出

  • 要使用 pygame 提供的所有功能之前,需要调用 init 方法
  • 在游戏结束前需要调用一下 quit 方法


1、初始化资源:pygame.init()

  • 功能: pygame.init() 方法用于初始化 Pygame 库,准备开始使用 Pygame 提供的功能和模块。
  • 调用位置: 在使用 Pygame 前,通常需要在代码的开头调用这个方法。
  • 初始化内容: 这个方法会初始化所有导入的模块,包括显示、声音、事件、时钟等。它会检查并初始化 Pygame 的各个模块,确保准备好开始使用 Pygame 提供的功能。
  • 返回值: 一般情况下不需要接收返回值,如果初始化失败,可能会返回一个错误码或者抛出异常。

2、释放资源:pygame.quit()

  • 功能: pygame.quit() 方法用于退出 Pygame 库,释放资源并关闭 Pygame 使用的窗口和其他资源。
  • 调用位置: 通常在程序结束时调用这个方法,确保资源得到释放。
  • 关闭内容: 这个方法会释放 Pygame 使用的所有资源,包括显示、声音、事件等。它会关闭打开的窗口、释放申请的内存等资源。
  • 注意事项: 在调用 pygame.quit() 之后,一般来说就不能再继续使用 Pygame 的其他功能了,除非重新调用 pygame.init() 重新初始化 Pygame。

这两个方法是 Pygame 中很重要的初始化和退出函数,在使用 Pygame 开发游戏或应用时,通常在程序的开始和结束时分别调用这两个方法。

二、理解游戏中的坐标系

坐标系

原点 在 左上角(0, 0)

x轴 水平方向向 ,逐渐增加

y轴 垂直方向向 ,逐渐增加

在游戏中,所有可见的元素都是以 矩形区域 来描述位置的

要描述一个矩形区域有四个要素:(x, y)(width, height)

三、pygame.Rect:矩形区域类

  • pygame.Rect 是 Pygame 中用来表示矩形区域的类。它提供了对矩形进行各种操作和检测的方法
  • pygame.Rect 是一个非常实用的类,用于处理游戏中的碰撞检测、位置控制等。通过这个类的方法和属性,可以方便地对矩形进行各种操作
  • pygame.Rect是一个比较特殊的类,内部只是封装了一些数字计算
  • 不执行pygame.init()方法同样能够直接使用

1、创建矩形对象

使用坐标和尺寸创建

rect = pygame.Rect(x, y, width, height)

使用元组创建

rect = pygame.Rect((x, y), (width, height))

通过其他矩形对象创建

rect2 = rect1.copy()  # 创建一个与 rect1 相同的矩形对象

2、常用属性和方法

  • xywidthheight 分别表示矩形的左上角坐标和宽度、高度。
  • topbottomleftright 分别表示矩形的上边缘、下边缘、左边缘、右边缘的坐标位置。
  • centerxcentery 表示矩形的中心点坐标。
  • size 返回矩形的尺寸 (width, height)。
  • colliderect() 用于检测两个矩形是否相交。
  • collidepoint() 用于检测一个点是否在矩形内部。
  • inflate() 扩大或缩小矩形的尺寸。
  • move() 移动矩形。
  • clamp() 限制矩形在另一个矩形范围内。

3、基本用法

# 创建矩形
rect = pygame.Rect(100, 100, 200, 150)

# 获取矩形属性
print(rect.x, rect.y)  # 左上角坐标
print(rect.width, rect.height)  # 宽度和高度
print(rect.centerx, rect.centery)  # 中心点坐标

# 检测矩形相交
other_rect = pygame.Rect(150, 120, 100, 100)
if rect.colliderect(other_rect):
    print("Rectangles collide!")

# 移动矩形
rect.move_ip(10, 20)  # 直接修改矩形的位置

# 扩大矩形尺寸
rect.inflate_ip(20, 30)  # 直接修改矩形的大小

四、pygame.display:创建游戏主窗口

pygame专门提供了一个 模块 pygame.display 用于创建,管理 游戏窗口

pygame.display 模块提供了管理游戏窗口和屏幕显示的功能。它包含了一些常用的函数和常量,用于控制显示窗口的属性、显示模式、刷新等操作。

以下是一些常用的 pygame.display 模块的函数和常量:

1、函数

  • set_mode((width, height), flags=0, depth=0) 
    • 创建一个游戏窗口,并返回一个 Surface 对象,其中 width 和 height 分别表示窗口的宽度和高度,flags 和 depth 是可选参数,用于设置显示模式和颜色深度。
  • set_caption(title)
    •  设置窗口标题,传入一个字符串作为标题内容。
  • flip() 或 update() 
    • flip() 方法用于更新整个窗口,update() 方法也可以达到同样的效果,但可以传入参数来指定部分更新。
  • get_surface() 
    • 获取当前窗口的 Surface 对象。
  • get_caption() 
    • 获取当前窗口的标题。
  • toggle_fullscreen() 
    • 切换全屏模式。
  • toggle_window() 
    • 切换窗口模式。

1.1、pygame.display.set_mode()

初始化游戏显示窗口

set_mode(resolution=(0, 0), flags=0, depth=0) -> Surface

作用:创建游戏显示窗口

参数:

  • resolution:指定屏幕的 宽 和 高,默认创建的窗口大小和屏幕大小一致
  • flags:参数指定屏幕的附加选项,例如是否全屏等等,默认不需要传递
  • depth:参数表示颜色的位数,默认自动匹配

返回值:

暂时可以理解为 游戏的屏幕,游戏的元素 都需要绘制到 游戏的屏幕上

注意:必须使用变量记录 set_mode()方法的返回结果!因为:后续所有的图像绘制都基于这个返回结果

2、常量

  • FULLSCREEN 用于设置全屏模式的标志。
  • DOUBLEBUF 用于设置双缓冲模式的标志,能够有效地减少屏幕闪烁。
  • HWSURFACE 和 SWSURFACE 用于设置硬件加速和软件加速的标志。

3、基本用法

import pygame
from pygame import Surface

pygame.init()

# screen: Surface = pygame.display.set_mode()
# print(screen)  # <Surface(1920x1080x32 SW)>
# print(screen.__str__())  # <Surface(1920x1080x32 SW)>

# 创建游戏的窗口 480 * 700
screen: Surface = pygame.display.set_mode((480, 700))

# 游戏循环
while True:
    pass

pygame.quit()

pygame.display 模块提供了一些重要的功能来控制游戏窗口的显示模式、标题、全屏切换等操作。配合其他 Pygame 模块一起使用,可以实现游戏窗口的创建、更新和关闭等功能。

五、pygame.image:绘制图像

在游戏中,能够看到的 游戏元素 大多都是 图像

图像文件 初始是保存在磁盘上的,如果需要使用,第一步 就需要 被加载到内存

要在屏幕上 看到某一个图像的内容,需要按照三个步骤:

  • 使用 pygame.image.load() 加载图像的数据
  • 使用 游戏屏幕 对象,调用blit()方法,将图像绘制到指定位置
  • 调用 pygame.display.update()方法更新整个屏幕

提示:要想在屏幕上看到绘制的结果,就一定要调用 pygame.display.update()方法

需求:

1、加载 me1.png 创建英雄飞机

2、将 英雄飞机 绘制在屏幕的 (200, 500)位置

3、调用屏幕更新显示飞机图像

# 绘制英雄的飞机
# 1、load:加载图像
hero: Surface = pygame.image.load("../images/me1.png")
# 2、blit:绘制在屏幕
screen.blit(hero, (200, 500))
# 3、update:更新屏幕显示
pygame.display.update()

1、透明图像

透明图像:

.png 格式的图像是支持 透明

在绘制图像时,透明区域 不会显示任何内容

但是如果下方已经有内容,会 透过 透明区域 显示出来

2、理解 update() 方法的作用

可以在 screen 对象完成 所有 blit 方法之后,统一调用一次 display.update 方法,同样可以在屏幕上 看到最终的绘制对象

  • 使用 pygame.display.set_mode() 创建的screen 对象 是一个 内存中的屏幕数据对象
    • 可以理解成是 油画画布
  • screen.blit() 方法可以在 画布 上绘制很多图像
    • 例如:英雄,敌机, 子弹......
    • 这些图像 有可能会彼此的 重叠或覆盖
  • pygame.display.update()方法会将 画布 的最终结果 绘制到屏幕上,这样可以 提高屏幕绘制效率,增加游戏的流畅度

六、设置游戏时钟

pygame专门提供了一个类 pygame.time.Clock 可以非常方便的设置屏幕绘制速度 (刷新帧率

要使用 时钟对象 需要两步:

  1. 在 游戏初始化  创建一个 时钟对象
  2. 在 游戏循环 中让时钟对象调用 tick(帧率) 方法

tick 方法会根据 上次被调用的时间,自动设置 游戏循环 中的延时

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

# 游戏循环 -》 意味着游戏的正式开始
i: int = 0
while True:
    # 帧率60,每秒钟执行60次
    # 可以指定循环体内部的代码执行的频率
    clock.tick(60)
    print(i)
    i += 1

第四章、游戏循环

一、概述

为了做到游戏程序启动后,不会立即退出,通常会在游戏程序中增加一个 游戏循环

所谓的 游戏循环 就是一个 无限循环

在 创建游戏窗口 代码下方,增加一个无限循环

注意:游戏窗口不需要重复创建

import pygame

# 创建游戏主窗口
screen = pygame.display.set_mode((480, 700))

# 游戏循环
while True:
    pass

1、游戏组成两部分:游戏初始化,游戏循环

 2、游戏循环的作用

保证游戏 不会直接退出

变化图像位置:动画效果

  • 每隔 1/60 秒 移动一下所有图像的位置
  • 调用 pygame.display.update() 更新屏幕显示

检测用户交互:按键,鼠标等......

二、游戏循环中的 监听 事件

1、事件

事件 event

就是游戏启动后,用户针对游戏所做的操作

例如:点击关闭按钮,点击鼠标,按下键盘

2、监听

在 游戏循环 中,判断用户 具体的操作

只有 捕获 到用户具体的操作,才能有针对性的做出响应

3、代码实现

pygame 中通过 pygame.event.get() 可以获得 用户当前所做动作 的 事件列表

用户可以同一时间做很多事情

提示:这段代码非常的固定,几乎所有的 pygame 游戏都大同小异

import sys
import pygame

# 初始化pygame
pygame.init()

# 游戏初始化
# 设置游戏窗口
screen = pygame.display.set_mode((480, 700))
# 设置游戏窗口标题
pygame.display.set_caption("飞机大战")
# 绘制图像初始位置
background = pygame.image.load("../images/background.png")
screen.blit(background, (0, 0))
hero = pygame.image.load("../images/me1.png")
screen.blit(hero, (150, 300))
# 设置游戏时钟
clock = pygame.time.Clock()

# 1、定义rect记录飞机的初始位置
hero_rect = pygame.Rect(150, 300, 102, 126)

# 游戏循环
running = True
while running:
    clock.tick(60)
    # 捕获事件
    event_list = pygame.event.get()
    if event_list.__len__() > 0:
        print(event_list)
    # 遍历事件列表
    for event in event_list:
        # 判断事件类型是否为退出事件
        if event.type == pygame.QUIT:
            print("游戏退出...")
            # quit 卸载所有的模块
            pygame.quit()
            # 退出进程
            sys.exit(0)

    # 2、修改飞机的位置
    hero_rect.y -= 1
    # 判断飞机的位置
    if hero_rect.y <= 0:
        hero_rect.y = 574

    # 3、调用blit方法绘制图像
    screen.blit(background, (0, 0))
    screen.blit(hero, hero_rect)
    # 4、调用update方法更新显示
    pygame.display.update()

# 退出pygame
pygame.quit()

二、案例

1、英雄的简单动画实现

需求:

  • 在 游戏初始化 顶一个pygame.Rect 的变量记录英雄的初始位置
  • 在 游戏循环 中每次让 英雄 的 y - 1 ,向上移动
  • y <= 0 时,将英雄移动到屏幕的底部

提示:

每一次调用 update() 方法之前,需要把 所有的游戏图像都重新绘制一遍

而且应该 最先 重新绘制背景图片

import pygame

# 初始化pygame
pygame.init()

# 游戏初始化
# 设置游戏窗口
screen = pygame.display.set_mode((480, 700))
# 设置游戏窗口标题
pygame.display.set_caption("飞机大战")
# 绘制图像初始位置
background = pygame.image.load("../images/background.png")
screen.blit(background, (0, 0))
hero = pygame.image.load("../images/me1.png")
screen.blit(hero, (150, 300))
# 设置游戏时钟
clock = pygame.time.Clock()

# 1、定义rect记录飞机的初始位置
hero_rect = pygame.Rect(150, 300, 102, 126)

# 游戏循环
running = True
while running:
    clock.tick(60)
    # 2、修改飞机的位置
    hero_rect.y -= 1

    # 判断飞机的位置
    if hero_rect.y <= 0:
        hero_rect.y = 700

    # 3、调用blit方法绘制图像
    screen.blit(background, (0, 0))
    screen.blit(hero, hero_rect)
    # 4、调用update方法更新显示
    pygame.display.update()

# 退出pygame
pygame.quit()

第五章、 精灵和精灵组

一、概述

在上述的案例中,图像加载,位置变化,绘制图像 都需要程序员编写代码分别处理

为了简化开发步骤,pygame提供了两个类

  • pygame.sprite.Sprite     存储 图像数据 image 和 位置rect 的对象
  • pygame.sprite.Group

1、精灵:pygame.sprite.Sprite(需要派生子类)

  • image:记录图像数据
  • rect:记录在屏幕上的位置
  • update(*args):更新精灵位置
  • kill():从所有组中删除

2、精灵组:pygame.sprite.Group

  • __init__(self, *精灵)
  • add(*sprites):向组中增加精灵
  • sprites():返回所有精灵列表
  • update(*args):让组中所有精灵调用 update 方法
  • draw(Surface):将组中所有精灵的 image,绘制到Surface 的rect 位置

二、派生精灵子类

1、创建精灵子类

新建:plane_sprites.py 文件

定义 GameSprite类,继承自 pygame.sprite.Sprite类

注意:

  1. 如果一个类的 父类 不是 object
  2. 在重写 初始化方法 时,一定要先 super() 一下父类的 __init__方法
  3. 保证父类中实现的 __init__ 代码能够被正常执行

属性:

  • image:精灵图像,使用 image_name 加载
  • rect:精灵大小,默认使用图像大小
  • speed:精灵移动速度,默认为1

方法:

  • update:每次更新屏幕时在游戏循环内调用
  • 让精灵的 self.rect.y += self.speed

提示:

image 的 get_rect() 方法,可以返回 pagame.Rect(0, 0, 图像宽, 图像高) 的对象

import pygame

class PlaneSprites(pygame.sprite.Sprite):
    """
    飞机大战游戏精灵
    """
    def __init__(self, image_name, speed=1) -> None:
        # 调用父类的构造初始化方法
        super().__init__()
        # 定义对象的属性
        self.image = pygame.image.load(image_name)
        self.rect = self.image.get_rect()
        self.speed = speed

    def update(self) -> None:
        # 在屏幕的垂直方向上移动
        self.rect.y += self.speed

2、使用 游戏精灵 和 精灵组 创建敌机

需求:

使用 游戏精灵派生类 和 精灵组 创建 敌机,并且实现敌机动画

步骤:

1、使用from 导入 plane_sprites 模块中的PlaneSprites类

  • from 导入的模块可以直接使用
  • import 导入的模块需要通过 模块名. 来使用

2、在 游戏初始化 创建 精灵对象 和 精灵组 对象

3、在 游戏循环中 让 精灵组 分别调用 update() 和 draw(screen) 方法

职责:

  • 精灵
    • 封装 图像image,位置rect 和 速度 speed
    • 提供 update() 方法,根据游戏需求,更新位置 rect
  • 精灵组
    • 包含多个 精灵对象
    • update方法,让精灵组中的所有精灵调用 update 方法更新位置
    • draw(screen) 方法,在screen上绘制精灵组中的所有精灵
import sys
from typing import Any
import pygame
from pygame import Surface
import plane_sprites

# 初始化pygame
pygame.init()
# 创建游戏窗口对象
screen: Surface = pygame.display.set_mode((480, 700))
# 设置窗口标题
pygame.display.set_caption("飞机大战")
# 初始化图像位置
background = pygame.image.load("../images/background.png")
screen.blit(background, (0, 0))
hero = pygame.image.load("../images/me1.png")
screen.blit(hero, (150, 300))
# 创建 时钟对象
clock = pygame.time.Clock()
rect = pygame.Rect(150, 300, 102, 126)

# 创建敌机的精灵
enemy: Any = plane_sprites.GameSprite("../images/enemy1.png")
enemy1: Any = plane_sprites.GameSprite("../images/enemy1.png", 2)
# 创建敌机的精灵组
enemy_group = pygame.sprite.Group()
enemy_group.add(enemy)
enemy_group.add(enemy1)

running = True
while running:
    clock.tick(60)
    event_list = pygame.event.get()
    for event in event_list:
        if event.type == pygame.QUIT:
            print("游戏退出...")
            pygame.quit()
            sys.exit(0)
    rect.y -= 1
    if rect.y <= 0:
        rect.y = 574

    screen.blit(background, (0, 0))
    screen.blit(hero, rect)

    # 让精灵组调用两个方法
    # update 让组中的所有精灵更新位置
    enemy_group.update()
    # draw 将精灵组中的所有精灵绘制到屏幕时上
    enemy_group.draw(screen)
    # 更新屏幕的显示
    pygame.display.update()

# 退出pygame
pygame.quit()

第六章、游戏框架搭建

一、主游戏类搭建

二、游戏配置文件设计

使用 常量 代替固定的数值

  • 常量 -- 不变化的量
  • 变量 -- 可以变化的量

应用场景:

  • 在开发时,可能会需要使用 固定的数值,例如 屏幕的高度 是 700
  • 这个时候,建议 不要 直接使用固定数值,而应该使用 常量
  • 在开发时,为了保证代码的可维护性,尽量不要使用 魔法数字

常量的定义

  • 定义 常量 和定义 变量 的语法完全一样,都是使用 赋值语句
  • 常量 的命名应该 所有字母都使用大写,单词与单词之间使用下划线连接

常量的好处

  • 阅读代码时,通过 常量名 见名知意,不需要猜测数字的含义
  • 如果需要 调整值,只需要 修改常量定义 就可以实现 统一修改

提示:python中并没有真正意义上的常量,只是通过命名的约定 -- 所有字母都是大写的就是常量,开发时不要轻易的修改

三、游戏背景

背景交替滚动的思路确定

游戏启动后,背景图像 会 连续不断地向下方 移动

在 视觉上 产生英雄的飞机不断向上方飞行的 错觉 -- 在很多跑酷类游戏中常用的套路

  • 游戏的背景 不断变化
  • 游戏的主角 位置保持不变

实现思路:

1、设计背景类

初始化方法

  • 直接指定 背景图片
  • is_alt 判断是否为另一张图像
    • False:表示 第一张图像,需要与屏幕重合
    • True:表示 另一张图像,在屏幕的正上方
  • update() 方法
    • 判断 是否移动出屏幕,如果是,将图像设置到 屏幕的正上方,从而实现 交替滚动

继承:如果父类提供的方法,不能满足子类的需求

  • 派生出一个子类
  • 在子类中针对特有的需求,重写父类的方法,并且进行扩展

2、在main.py 中显示背景精灵

在 __create_sprites 方法中创建 精灵 和 精灵组

在 __update_sprites 方法中,让 精灵组 调用 update() 和 draw() 方法

__create_sprites方法

    def __create_sprites(self):
        """
        创建游戏精灵
        :return:
        """
        # 创建精灵组
        self.sprite_group = pygame.sprite.Group()
        # 创建背景精灵1
        self.background1: Any = background_sprites.BackgroundSprites(game_config.IMAGE_BACKGROUND)
        # 创建背景精灵2
        self.background2: Any = background_sprites.BackgroundSprites(game_config.IMAGE_BACKGROUND)
        # 修改背景精灵2的初始位置
        self.background2.rect.y = -self.background2.rect.height
        # 将创建的游戏精灵添加进精灵组中
        self.sprite_group.add(self.background1, self.background2)

__update_sprites方法

 # self.rect.y -= 1
        # if self.rect.y <= 0:
        #     self.rect.y = 574

        # self.screen.blit(self.background, (0, 0))

        # self.sprite_group.add(self.enemy1, self.enemy2)

四、定时器

使用 定时器 添加敌机

设计 Enemy 类

1、使用定时器添加敌机

  • 游戏启动后,每隔1秒 会出现一架敌机
  • 每架敌机 向屏幕下方飞行,飞行速度各不相同
  • 每架敌机出现的水平位置也不尽相同
  • 当敌机 从屏幕下方飞出,不会在飞回到屏幕中

2、定时器

在 pygame 中可以使用 pygame.time.set_timer() 来添加 定时器

所谓 定时器 ,就是 每隔一段时间 ,去执行一些动作

def set_timer(event: Union[int, Event], millis: int, loops: int = 0) -> None: ...
  • set_timer:可以创建一个 事件
  • 可以在 游戏循环 的 事件监听 方法中捕获到该事件
  • 第1个参数 事件代号 需要基于常量 pygame.USEREVENT 来指定
    • USEREVENT 是一个整数,在增加的事件可以使用 USEREVENT + 1指定,依次类推
    • 传入的参数 事件ID 必须比USEREVENT大,不能比这个数小,因此只能加
  • 第2个参数是 事件触发 间隔的 毫秒值  1秒 = 1000毫秒

3、定时器事件的监听

  1. 通过 pygame.event.get() 可以获取当前时刻所有的事件列表
  2. 遍历列表 并且判断 event.type 是否等于 eventid,如果相等,表示 定时器事件 发生

4、定义并监听创建敌机的定时器事件

pygame的定时器 使用套路非常固定:

  1. 定义 定时器常量 (eventid)
  2. 在 初始化方法 中,调用 set_timer 方法,设置定时器事件
  3. 在 游戏循环 中,监听定时器事件

五、设计Enemy类

  • 游戏启动后,每隔1秒 会出现一架敌机
  • 每架敌机 向屏幕下方飞行,飞行速度各不相同
  • 每架敌机出现的水平位置也不尽相同
  • 当敌机 从屏幕下方飞出,不会在飞回到屏幕中

初始化方法:

  • 指定 敌机图片
  • 随机 敌机的 初始位置 和 初始速度

重写 update() 方法

  • 判断 是否飞出屏幕,如果是,从 精灵组 删除

1、创建敌机

  • 在 __create_sprites,添加 敌机精灵组
    • 敌机是 定时被创建的,因此在初始化方法中,不需要创建敌机
  • 在__event_handler,创建敌机,并且 添加到精灵组
    • 调用 精灵组 的 add 方法可以 向精灵组中添加精灵
  • 在 __update_sprites,让 敌机精灵组 调用 update和draw方法

2、随机敌机位置和速度

随机速度

导入模块 random

在导入模块时,建议 按照以下顺序导入

  1. 官网标准模块导入
  2. 第三方模块导入
  3. 应用程序模块导入

随机位置

3、移出屏幕销毁敌机

  • 敌机移出屏幕之后,如果 没有撞到英雄,敌机的历史使命已经终结
  • 需要从 敌机组 删除,否则会造成 内存浪费

检测敌机被销毁

__del__内置方法会在对象被销毁前调用,在开发中,可以用于判断对象是否被销毁

六、设计Hero类

需求:

游戏启动后,英雄 出现在屏幕的 水平中间 位置,距离 屏幕底部 120 像素

英雄 每隔0.5秒发射一次子弹,每次连发三枚子弹

英雄默认不会移动,需要通过 左/右 方向键,控制 英雄 在水平方向移动

1、 初始化方法

指定 英雄图片

初始速度 为 0  英雄默认静止不动

定义 bullets 子弹精灵组 保存子弹精灵

2、重写update() 方法

英雄需要 水平移动

并且需要保证不能移出屏幕

增加bullets 属性,记录所有子弹精灵

增加fire方法,用于发射子弹

3、移动英雄位置

在pygame 中针对 键盘按键的捕获,有 两种 方式

第一种方式:

  • 判断 event.type == pygame.KEYDOWN
event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT

第二种方式:

  • 首先使用 pygame.key.get_pressed() 返回 所有按键元祖
  • 通过 键盘常量,判断元祖中 某一个键是否被按下,如果被按下,对应数值为1
# 使用键盘提供的方法获取键盘按键
keys_pressed: tuple[Any, ...] = pygame.key.get_pressed()
# 判断元祖中对应的按键索引值
if keys_pressed[pygame.K_RIGHT]:
    print(keys_pressed[pygame.K_RIGHT])
    print("右移")

两种方式的区别:

  • 第一种方式:event.type 用户 必须要抬起按键 才算一次 按键事件,操作灵活性会大打折扣
  • 第二种方式:用户可以按住方向键不放,就能够实现持续向某一个方向移动了,操作灵活性更好

在 Hero 类中重写 update方法

  • 用速度speed 和英雄 rect.x 进行叠加
  • 不需要调用父类方法 -- 父类方法只是实现了单纯的垂直运动

在__event_handler 方法中更具 左右方向键 设置英雄的 速度

  • 向右 --》 speed = 2
  • 向左 --》 speed = -2
  • 其他 --》 speed = 0

七、设计子弹类

子弹 从 英雄 的正上方发射 沿直线 向 上方飞行

飞出屏幕后,需要从 精灵组 中删除

1、初始化方法

指定子弹图片

初始速度 = 2  子弹需要向上方飞行

2、重写update()方法

判断是否飞出屏幕,如果是,从精灵组 删除

3、发射子弹

  • 在Hero的初始化方法中创还能 子弹精灵组 属性
  • 修改main.py的__update_sprites方法,让 子弹精灵组 调用 update和draw方法
  • 实现fire()方法
    • 创建子弹精灵
    • 设置初始位置 -- 在英雄的正上方
    • 将 子弹 添加到精灵组

八、碰撞检测

1、碰撞检测方法

pygame.sprite.groupcollide()

两个精灵组 中 所有的精灵 的碰撞检测

def groupcollide(
    groupa: AbstractGroup[_TSprite],
    groupb: AbstractGroup[_TSprite2],
    dokilla: bool | Literal[1] | Literal[0],
    dokillb: bool | Literal[1] | Literal[0],
    collided: Optional[Callable[[_TSprite, _TSprite2], bool]] = None,
) -> Dict[_TSprite, List[_TSprite2]]: ...
  • 如果将 dokill 设置为 True,则 发生碰撞的精灵将被自动移动
  • collided 参数是用于 计算碰撞的回调函数
    • 如果没有指定,则每个精灵必须有一个rect属性

pygame.sprite.spritecollide()

判断 某个精灵 和 指定精灵组 中的精灵的碰撞

def spritecollide(
    sprite: _HasRect,
    group: AbstractGroup[_TSprite],
    dokill: bool | Literal[1] | Literal[0],
    collided: Optional[Callable[[_HasRect, _TSprite], bool]] = None,
) -> List[_TSprite]: ...
  • 如果将 dokill 设置为 True,则 指定精灵组中发生碰撞的精灵将被自动移除
  • collided 参数是用于 计算碰撞的回调函数
    • 如果没有指定,则每个精灵必须有一个rect属性
  • 返回 精灵组 中跟 精灵 发生碰撞的 精灵列表

第七章、相关问题

一、pygame: libpng warning: iCCP问题

解决方案

示例用法:

python
import pygame

# 初始化 Pygame
pygame.init()

# 创建窗口
width = 800
height = 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("My Game")

# 游戏循环
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 绘制游戏元素等操作

    # 刷新屏幕显示
    pygame.display.flip()

# 退出 Pygame
pygame.quit()

基本用法:

import pygame
import sys

pygame.init()

# 设置窗口大小
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('My Game')

# 游戏主循环
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 绘制游戏界面

    pygame.display.flip()

pygame.quit()
sys.exit()

注意事项:

  • pygame 是一个强大的工具,但并不是专门为复杂游戏设计的。对于大型游戏项目,可能需要额外的游戏引擎或框架。
  • 使用 pygame 前需要确保已经正确安装,并了解其基本概念和功能,以充分利用其提供的功能。
  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值