关注

Pygame 完整教程

Pygame 完整教程

目录

  1. 第1章:Pygame 简介与环境搭建
  2. 第2章:Pygame 基础:窗口创建与事件处理
  3. 第3章:图形绘制
  4. 第4章:图像加载与处理
  5. 第5章:音频处理
  6. 第6章:游戏循环与动画
  7. 第7章:碰撞检测
  8. 第8章:游戏角色与精灵
  9. 第9章:用户输入处理

第1章:Pygame 简介与环境搭建

1.1 Pygame 简介

Pygame 是一个基于 SDL(Simple DirectMedia Layer)库的 Python 游戏开发库,它提供了创建游戏和多媒体应用程序的功能。Pygame 包含了图形、声音、输入处理等模块,使得开发者可以更轻松地创建各种类型的游戏。

主要特点:

  • 跨平台:支持 Windows、macOS、Linux 等操作系统
  • 简单易用:提供了简洁的 API,适合初学者
  • 功能丰富:支持图形绘制、图像加载、音频处理、碰撞检测等
  • 活跃的社区:有大量的教程和资源

1.2 环境搭建

1.2.1 安装 Python

Pygame 支持 Python 3.6 及以上版本,本教程使用 Python 3.12。

  1. 访问 Python 官方网站
  2. 下载并安装 Python 3.12
  3. 确保在安装过程中勾选 “Add Python to PATH”

1.2.2 安装 Pygame

使用 pip 命令安装 Pygame:

pip install pygame

1.3 第一个 Pygame 程序

下面是一个简单的 Pygame 程序,它创建一个窗口并显示 “Hello, Pygame!” 文字。

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块,用于退出程序
import sys

# 初始化 pygame
pygame.init()

# 设置窗口大小和标题
# set_mode 函数参数:(width, height),返回一个 Surface 对象
# 400x300 表示窗口宽度为 400 像素,高度为 300 像素
screen = pygame.display.set_mode((400, 300))
# set_caption 函数设置窗口标题
pygame.display.set_caption("我的第一个 Pygame 程序")

# 设置字体
# font.SysFont 函数参数:字体名称,字体大小
# None 表示使用默认字体,36 表示字体大小为 36
font = pygame.font.SysFont(None, 36)

# 创建文本表面
# render 函数参数:文本内容,抗锯齿标志,文本颜色,背景颜色
# "Hello, Pygame!" 是要显示的文本
# True 表示开启抗锯齿,使文本边缘更平滑
# (255, 255, 255) 是文本颜色,白色
# (0, 0, 0) 是背景颜色,黑色
text = font.render("Hello, Pygame!", True, (255, 255, 255), (0, 0, 0))

# 获取文本表面的矩形
text_rect = text.get_rect()
# 设置文本在窗口中的位置
# centerx 和 centery 分别设置矩形的中心 x 和 y 坐标
text_rect.centerx = screen.get_rect().centerx
text_rect.centery = screen.get_rect().centery

# 游戏主循环
while True:
    # 处理事件
    for event in pygame.event.get():
        # 如果点击关闭按钮
        if event.type == pygame.QUIT:
            # 退出 pygame
            pygame.quit()
            # 退出程序
            sys.exit()

    # 填充屏幕背景色
    # fill 函数参数:(R, G, B),设置背景颜色为黑色
    screen.fill((0, 0, 0))

    # 绘制文本
    # blit 函数参数:要绘制的表面,位置
    screen.blit(text, text_rect)

    # 更新屏幕
    # flip 函数将绘制的内容显示到屏幕上
    pygame.display.flip()

代码解释:

  1. 导入库:导入 pygame 和 sys 模块
  2. 初始化 pygame:调用 pygame.init() 初始化所有 pygame 模块
  3. 创建窗口:使用 pygame.display.set_mode() 创建一个指定大小的窗口
  4. 设置标题:使用 pygame.display.set_caption() 设置窗口标题
  5. 创建字体:使用 pygame.font.SysFont() 创建一个字体对象
  6. 渲染文本:使用 font.render() 创建一个包含文本的表面
  7. 设置文本位置:获取文本表面的矩形并设置其中心位置
  8. 游戏主循环
    • 处理事件:使用 pygame.event.get() 获取所有事件并处理
    • 填充背景:使用 screen.fill() 填充屏幕背景色
    • 绘制文本:使用 screen.blit() 绘制文本到屏幕
    • 更新屏幕:使用 pygame.display.flip() 更新屏幕显示

1.4 运行程序

保存上述代码为 hello_pygame.py,然后在命令行中运行:

python hello_pygame.py

你应该会看到一个黑色窗口,中央显示 “Hello, Pygame!” 白色文字。

1.5 小结

本章介绍了 Pygame 的基本概念和环境搭建步骤,并创建了第一个 Pygame 程序。通过这个简单的例子,你已经了解了 Pygame 的基本结构和工作原理。

在下一章中,我们将学习 Pygame 的窗口创建和事件处理的更多细节。


第2章:Pygame 基础:窗口创建与事件处理

2.1 窗口创建

在 Pygame 中,窗口是通过 pygame.display.set_mode() 函数创建的。这个函数返回一个 Surface 对象,代表游戏窗口的显示区域。

2.1.1 基本窗口创建

# 创建一个 800x600 的窗口
screen = pygame.display.set_mode((800, 600))

2.1.2 窗口创建的参数

pygame.display.set_mode() 函数接受以下参数:

  1. size:一个元组 (width, height),指定窗口的宽度和高度
  2. flags:控制窗口行为的标志,常见的标志有:
    • pygame.RESIZABLE:窗口可调整大小
    • pygame.FULLSCREEN:全屏模式
    • pygame.NOFRAME:无边框窗口
    • pygame.HWSURFACE:硬件加速(如果可用)
    • pygame.DOUBLEBUF:双缓冲(减少闪烁)
  3. depth:颜色深度,默认为 0(使用系统默认值)

2.1.3 示例:创建不同类型的窗口

# 创建可调整大小的窗口
screen = pygame.display.set_mode((800, 600), pygame.RESIZABLE)

# 创建全屏窗口
screen = pygame.display.set_mode((800, 600), pygame.FULLSCREEN)

# 创建双缓冲窗口(推荐用于游戏)
screen = pygame.display.set_mode((800, 600), pygame.DOUBLEBUF)

# 组合多个标志
screen = pygame.display.set_mode((800, 600), pygame.RESIZABLE | pygame.DOUBLEBUF)

2.2 事件处理

Pygame 使用事件系统来处理用户输入和系统消息。事件是发生在游戏中的各种事情,如键盘按键、鼠标点击、窗口调整大小等。

2.2.1 事件循环

在 Pygame 中,事件处理通常在游戏主循环中进行。使用 pygame.event.get() 函数获取所有待处理的事件,然后遍历这些事件进行处理。

# 游戏主循环
while True:
    # 获取所有事件
    for event in pygame.event.get():
        # 处理事件
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

2.2.2 常见事件类型

事件类型描述事件属性
pygame.QUIT用户点击窗口关闭按钮
pygame.KEYDOWN键盘按键被按下key:按下的键,mod:修饰键状态
pygame.KEYUP键盘按键被释放key:释放的键,mod:修饰键状态
pygame.MOUSEBUTTONDOWN鼠标按钮被按下pos:鼠标位置,button:按下的按钮
pygame.MOUSEBUTTONUP鼠标按钮被释放pos:鼠标位置,button:释放的按钮
pygame.MOUSEMOTION鼠标移动pos:鼠标位置,rel:相对移动距离
pygame.VIDEORESIZE窗口大小被调整size:新的窗口大小

2.2.3 事件处理示例

示例1:处理键盘事件
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            # 处理按键按下事件
            if event.key == pygame.K_ESCAPE:
                # 按下 ESC 键退出
                pygame.quit()
                sys.exit()
            elif event.key == pygame.K_SPACE:
                # 按下空格键执行某个操作
                print("空格键被按下")
        elif event.type == pygame.KEYUP:
            # 处理按键释放事件
            if event.key == pygame.K_SPACE:
                print("空格键被释放")
示例2:处理鼠标事件
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            # 处理鼠标按下事件
            if event.button == 1:  # 左键
                print(f"鼠标左键在位置 {event.pos} 被按下")
            elif event.button == 3:  # 右键
                print(f"鼠标右键在位置 {event.pos} 被按下")
        elif event.type == pygame.MOUSEMOTION:
            # 处理鼠标移动事件
            print(f"鼠标位置:{event.pos}")
示例3:处理窗口调整大小事件
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.VIDEORESIZE:
            # 处理窗口调整大小事件
            new_width, new_height = event.size
            print(f"窗口大小调整为:{new_width}x{new_height}")
            # 重新创建窗口以适应新大小
            screen = pygame.display.set_mode((new_width, new_height), pygame.RESIZABLE)

2.3 综合示例:交互式窗口

下面是一个综合示例,展示了窗口创建和事件处理的各种功能:

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块
import sys

# 初始化 pygame
pygame.init()

# 创建可调整大小的窗口
width, height = 800, 600
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
pygame.display.set_caption("交互式窗口示例")

# 设置字体
# 使用支持中文的字体
font = pygame.font.SysFont("SimHei", 30)

# 游戏主循环
while True:
    # 填充背景色
    screen.fill((240, 240, 240))

    # 获取鼠标位置
    mouse_pos = pygame.mouse.get_pos()

    # 渲染文本
    text1 = font.render(f"鼠标位置:{mouse_pos}", True, (0, 0, 0))
    text2 = font.render("按下 ESC 键退出,按下空格键切换背景色", True, (0, 0, 0))
    text3 = font.render(f"窗口大小:{width}x{height}", True, (0, 0, 0))

    # 绘制文本
    screen.blit(text1, (10, 10))
    screen.blit(text2, (10, 50))
    screen.blit(text3, (10, 90))

    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            # 退出程序
            pygame.quit()
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            # 处理按键按下事件
            if event.key == pygame.K_ESCAPE:
                # 按下 ESC 键退出
                pygame.quit()
                sys.exit()
            elif event.key == pygame.K_SPACE:
                # 按下空格键切换背景色
                import random
                screen.fill((random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
        elif event.type == pygame.MOUSEBUTTONDOWN:
            # 处理鼠标按下事件
            if event.button == 1:
                # 左键点击,在点击位置绘制一个红色圆点
                pygame.draw.circle(screen, (255, 0, 0), event.pos, 10)
        elif event.type == pygame.VIDEORESIZE:
            # 处理窗口调整大小事件
            width, height = event.size
            screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)

    # 更新屏幕
    pygame.display.flip()

代码解释:

  1. 创建窗口:使用 pygame.RESIZABLE 标志创建一个可调整大小的窗口
  2. 主循环:在循环中处理事件和更新显示
  3. 鼠标位置:使用 pygame.mouse.get_pos() 获取鼠标当前位置
  4. 文本渲染:显示鼠标位置、操作提示和窗口大小信息
  5. 事件处理
    • 处理窗口关闭事件
    • 处理 ESC 键退出
    • 处理空格键切换背景色
    • 处理鼠标左键点击绘制圆点
    • 处理窗口调整大小事件

2.4 小结

本章介绍了 Pygame 中窗口创建的各种选项和事件处理的方法。通过学习这些内容,你可以创建不同类型的窗口,并根据用户输入和系统消息做出相应的响应。

在下一章中,我们将学习 Pygame 的图形绘制功能,包括基本图形的绘制方法。


第3章:图形绘制

3.1 基本图形绘制

Pygame 提供了丰富的图形绘制功能,通过 pygame.draw 模块可以绘制各种基本图形。这些功能对于创建游戏中的元素、UI 界面和视觉效果非常重要。

3.1.1 绘制点

使用 pygame.draw.circle() 函数可以绘制点,将半径设置为 1 即可。

# 绘制一个红色的点,坐标为 (100, 100)
pygame.draw.circle(screen, (255, 0, 0), (100, 100), 1)

3.1.2 绘制线

使用 pygame.draw.line() 函数绘制直线。

# 绘制一条从 (100, 100) 到 (200, 200) 的蓝色直线,线宽为 2
pygame.draw.line(screen, (0, 0, 255), (100, 100), (200, 200), 2)

3.1.3 绘制矩形

使用 pygame.draw.rect() 函数绘制矩形。

# 绘制一个绿色的矩形,左上角坐标为 (100, 100),宽度为 200,高度为 100
pygame.draw.rect(screen, (0, 255, 0), (100, 100, 200, 100))

# 绘制一个带边框的矩形,边框宽度为 3
pygame.draw.rect(screen, (0, 255, 0), (100, 100, 200, 100), 3)

3.1.4 绘制圆形

使用 pygame.draw.circle() 函数绘制圆形。

# 绘制一个黄色的圆形,圆心坐标为 (300, 200),半径为 50
pygame.draw.circle(screen, (255, 255, 0), (300, 200), 50)

# 绘制一个带边框的圆形,边框宽度为 2
pygame.draw.circle(screen, (255, 255, 0), (300, 200), 50, 2)

3.1.5 绘制椭圆

使用 pygame.draw.ellipse() 函数绘制椭圆。

# 绘制一个紫色的椭圆,外接矩形为 (100, 100, 200, 100)
pygame.draw.ellipse(screen, (128, 0, 128), (100, 100, 200, 100))

# 绘制一个带边框的椭圆,边框宽度为 2
pygame.draw.ellipse(screen, (128, 0, 128), (100, 100, 200, 100), 2)

3.1.6 绘制多边形

使用 pygame.draw.polygon() 函数绘制多边形。

# 绘制一个青色的三角形,顶点坐标为 (100, 100), (200, 50), (200, 150)
pygame.draw.polygon(screen, (0, 255, 255), [(100, 100), (200, 50), (200, 150)])

# 绘制一个带边框的多边形,边框宽度为 2
pygame.draw.polygon(screen, (0, 255, 255), [(100, 100), (200, 50), (200, 150)], 2)

3.1.7 绘制弧线

使用 pygame.draw.arc() 函数绘制弧线。

import math

# 绘制一个红色的弧线,外接矩形为 (100, 100, 200, 200),起始角度为 0,结束角度为 π/2
pygame.draw.arc(screen, (255, 0, 0), (100, 100, 200, 200), 0, math.pi/2, 2)

3.2 颜色表示

在 Pygame 中,颜色可以用以下几种方式表示:

  1. RGB 元组(R, G, B),每个值的范围是 0-255
  2. RGBA 元组(R, G, B, A),其中 A 是透明度,范围是 0-255
  3. 十六进制字符串#RRGGBB#RRGGBBAA
  4. 颜色名称:Pygame 内置了一些颜色名称,如 pygame.Color('red')

3.2.1 示例:使用不同的颜色表示

# 使用 RGB 元组
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)

# 使用 RGBA 元组(半透明)
semi_transparent_red = (255, 0, 0, 128)

# 使用十六进制字符串
yellow = '#FFFF00'
purple = '#800080'

# 使用颜色名称
white = pygame.Color('white')
black = pygame.Color('black')

3.3 综合示例:绘制各种图形

下面是一个综合示例,展示了如何绘制各种基本图形:

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块
import sys
import math

# 初始化 pygame
pygame.init()

# 创建窗口
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("图形绘制示例")

# 设置字体
# 使用支持中文的字体
font = pygame.font.SysFont("SimHei", 24)

# 游戏主循环
while True:
    # 填充背景色
    screen.fill((240, 240, 240))

    # 绘制点
    pygame.draw.circle(screen, (255, 0, 0), (100, 100), 1)
    text = font.render("点", True, (0, 0, 0))
    screen.blit(text, (110, 100))

    # 绘制线
    pygame.draw.line(screen, (0, 0, 255), (100, 150), (200, 150), 2)
    text = font.render("线", True, (0, 0, 0))
    screen.blit(text, (210, 150))

    # 绘制矩形
    pygame.draw.rect(screen, (0, 255, 0), (100, 200, 100, 50))
    text = font.render("矩形", True, (0, 0, 0))
    screen.blit(text, (210, 220))

    # 绘制带边框的矩形
    pygame.draw.rect(screen, (0, 255, 0), (100, 270, 100, 50), 2)
    text = font.render("带边框的矩形", True, (0, 0, 0))
    screen.blit(text, (210, 290))

    # 绘制圆形
    pygame.draw.circle(screen, (255, 255, 0), (150, 370), 30)
    text = font.render("圆形", True, (0, 0, 0))
    screen.blit(text, (210, 370))

    # 绘制带边框的圆形
    pygame.draw.circle(screen, (255, 255, 0), (150, 450), 30, 2)
    text = font.render("带边框的圆形", True, (0, 0, 0))
    screen.blit(text, (210, 450))

    # 绘制椭圆
    pygame.draw.ellipse(screen, (128, 0, 128), (350, 100, 200, 100))
    text = font.render("椭圆", True, (0, 0, 0))
    screen.blit(text, (560, 140))

    # 绘制带边框的椭圆
    pygame.draw.ellipse(screen, (128, 0, 128), (350, 220, 200, 100), 2)
    text = font.render("带边框的椭圆", True, (0, 0, 0))
    screen.blit(text, (560, 260))

    # 绘制多边形(三角形)
    pygame.draw.polygon(screen, (0, 255, 255), [(450, 350), (550, 300), (550, 400)])
    text = font.render("多边形", True, (0, 0, 0))
    screen.blit(text, (560, 350))

    # 绘制弧线
    pygame.draw.arc(screen, (255, 0, 0), (350, 450, 200, 100), 0, math.pi/2, 2)
    text = font.render("弧线", True, (0, 0, 0))
    screen.blit(text, (560, 490))

    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    # 更新屏幕
    pygame.display.flip()

代码解释:

  1. 初始化 Pygame:调用 pygame.init() 初始化所有 Pygame 模块
  2. 创建窗口:设置窗口大小为 800x600
  3. 主循环:在循环中绘制各种图形并处理事件
  4. 绘制图形
    • 点:使用 pygame.draw.circle() 绘制,半径为 1
    • 线:使用 pygame.draw.line() 绘制
    • 矩形:使用 pygame.draw.rect() 绘制
    • 圆形:使用 pygame.draw.circle() 绘制
    • 椭圆:使用 pygame.draw.ellipse() 绘制
    • 多边形:使用 pygame.draw.polygon() 绘制
    • 弧线:使用 pygame.draw.arc() 绘制
  5. 文本标注:为每个图形添加文本标注,说明图形类型
  6. 事件处理:处理窗口关闭事件

3.4 高级图形绘制

3.4.1 绘制抗锯齿图形

Pygame 提供了 pygame.gfxdraw 模块,用于绘制抗锯齿图形。这些函数可以绘制更平滑的图形边缘。

# 绘制抗锯齿圆形
pygame.gfxdraw.aacircle(screen, x, y, radius, color)
pygame.gfxdraw.filled_circle(screen, x, y, radius, color)

# 绘制抗锯齿矩形
pygame.gfxdraw.box(screen, rect, color)

# 绘制抗锯齿多边形
pygame.gfxdraw.aapolygon(screen, points, color)
pygame.gfxdraw.filled_polygon(screen, points, color)

3.4.2 绘制线条和曲线

使用 pygame.draw.lines() 函数可以绘制连续的线条。

# 绘制连续的线条
points = [(100, 100), (200, 150), (300, 100), (400, 150)]
pygame.draw.lines(screen, (0, 0, 255), False, points, 2)

# 绘制闭合的线条
pygame.draw.lines(screen, (0, 0, 255), True, points, 2)

3.5 小结

本章介绍了 Pygame 中的图形绘制功能,包括基本图形的绘制方法、颜色表示和高级图形绘制技术。通过学习这些内容,你可以创建各种视觉元素,为游戏添加丰富的视觉效果。

在下一章中,我们将学习 Pygame 的图像加载与处理功能,包括如何加载和显示图像、如何对图像进行变换等。


第4章:图像加载与处理

4.1 图像加载

在 Pygame 中,图像是通过 pygame.image 模块来加载和处理的。加载图像是游戏开发中的基本操作,因为游戏中通常需要使用各种图像资源,如角色、背景、道具等。

4.1.1 基本图像加载

使用 pygame.image.load() 函数加载图像文件。

# 加载图像文件
image = pygame.image.load("image.png")

4.1.2 支持的图像格式

Pygame 支持多种图像格式,包括:

  • BMP
  • GIF
  • JPEG
  • PNG
  • SVG
  • TGA
  • TIFF

4.1.3 图像路径

当加载图像时,需要指定正确的文件路径。有两种方式:

  1. 相对路径:相对于当前脚本文件的路径
  2. 绝对路径:完整的文件路径
# 使用相对路径
image = pygame.image.load("images/player.png")

# 使用绝对路径
import os
image_path = os.path.join(os.path.dirname(__file__), "images", "player.png")
image = pygame.image.load(image_path)

4.2 显示图像

加载图像后,使用 blit() 方法将图像绘制到屏幕上。

4.2.1 基本显示

# 加载图像
image = pygame.image.load("player.png")

# 显示图像,位置为 (100, 100)
screen.blit(image, (100, 100))

4.2.2 图像位置

blit() 方法的第二个参数是图像的位置,即图像左上角的坐标。

# 显示图像在不同位置
screen.blit(image, (0, 0))      # 左上角
screen.blit(image, (width - image.get_width(), 0))  # 右上角
screen.blit(image, (0, height - image.get_height()))  # 左下角
screen.blit(image, (width - image.get_width(), height - image.get_height()))  # 右下角

4.2.3 图像尺寸

使用 get_width()get_height() 方法获取图像的宽度和高度。

# 获取图像尺寸
width = image.get_width()
height = image.get_height()
print(f"图像尺寸:{width}x{height}")

4.3 图像变换

Pygame 提供了多种图像变换方法,可以对图像进行缩放、旋转、翻转等操作。

4.3.1 缩放图像

使用 pygame.transform.scale() 函数缩放图像。

# 缩放图像到指定大小
scaled_image = pygame.transform.scale(image, (100, 100))

# 按比例缩放图像
new_width = image.get_width() // 2
new_height = image.get_height() // 2
scaled_image = pygame.transform.scale(image, (new_width, new_height))

4.3.2 旋转图像

使用 pygame.transform.rotate() 函数旋转图像。

# 旋转图像 45 度
rotated_image = pygame.transform.rotate(image, 45)

# 旋转图像 -90 度(逆时针旋转)
rotated_image = pygame.transform.rotate(image, -90)

4.3.3 翻转图像

使用 pygame.transform.flip() 函数翻转图像。

# 水平翻转图像
flipped_image = pygame.transform.flip(image, True, False)

# 垂直翻转图像
flipped_image = pygame.transform.flip(image, False, True)

# 同时水平和垂直翻转图像
flipped_image = pygame.transform.flip(image, True, True)

4.3.4 平滑缩放

使用 pygame.transform.smoothscale() 函数进行平滑缩放,适用于需要高质量缩放的情况。

# 平滑缩放图像
smoothed_image = pygame.transform.smoothscale(image, (200, 200))

4.4 图像透明度

Pygame 支持图像的透明度处理,可以创建半透明效果。

4.4.1 设置图像透明度

使用 set_alpha() 方法设置图像的透明度。

# 设置图像透明度为 128(范围 0-255,0 完全透明,255 完全不透明)
image.set_alpha(128)

4.4.2 混合模式

使用 blit() 方法的 special_flags 参数设置混合模式。

# 使用加法混合模式
screen.blit(image, (100, 100), special_flags=pygame.BLEND_ADD)

# 使用乘法混合模式
screen.blit(image, (100, 100), special_flags=pygame.BLEND_MULT)

4.5 图像裁剪

使用 subsurface() 方法从图像中裁剪出一部分。

# 裁剪图像,从 (0, 0) 开始,宽度为 50,高度为 50
rect = pygame.Rect(0, 0, 50, 50)
clipped_image = image.subsurface(rect)

4.6 综合示例:图像操作

下面是一个综合示例,展示了图像加载、显示、变换和透明度处理的功能:

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块
import sys
import os

# 初始化 pygame
pygame.init()

# 创建窗口
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("图像加载与处理示例")

# 设置字体
# 使用支持中文的字体
font = pygame.font.SysFont("SimHei", 24)

# 加载图像
# 注意:这里需要确保有对应的图像文件
# 如果没有图像文件,可以注释掉相关代码

# 尝试加载图像
image = None
try:
    # 这里使用一个占位图像,实际使用时需要替换为真实的图像文件
    # 创建一个简单的红色矩形作为占位图像
    image = pygame.Surface((100, 100))
    image.fill((255, 0, 0))
except Exception as e:
    print(f"无法加载图像:{e}")

# 图像变换
if image:
    # 缩放图像
    scaled_image = pygame.transform.scale(image, (50, 50))

    # 旋转图像
    rotated_image = pygame.transform.rotate(image, 45)

    # 翻转图像
    flipped_image = pygame.transform.flip(image, True, False)

    # 设置透明度
    transparent_image = image.copy()
    transparent_image.set_alpha(128)

# 游戏主循环
while True:
    # 填充背景色
    screen.fill((240, 240, 240))

    # 显示文本
    text = font.render("图像加载与处理示例", True, (0, 0, 0))
    screen.blit(text, (10, 10))

    # 显示图像
    if image:
        # 原始图像
        screen.blit(image, (100, 100))
        text = font.render("原始图像", True, (0, 0, 0))
        screen.blit(text, (100, 210))

        # 缩放图像
        screen.blit(scaled_image, (250, 100))
        text = font.render("缩放图像", True, (0, 0, 0))
        screen.blit(text, (250, 210))

        # 旋转图像
        screen.blit(rotated_image, (350, 100))
        text = font.render("旋转图像", True, (0, 0, 0))
        screen.blit(text, (350, 210))

        # 翻转图像
        screen.blit(flipped_image, (500, 100))
        text = font.render("翻转图像", True, (0, 0, 0))
        screen.blit(text, (500, 210))

        # 透明图像
        screen.blit(transparent_image, (100, 250))
        text = font.render("透明图像", True, (0, 0, 0))
        screen.blit(text, (100, 360))
    else:
        text = font.render("未加载到图像", True, (255, 0, 0))
        screen.blit(text, (100, 100))

    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    # 更新屏幕
    pygame.display.flip()

代码解释:

  1. 初始化 Pygame:调用 pygame.init() 初始化所有 Pygame 模块
  2. 创建窗口:设置窗口大小为 800x600
  3. 加载图像:尝试加载图像文件,如果失败则创建一个红色矩形作为占位图像
  4. 图像变换
    • 缩放图像:使用 pygame.transform.scale() 函数
    • 旋转图像:使用 pygame.transform.rotate() 函数
    • 翻转图像:使用 pygame.transform.flip() 函数
    • 设置透明度:使用 set_alpha() 方法
  5. 主循环:在循环中显示各种图像和处理事件

4.7 图像资源管理

在游戏开发中,合理管理图像资源非常重要。以下是一些最佳实践:

  1. 创建图像目录:将所有图像资源放在一个专门的目录中,如 images/
  2. 预加载图像:在游戏开始时加载所有需要的图像,避免在游戏过程中加载
  3. 使用图像精灵表:将多个小图像合并到一个大图像中,减少文件数量和加载时间
  4. 释放未使用的图像:当不再需要某个图像时,将其设置为 None,让 Python 的垃圾回收机制回收内存

4.8 小结

本章介绍了 Pygame 中的图像加载与处理功能,包括如何加载图像、显示图像、对图像进行变换和处理透明度等。通过学习这些内容,你可以在游戏中使用各种图像资源,创建丰富的视觉效果。

在下一章中,我们将学习 Pygame 的音频处理功能,包括如何加载和播放声音、音乐等。


第5章:音频处理

5.1 音频基础

Pygame 提供了音频处理功能,通过 pygame.mixer 模块可以加载和播放声音、音乐等音频资源。音频是游戏中重要的组成部分,它可以增强游戏的沉浸感和交互体验。

5.1.1 音频类型

Pygame 支持两种类型的音频:

  1. 声音(Sound):短音效,如爆炸声、脚步声等,使用 pygame.mixer.Sound
  2. 音乐(Music):长音乐,如背景音乐,使用 pygame.mixer.music 模块

5.2 声音处理

5.2.1 加载声音

使用 pygame.mixer.Sound 类加载声音文件。

# 加载声音文件
sound = pygame.mixer.Sound("sound.wav")

5.2.2 播放声音

使用 play() 方法播放声音。

# 播放声音
sound.play()

# 播放声音,循环 2 次(总共播放 3 次)
sound.play(loops=2)

# 播放声音,延迟 1000 毫秒后开始播放
sound.play(delay=1000)

5.2.3 停止声音

使用 stop() 方法停止声音。

# 停止声音
sound.stop()

5.2.4 设置声音音量

使用 set_volume() 方法设置声音音量。

# 设置音量为 50%
sound.set_volume(0.5)

# 获取当前音量
volume = sound.get_volume()
print(f"当前音量:{volume}")

5.3 音乐处理

5.3.1 加载音乐

使用 pygame.mixer.music.load() 函数加载音乐文件。

# 加载音乐文件
pygame.mixer.music.load("music.mp3")

5.3.2 播放音乐

使用 pygame.mixer.music.play() 函数播放音乐。

# 播放音乐
pygame.mixer.music.play()

# 播放音乐,循环 -1 次(无限循环)
pygame.mixer.music.play(loops=-1)

# 播放音乐,延迟 2000 毫秒后开始播放
pygame.mixer.music.play(start=2.0)  # 从音乐的 2 秒处开始播放

5.3.3 暂停和恢复音乐

使用 pause()unpause() 函数暂停和恢复音乐。

# 暂停音乐
pygame.mixer.music.pause()

# 恢复音乐
pygame.mixer.music.unpause()

5.3.4 停止音乐

使用 stop() 函数停止音乐。

# 停止音乐
pygame.mixer.music.stop()

5.3.5 设置音乐音量

使用 set_volume() 函数设置音乐音量。

# 设置音量为 70%
pygame.mixer.music.set_volume(0.7)

# 获取当前音量
volume = pygame.mixer.music.get_volume()
print(f"当前音量:{volume}")

5.3.6 音乐结束事件

当音乐播放完毕时,可以设置一个回调函数。

# 定义音乐结束回调函数
def music_end():
    print("音乐播放完毕")

# 设置音乐结束回调
pygame.mixer.music.set_endevent(pygame.USEREVENT)

# 在事件循环中处理音乐结束事件
while True:
    for event in pygame.event.get():
        if event.type == pygame.USEREVENT:
            music_end()

5.4 音频格式支持

Pygame 支持多种音频格式,具体取决于底层的 SDL_mixer 库。常见的支持格式包括:

  • WAV
  • MP3
  • OGG
  • MIDI
  • FLAC

5.5 综合示例:音频播放器

下面是一个综合示例,展示了音频加载和播放的功能:

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块
import sys

# 初始化 pygame
pygame.init()

# 初始化混音器
# 设置音频缓冲区大小为 4096 字节
pygame.mixer.init(buffer=4096)

# 创建窗口
width, height = 600, 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("音频处理示例")

# 设置字体
# 使用支持中文的字体
font = pygame.font.SysFont("SimHei", 24)

# 加载声音和音乐
# 注意:这里需要确保有对应的音频文件
# 如果没有音频文件,会使用模拟的音频

sound = None
music = None
try:
    # 尝试加载音频文件
    sound = pygame.mixer.Sound("sounds/sound.wav")
    pygame.mixer.music.load("sounds/music.wav")
    print("音频加载成功!")
except Exception as e:
    # 如果加载失败,使用模拟的音频
    print(f"无法加载音频文件:{e}")
    print("使用模拟音频模式")
    # 创建一个简单的正弦波作为模拟声音
    import numpy as np
    sample_rate = 44100
    duration = 0.5  # 0.5秒
    frequency = 440  # 440Hz
    samples = np.sin(2 * np.pi * np.arange(sample_rate * duration) * frequency / sample_rate)
    samples = (samples * 32767).astype(np.int16)
    sound = pygame.mixer.Sound(buffer=samples)

# 游戏主循环
while True:
    # 填充背景色
    screen.fill((240, 240, 240))

    # 显示文本
    text = font.render("音频处理示例", True, (0, 0, 0))
    screen.blit(text, (10, 10))

    text = font.render("按空格键播放/暂停声音", True, (0, 0, 0))
    screen.blit(text, (10, 50))

    text = font.render("按 M 键播放/停止音乐", True, (0, 0, 0))
    screen.blit(text, (10, 90))

    text = font.render("按 ESC 键退出", True, (0, 0, 0))
    screen.blit(text, (10, 130))

    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                sys.exit()
            elif event.key == pygame.K_SPACE:
                # 播放声音
                if sound:
                    sound.play()
                else:
                    print("没有加载声音文件")
            elif event.key == pygame.K_m:
                # 播放/停止音乐
                if pygame.mixer.music.get_busy():
                    pygame.mixer.music.stop()
                else:
                    # 直接播放音乐,不需要检查 music 变量
                    pygame.mixer.music.play(loops=-1)  # 无限循环

    # 更新屏幕
    pygame.display.flip()

代码解释:

  1. 初始化 Pygame:调用 pygame.init() 初始化所有 Pygame 模块
  2. 初始化混音器:调用 pygame.mixer.init() 初始化音频系统
  3. 创建窗口:设置窗口大小为 600x400
  4. 加载音频:尝试加载声音和音乐文件
  5. 主循环:在循环中处理事件和更新显示
  6. 事件处理
    • 按空格键播放声音
    • 按 M 键播放/停止音乐
    • 按 ESC 键退出

5.6 音频资源管理

在游戏开发中,合理管理音频资源非常重要。以下是一些最佳实践:

  1. 创建音频目录:将所有音频资源放在一个专门的目录中,如 sounds/music/
  2. 预加载音频:在游戏开始时加载所有需要的音频,避免在游戏过程中加载
  3. 释放未使用的音频:当不再需要某个音频时,将其设置为 None,让 Python 的垃圾回收机制回收内存
  4. 控制音量:为不同类型的音频设置适当的音量,确保游戏声音不会过于嘈杂

5.7 小结

本章介绍了 Pygame 中的音频处理功能,包括如何加载和播放声音、音乐,以及如何控制音量和处理音乐结束事件等。通过学习这些内容,你可以在游戏中添加声音效果和背景音乐,增强游戏的沉浸感和交互体验。

在下一章中,我们将学习 Pygame 的游戏循环与动画功能,包括如何创建游戏循环、实现动画效果等。


第6章:游戏循环与动画

6.1 游戏循环

游戏循环是游戏的核心,它负责持续更新游戏状态、处理用户输入、渲染画面等。一个典型的游戏循环包括以下几个步骤:

  1. 处理事件:获取并处理用户输入和系统事件
  2. 更新游戏状态:更新游戏对象的位置、状态等
  3. 渲染画面:绘制游戏对象到屏幕上
  4. 控制帧率:确保游戏以稳定的帧率运行

6.1.1 基本游戏循环结构

import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()

running = True
while running:
    # 1. 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 2. 更新游戏状态
    # 这里可以更新游戏对象的位置、状态等

    # 3. 渲染画面
    screen.fill((0, 0, 0))
    # 这里可以绘制游戏对象
    pygame.display.flip()

    # 4. 控制帧率
    clock.tick(60)  # 限制帧率为 60 FPS

pygame.quit()
sys.exit()

6.2 帧率控制

帧率(FPS,Frames Per Second)是指游戏每秒绘制的帧数。稳定的帧率对于游戏的流畅度非常重要。Pygame 提供了 pygame.time.Clock 类来帮助控制帧率。

6.2.1 使用 Clock 控制帧率

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

# 在游戏循环中限制帧率
while running:
    # 处理事件和更新游戏状态
    # ...

    # 限制帧率为 60 FPS
    clock.tick(60)

    # 可以获取实际帧率
    fps = clock.get_fps()
    print(f"当前帧率:{fps:.2f} FPS")

6.2.2 时间增量

在游戏开发中,使用时间增量(delta time)来确保游戏在不同帧率下的行为一致是一种常见的做法。时间增量是指上一帧到当前帧的时间间隔。

clock = pygame.time.Clock()

while running:
    # 获取时间增量(以秒为单位)
    dt = clock.tick(60) / 1000.0

    # 使用时间增量更新游戏状态
    # 例如,移动一个物体,速度为 100 像素/秒
    x += 100 * dt

6.3 动画实现

动画是通过在每一帧中微小地改变游戏对象的状态(如位置、大小、颜色等)来实现的。

6.3.1 基本动画

示例:移动一个矩形
import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()

# 矩形初始位置和速度
x, y = 100, 100
velocity = 5

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 更新位置
    x += velocity

    # 边界检测
    if x > 700 or x < 0:
        velocity = -velocity

    # 渲染
    screen.fill((0, 0, 0))
    pygame.draw.rect(screen, (255, 0, 0), (x, y, 100, 50))
    pygame.display.flip()

    clock.tick(60)

pygame.quit()
sys.exit()

6.3.2 精灵动画

精灵动画是通过在每一帧中显示不同的图像来实现的。例如,一个角色的行走动画可以由一系列不同的帧组成。

示例:简单的精灵动画
import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()

# 创建一个简单的精灵动画
# 这里使用不同颜色的矩形模拟不同的帧
frames = []
for i in range(4):
    frame = pygame.Surface((50, 50))
    frame.fill((i * 50, 0, 255 - i * 50))
    frames.append(frame)

current_frame = 0
frame_count = 0
x, y = 100, 100

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 更新位置
    x += 2
    if x > 750:
        x = 0

    # 更新动画帧
    frame_count += 1
    if frame_count % 10 == 0:  # 每 10 帧切换一次
        current_frame = (current_frame + 1) % len(frames)

    # 渲染
    screen.fill((0, 0, 0))
    screen.blit(frames[current_frame], (x, y))
    pygame.display.flip()

    clock.tick(60)

pygame.quit()
sys.exit()

6.4 综合示例:弹跳球动画

下面是一个综合示例,展示了游戏循环和动画的实现:

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块
import sys

# 初始化 pygame
pygame.init()

# 创建窗口
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("弹跳球动画")

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

# 球的属性
ball_radius = 30
ball_x = width // 2
ball_y = height // 2
ball_velocity_x = 5
ball_velocity_y = 5
ball_color = (255, 0, 0)

# 游戏主循环
running = True
while running:
    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False

    # 更新球的位置
    ball_x += ball_velocity_x
    ball_y += ball_velocity_y

    # 边界检测
    if ball_x - ball_radius < 0 or ball_x + ball_radius > width:
        ball_velocity_x = -ball_velocity_x
    if ball_y - ball_radius < 0 or ball_y + ball_radius > height:
        ball_velocity_y = -ball_velocity_y

    # 渲染画面
    screen.fill((240, 240, 240))
    pygame.draw.circle(screen, ball_color, (ball_x, ball_y), ball_radius)

    # 显示帧率
    font = pygame.font.SysFont(None, 24)
    fps = clock.get_fps()
    text = font.render(f"FPS: {fps:.2f}", True, (0, 0, 0))
    screen.blit(text, (10, 10))

    # 更新屏幕
    pygame.display.flip()

    # 控制帧率
    clock.tick(60)

# 退出游戏
pygame.quit()
sys.exit()

代码解释:

  1. 初始化 Pygame:调用 pygame.init() 初始化所有 Pygame 模块
  2. 创建窗口:设置窗口大小为 800x600
  3. 创建时钟对象:用于控制帧率
  4. 设置球的属性:包括位置、速度、大小和颜色
  5. 游戏主循环
    • 处理事件:处理窗口关闭和 ESC 键退出
    • 更新球的位置:根据速度更新球的坐标
    • 边界检测:当球碰到窗口边缘时反弹
    • 渲染画面:填充背景色并绘制球
    • 显示帧率:在屏幕左上角显示当前帧率
    • 控制帧率:限制帧率为 60 FPS

6.5 高级动画技术

6.5.1 使用 pygame.time 模块

Pygame 的 pygame.time 模块提供了一些用于动画的功能,如定时器和事件。

# 创建一个自定义事件
ANIMATION_EVENT = pygame.USEREVENT + 1

# 设置定时器,每 100 毫秒触发一次事件
pygame.time.set_timer(ANIMATION_EVENT, 100)

# 在事件循环中处理动画事件
while running:
    for event in pygame.event.get():
        if event.type == ANIMATION_EVENT:
            # 执行动画更新
            update_animation()

6.5.2 平滑动画

使用时间增量和插值可以实现更平滑的动画效果。

# 目标位置
target_x, target_y = 400, 300

# 当前位置
current_x, current_y = 100, 100

# 缓动系数
ease = 0.05

while running:
    # 计算时间增量
    dt = clock.tick(60) / 1000.0

    # 使用缓动更新位置
    current_x += (target_x - current_x) * ease
    current_y += (target_y - current_y) * ease

    # 渲染
    screen.fill((0, 0, 0))
    pygame.draw.circle(screen, (255, 0, 0), (int(current_x), int(current_y)), 20)
    pygame.display.flip()

6.6 小结

本章介绍了 Pygame 中的游戏循环与动画实现,包括游戏循环的基本结构、帧率控制、基本动画和精灵动画的实现方法。通过学习这些内容,你可以创建流畅的游戏动画,提升游戏的视觉效果和用户体验。

在下一章中,我们将学习 Pygame 的碰撞检测功能,包括如何检测游戏对象之间的碰撞。


第7章:碰撞检测

7.1 碰撞检测基础

碰撞检测是游戏开发中的重要功能,用于检测游戏对象之间的碰撞。在 Pygame 中,有多种方法可以实现碰撞检测,包括矩形碰撞、圆形碰撞、像素级碰撞等。

7.1.1 碰撞检测的重要性

碰撞检测在游戏中有着广泛的应用:

  • 检测玩家与敌人的碰撞
  • 检测玩家与道具的碰撞
  • 检测游戏对象与边界的碰撞
  • 检测子弹与目标的碰撞

7.2 矩形碰撞检测

矩形碰撞检测是最常用的碰撞检测方法,通过检测两个矩形是否重叠来判断碰撞。

7.2.1 使用 Rect 对象

Pygame 中的 Rect 对象提供了碰撞检测方法。

# 创建两个矩形
rect1 = pygame.Rect(100, 100, 50, 50)
rect2 = pygame.Rect(120, 120, 50, 50)

# 检测矩形碰撞
if rect1.colliderect(rect2):
    print("矩形碰撞了!")

7.2.2 点与矩形碰撞

检测一个点是否在矩形内。

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

# 检测点是否在矩形内
point = (120, 120)
if rect.collidepoint(point):
    print("点在矩形内!")

7.2.3 多个矩形碰撞

检测一个矩形是否与多个矩形中的任何一个碰撞。

# 创建一个矩形
rect = pygame.Rect(100, 100, 50, 50)

# 创建多个矩形
rects = [
    pygame.Rect(120, 120, 50, 50),
    pygame.Rect(200, 200, 50, 50),
    pygame.Rect(300, 300, 50, 50)
]

# 检测碰撞
collided = rect.collidelist(rects)
if collided != -1:
    print(f"与第 {collided} 个矩形碰撞了!")

7.3 圆形碰撞检测

圆形碰撞检测通过计算两个圆心之间的距离是否小于两个圆的半径之和来判断碰撞。

7.3.1 基本圆形碰撞检测

import math

# 圆 1 的位置和半径
x1, y1, r1 = 100, 100, 25

# 圆 2 的位置和半径
x2, y2, r2 = 150, 150, 30

# 计算圆心距离
distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2)

# 检测碰撞
if distance < r1 + r2:
    print("圆形碰撞了!")

7.3.2 使用 Pygame 的 Circle 类

Pygame 没有内置的 Circle 类,但可以使用 pygame.draw.circle() 绘制圆形,并手动实现碰撞检测。

7.4 像素级碰撞检测

像素级碰撞检测是最精确的碰撞检测方法,通过检测两个图像的像素是否重叠来判断碰撞。

7.4.1 使用 mask 进行像素级碰撞检测

# 加载图像
image1 = pygame.image.load("player.png").convert_alpha()
image2 = pygame.image.load("enemy.png").convert_alpha()

# 创建 mask
mask1 = pygame.mask.from_surface(image1)
mask2 = pygame.mask.from_surface(image2)

# 计算偏移量
offset = (x2 - x1, y2 - y1)

# 检测碰撞
if mask1.overlap(mask2, offset):
    print("像素级碰撞了!")

7.5 综合示例:碰撞检测游戏

下面是一个综合示例,展示了如何在游戏中实现碰撞检测:

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块
import sys
import random

# 初始化 pygame
pygame.init()

# 创建窗口
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("碰撞检测示例")

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

# 玩家属性
player_size = 50
player_x = width // 2 - player_size // 2
player_y = height - player_size - 20
player_velocity = 5
player_rect = pygame.Rect(player_x, player_y, player_size, player_size)

# 敌人属性
enemies = []
enemy_size = 50
enemy_velocity = 3

# 生成敌人
def create_enemy():
    x = random.randint(0, width - enemy_size)
    y = -enemy_size
    return pygame.Rect(x, y, enemy_size, enemy_size)

# 生成初始敌人
for i in range(5):
    enemy = create_enemy()
    enemies.append(enemy)

# 游戏主循环
running = True
while running:
    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False

    # 处理玩家输入
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_x > 0:
        player_x -= player_velocity
    if keys[pygame.K_RIGHT] and player_x < width - player_size:
        player_x += player_velocity

    # 更新玩家矩形
    player_rect = pygame.Rect(player_x, player_y, player_size, player_size)

    # 更新敌人
    for enemy in enemies:
        enemy.y += enemy_velocity

        # 检测敌人是否超出屏幕
        if enemy.y > height:
            enemies.remove(enemy)
            new_enemy = create_enemy()
            enemies.append(new_enemy)

        # 检测碰撞
        if player_rect.colliderect(enemy):
            print("碰撞了!游戏结束!")
            running = False

    # 渲染画面
    screen.fill((240, 240, 240))

    # 绘制玩家
    pygame.draw.rect(screen, (0, 0, 255), player_rect)

    # 绘制敌人
    for enemy in enemies:
        pygame.draw.rect(screen, (255, 0, 0), enemy)

    # 更新屏幕
    pygame.display.flip()

    # 控制帧率
    clock.tick(60)

# 退出游戏
pygame.quit()
sys.exit()

代码解释:

  1. 初始化 Pygame:调用 pygame.init() 初始化所有 Pygame 模块
  2. 创建窗口:设置窗口大小为 800x600
  3. 设置玩家属性:包括大小、位置和速度
  4. 设置敌人属性:包括大小、速度和初始数量
  5. 游戏主循环
    • 处理事件:处理窗口关闭和 ESC 键退出
    • 处理玩家输入:使用方向键控制玩家左右移动
    • 更新敌人:敌人向下移动,超出屏幕后重新生成
    • 碰撞检测:检测玩家与敌人的碰撞
    • 渲染画面:绘制玩家和敌人
    • 控制帧率:限制帧率为 60 FPS

7.6 高级碰撞检测技术

7.6.1 轴对齐 bounding box (AABB) 碰撞检测

AABB 是最常用的碰撞检测方法,适用于矩形对象。

def check_collision(rect1, rect2):
    return (
        rect1.x < rect2.x + rect2.width and
        rect1.x + rect1.width > rect2.x and
        rect1.y < rect2.y + rect2.height and
        rect1.y + rect1.height > rect2.y
    )

7.6.2 旋转矩形碰撞检测

对于旋转的矩形,需要使用更复杂的碰撞检测方法,如分离轴定理 (SAT)。

7.6.3 碰撞响应

碰撞检测后,需要处理碰撞响应,如反弹、损伤计算等。

# 简单的碰撞响应:反弹
if rect1.colliderect(rect2):
    # 交换速度
    rect1.velocity_x, rect2.velocity_x = rect2.velocity_x, rect1.velocity_x
    rect1.velocity_y, rect2.velocity_y = rect2.velocity_y, rect1.velocity_y

7.7 小结

本章介绍了 Pygame 中的碰撞检测功能,包括矩形碰撞、圆形碰撞和像素级碰撞的实现方法。通过学习这些内容,你可以在游戏中实现各种碰撞检测需求,如玩家与敌人的碰撞、玩家与道具的碰撞等。

在下一章中,我们将学习 Pygame 的游戏角色与精灵功能,包括如何创建和管理游戏角色。


第8章:游戏角色与精灵

8.1 精灵基础

精灵(Sprite)是游戏开发中用于表示游戏角色、道具、敌人等游戏对象的基本单位。Pygame 提供了 pygame.sprite 模块来帮助管理游戏精灵。

8.1.1 精灵的概念

精灵是一个包含图像、位置、速度等属性的对象,它可以在游戏中移动、碰撞检测和渲染。使用精灵可以使游戏对象的管理更加方便和高效。

8.2 创建精灵类

在 Pygame 中,创建精灵需要继承 pygame.sprite.Sprite 类。

8.2.1 基本精灵类

import pygame

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # 创建精灵图像
        self.image = pygame.Surface((50, 50))
        self.image.fill((0, 0, 255))
        # 获取精灵矩形
        self.rect = self.image.get_rect()
        # 设置精灵位置
        self.rect.center = (400, 300)
        # 设置精灵速度
        self.velocity = [0, 0]

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

8.2.2 使用图像创建精灵

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # 加载精灵图像
        self.image = pygame.image.load("player.png").convert_alpha()
        # 获取精灵矩形
        self.rect = self.image.get_rect()
        # 设置精灵位置
        self.rect.center = (400, 300)
        # 设置精灵速度
        self.velocity = [0, 0]

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

8.3 精灵组

精灵组(Sprite Group)是用于管理多个精灵的容器。使用精灵组可以方便地对多个精灵进行批量操作,如更新和渲染。

8.3.1 创建精灵组

# 创建精灵组
all_sprites = pygame.sprite.Group()

# 创建精灵
player = Player()

# 将精灵添加到精灵组
all_sprites.add(player)

8.3.2 更新和渲染精灵组

# 更新精灵组中的所有精灵
all_sprites.update()

# 渲染精灵组中的所有精灵
all_sprites.draw(screen)

8.3.3 碰撞检测与精灵组

# 创建敌人精灵组
enemies = pygame.sprite.Group()

# 检测玩家与敌人的碰撞
collisions = pygame.sprite.spritecollide(player, enemies, True)
if collisions:
    print("玩家与敌人碰撞了!")

8.4 精灵动画

精灵动画是通过在每一帧中切换精灵的图像来实现的。

8.4.1 基本精灵动画

class AnimatedPlayer(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # 加载动画帧
        self.frames = []
        for i in range(4):
            frame = pygame.Surface((50, 50))
            frame.fill((i * 50, 0, 255 - i * 50))
            self.frames.append(frame)
        # 当前帧索引
        self.current_frame = 0
        # 设置当前图像
        self.image = self.frames[self.current_frame]
        # 获取精灵矩形
        self.rect = self.image.get_rect()
        self.rect.center = (400, 300)
        # 帧计数器
        self.frame_count = 0

    def update(self):
        # 更新帧计数器
        self.frame_count += 1
        # 每 10 帧切换一次图像
        if self.frame_count % 10 == 0:
            self.current_frame = (self.current_frame + 1) % len(self.frames)
            self.image = self.frames[self.current_frame]

8.5 综合示例:精灵游戏

下面是一个综合示例,展示了如何使用精灵和精灵组创建一个简单的游戏:

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块
import sys
import random

# 初始化 pygame
pygame.init()

# 创建窗口
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("精灵游戏示例")

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

# 定义颜色
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)

# 玩家精灵类
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # 创建玩家图像
        self.image = pygame.Surface((50, 50))
        self.image.fill(BLUE)
        # 获取矩形
        self.rect = self.image.get_rect()
        # 设置初始位置
        self.rect.center = (width // 2, height - 50)
        # 设置速度
        self.velocity = 5

    def update(self):
        # 处理键盘输入
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT] and self.rect.left > 0:
            self.rect.x -= self.velocity
        if keys[pygame.K_RIGHT] and self.rect.right < width:
            self.rect.x += self.velocity

# 敌人精灵类
class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # 创建敌人图像
        self.image = pygame.Surface((30, 30))
        self.image.fill(RED)
        # 获取矩形
        self.rect = self.image.get_rect()
        # 设置初始位置
        self.rect.x = random.randint(0, width - 30)
        self.rect.y = random.randint(-100, -30)
        # 设置速度
        self.velocity = random.randint(2, 5)

    def update(self):
        # 向下移动
        self.rect.y += self.velocity
        # 如果超出屏幕,重新生成
        if self.rect.top > height:
            self.rect.x = random.randint(0, width - 30)
            self.rect.y = random.randint(-100, -30)
            self.velocity = random.randint(2, 5)

# 创建精灵组
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()

# 创建玩家
player = Player()
all_sprites.add(player)

# 创建敌人
for i in range(10):
    enemy = Enemy()
    all_sprites.add(enemy)
    enemies.add(enemy)

# 游戏主循环
running = True
while running:
    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False

    # 更新精灵
    all_sprites.update()

    # 检测碰撞
    collisions = pygame.sprite.spritecollide(player, enemies, True)
    if collisions:
        print("碰撞了!游戏结束!")
        running = False

    # 渲染画面
    screen.fill(WHITE)
    all_sprites.draw(screen)

    # 更新屏幕
    pygame.display.flip()

    # 控制帧率
    clock.tick(60)

# 退出游戏
pygame.quit()
sys.exit()

代码解释:

  1. 初始化 Pygame:调用 pygame.init() 初始化所有 Pygame 模块
  2. 创建窗口:设置窗口大小为 800x600
  3. 定义精灵类
    • Player 类:表示玩家,可通过方向键控制左右移动
    • Enemy 类:表示敌人,从屏幕上方随机位置向下移动
  4. 创建精灵组
    • all_sprites:包含所有精灵的组
    • enemies:仅包含敌人精灵的组
  5. 游戏主循环
    • 处理事件:处理窗口关闭和 ESC 键退出
    • 更新精灵:调用所有精灵的 update() 方法
    • 碰撞检测:检测玩家与敌人的碰撞
    • 渲染画面:绘制所有精灵
    • 控制帧率:限制帧率为 60 FPS

8.6 高级精灵技术

8.6.1 精灵图像的优化

对于大型游戏,精灵图像的加载和管理可能会成为性能瓶颈。以下是一些优化技巧:

  1. 使用精灵表:将多个精灵图像合并到一个大图像中,减少文件数量和加载时间
  2. 使用 Surface.convert():将图像转换为与显示格式匹配的格式,提高渲染速度
  3. 使用 Surface.convert_alpha():对于有透明通道的图像,使用此方法提高渲染速度

8.6.2 精灵的层次管理

在复杂游戏中,精灵的绘制顺序很重要。可以使用多个精灵组来管理不同层次的精灵。

# 创建不同层次的精灵组
background_sprites = pygame.sprite.Group()
midground_sprites = pygame.sprite.Group()
foreground_sprites = pygame.sprite.Group()

# 渲染时按层次绘制
background_sprites.draw(screen)
midground_sprites.draw(screen)
foreground_sprites.draw(screen)

8.6.3 精灵的物理属性

为精灵添加物理属性,如重力、碰撞响应等,可以使游戏更加真实。

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # ... 其他初始化代码 ...
        # 物理属性
        self.gravity = 0.5
        self.jump_power = -10
        self.velocity_y = 0
        self.on_ground = False

    def update(self):
        # 应用重力
        self.velocity_y += self.gravity
        self.rect.y += self.velocity_y

        # 地面碰撞检测
        if self.rect.bottom >= ground_level:
            self.rect.bottom = ground_level
            self.velocity_y = 0
            self.on_ground = True
        else:
            self.on_ground = False

        # 跳跃
        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE] and self.on_ground:
            self.velocity_y = self.jump_power

8.7 小结

本章介绍了 Pygame 中的精灵系统,包括如何创建精灵、使用精灵组管理精灵、实现精灵动画等。通过学习这些内容,你可以更有效地管理游戏对象,创建更加复杂和有趣的游戏。

在下一章中,我们将学习 Pygame 的用户输入处理功能,包括如何处理键盘、鼠标和游戏控制器的输入。


第9章:用户输入处理

9.1 输入处理基础

用户输入是游戏与玩家交互的重要方式。Pygame 提供了多种方法来处理用户输入,包括键盘、鼠标和游戏控制器的输入。

9.1.1 输入事件

Pygame 使用事件系统来处理用户输入。当用户按下键盘、移动鼠标或操作游戏控制器时,Pygame 会生成相应的事件。

9.2 键盘输入

键盘输入是最常用的输入方式之一,用于控制游戏角色的移动、选择菜单选项等。

9.2.1 事件驱动的键盘输入

通过事件循环处理键盘事件:

for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_SPACE:
            print("空格键被按下")
        elif event.key == pygame.K_ESCAPE:
            print("ESC键被按下")
    elif event.type == pygame.KEYUP:
        if event.key == pygame.K_SPACE:
            print("空格键被释放")

9.2.2 状态驱动的键盘输入

使用 pygame.key.get_pressed() 获取键盘的当前状态:

keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
    print("向左移动")
if keys[pygame.K_RIGHT]:
    print("向右移动")
if keys[pygame.K_UP]:
    print("向上移动")
if keys[pygame.K_DOWN]:
    print("向下移动")

9.2.3 键盘常量

Pygame 定义了许多键盘常量,用于表示不同的按键:

  • pygame.K_apygame.K_z:字母键
  • pygame.K_0pygame.K_9:数字键
  • pygame.K_SPACE:空格键
  • pygame.K_ESCAPE:ESC键
  • pygame.K_RETURN:回车键
  • pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT, pygame.K_RIGHT:方向键

9.3 鼠标输入

鼠标输入用于选择、点击和拖动游戏对象。

9.3.1 鼠标事件

通过事件循环处理鼠标事件:

for event in pygame.event.get():
    if event.type == pygame.MOUSEBUTTONDOWN:
        if event.button == 1:  # 左键
            print(f"鼠标左键在位置 {event.pos} 被按下")
        elif event.button == 3:  # 右键
            print(f"鼠标右键在位置 {event.pos} 被按下")
    elif event.type == pygame.MOUSEBUTTONUP:
        print(f"鼠标在位置 {event.pos} 被释放")
    elif event.type == pygame.MOUSEMOTION:
        print(f"鼠标移动到位置 {event.pos}")

9.3.2 鼠标状态

使用 pygame.mouse 模块获取鼠标的当前状态:

# 获取鼠标位置
mouse_pos = pygame.mouse.get_pos()
print(f"鼠标当前位置:{mouse_pos}")

# 获取鼠标按钮状态
mouse_buttons = pygame.mouse.get_pressed()
if mouse_buttons[0]:  # 左键
    print("鼠标左键被按下")
if mouse_buttons[1]:  # 中键
    print("鼠标中键被按下")
if mouse_buttons[2]:  # 右键
    print("鼠标右键被按下")

9.3.3 鼠标光标

控制鼠标光标的显示和隐藏:

# 隐藏鼠标光标
pygame.mouse.set_visible(False)

# 显示鼠标光标
pygame.mouse.set_visible(True)

# 设置自定义鼠标光标
# 首先创建一个表面作为光标
cursor_surface = pygame.Surface((32, 32))
cursor_surface.fill((255, 255, 255))
pygame.draw.circle(cursor_surface, (0, 0, 0), (16, 16), 10)
# 设置光标,第二个参数是热点位置
pygame.mouse.set_cursor((32, 32), (16, 16), *pygame.cursors.compile(pygame.cursors.arrow))

9.4 游戏控制器输入

Pygame 支持游戏控制器(如 Xbox 控制器、PlayStation 控制器等)的输入。

9.4.1 初始化游戏控制器

# 初始化 joystick 模块
pygame.joystick.init()

# 获取控制器数量
joystick_count = pygame.joystick.get_count()
print(f"发现 {joystick_count} 个控制器")

# 初始化第一个控制器
if joystick_count > 0:
    joystick = pygame.joystick.Joystick(0)
    joystick.init()
    print(f"控制器名称:{joystick.get_name()}")
    print(f"按钮数量:{joystick.get_numbuttons()}")
    print(f"轴数量:{joystick.get_numaxes()}")

9.4.2 处理控制器事件

for event in pygame.event.get():
    if event.type == pygame.JOYBUTTONDOWN:
        print(f"按钮 {event.button} 被按下")
    elif event.type == pygame.JOYBUTTONUP:
        print(f"按钮 {event.button} 被释放")
    elif event.type == pygame.JOYAXISMOTION:
        print(f"轴 {event.axis} 移动到 {event.value}")

9.4.3 获取控制器状态

# 获取按钮状态
for i in range(joystick.get_numbuttons()):
    if joystick.get_button(i):
        print(f"按钮 {i} 被按下")

# 获取轴状态
for i in range(joystick.get_numaxes()):
    value = joystick.get_axis(i)
    if abs(value) > 0.1:  # 死区
        print(f"轴 {i} 值:{value}")

9.5 综合示例:输入处理演示

下面是一个综合示例,展示了如何处理键盘、鼠标和游戏控制器的输入:

示例代码:

# 导入 pygame 库
import pygame
# 导入 sys 模块
import sys

# 初始化 pygame
pygame.init()

# 初始化 joystick 模块
pygame.joystick.init()

# 创建窗口
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("输入处理演示")

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

# 定义颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# 初始化控制器
joystick = None
joystick_count = pygame.joystick.get_count()
if joystick_count > 0:
    joystick = pygame.joystick.Joystick(0)
    joystick.init()

# 游戏主循环
running = True
while running:
    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False
            elif event.key == pygame.K_SPACE:
                print("空格键被按下")
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                print(f"鼠标左键在位置 {event.pos} 被按下")
        elif event.type == pygame.JOYBUTTONDOWN:
            print(f"控制器按钮 {event.button} 被按下")

    # 获取键盘状态
    keys = pygame.key.get_pressed()

    # 获取鼠标状态
    mouse_pos = pygame.mouse.get_pos()
    mouse_buttons = pygame.mouse.get_pressed()

    # 渲染画面
    screen.fill(WHITE)

    # 显示键盘状态
    # 使用支持中文的字体
    font = pygame.font.SysFont("SimHei", 24)
    text = font.render("键盘输入:", True, BLACK)
    screen.blit(text, (10, 10))

    if keys[pygame.K_LEFT]:
        text = font.render("向左箭头键", True, BLUE)
        screen.blit(text, (10, 40))
    if keys[pygame.K_RIGHT]:
        text = font.render("向右箭头键", True, BLUE)
        screen.blit(text, (150, 40))
    if keys[pygame.K_UP]:
        text = font.render("向上箭头键", True, BLUE)
        screen.blit(text, (10, 70))
    if keys[pygame.K_DOWN]:
        text = font.render("向下箭头键", True, BLUE)
        screen.blit(text, (150, 70))

    # 显示鼠标状态
    text = font.render("鼠标输入:", True, BLACK)
    screen.blit(text, (10, 120))

    text = font.render(f"鼠标位置:{mouse_pos}", True, BLACK)
    screen.blit(text, (10, 150))

    if mouse_buttons[0]:
        text = font.render("左键被按下", True, RED)
        screen.blit(text, (10, 180))
    if mouse_buttons[2]:
        text = font.render("右键被按下", True, RED)
        screen.blit(text, (150, 180))

    # 显示控制器状态
    text = font.render("控制器输入:", True, BLACK)
    screen.blit(text, (10, 230))

    if joystick:
        text = font.render(f"控制器名称:{joystick.get_name()}", True, BLACK)
        screen.blit(text, (10, 260))

        # 显示轴状态
        for i in range(joystick.get_numaxes()):
            value = joystick.get_axis(i)
            if abs(value) > 0.1:
                text = font.render(f"轴 {i}{value:.2f}", True, GREEN)
                screen.blit(text, (10, 290 + i * 30))

        # 显示按钮状态
        for i in range(joystick.get_numbuttons()):
            if joystick.get_button(i):
                text = font.render(f"按钮 {i} 被按下", True, GREEN)
                screen.blit(text, (200, 290 + i * 30))
    else:
        text = font.render("未检测到控制器", True, BLACK)
        screen.blit(text, (10, 260))

    # 更新屏幕
    pygame.display.flip()

    # 控制帧率
    clock.tick(60)

# 退出游戏
pygame.quit()
sys.exit()

代码解释:

  1. 初始化 Pygame:调用 pygame.init() 初始化所有 Pygame 模块
  2. 初始化 joystick 模块:用于处理游戏控制器输入
  3. 创建窗口:设置窗口大小为 800x600
  4. 初始化控制器:检测并初始化第一个游戏控制器
  5. 游戏主循环
    • 处理事件:处理窗口关闭、键盘、鼠标和控制器事件
    • 获取输入状态:获取键盘和鼠标的当前状态
    • 渲染画面:显示各种输入状态的信息
    • 控制帧率:限制帧率为 60 FPS

9.6 高级输入处理技术

9.6.1 输入映射

在复杂游戏中,通常需要将输入映射到游戏动作,以便玩家可以自定义按键。

# 输入映射字典
key_map = {
    "move_left": pygame.K_LEFT,
    "move_right": pygame.K_RIGHT,
    "jump": pygame.K_SPACE,
    "attack": pygame.K_z
}

# 使用输入映射
keys = pygame.key.get_pressed()
if keys[key_map["move_left"]]:
    player.move_left()
if keys[key_map["move_right"]]:
    player.move_right()
if keys[key_map["jump"]]:
    player.jump()
if keys[key_map["attack"]]:
    player.attack()

9.6.2 输入缓冲

使用输入缓冲来处理连续输入,如快速按键:

# 输入缓冲
input_buffer = []

# 处理键盘事件
for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        input_buffer.append(event.key)

# 处理输入缓冲
while input_buffer:
    key = input_buffer.pop(0)
    if key == pygame.K_SPACE:
        player.jump()
    elif key == pygame.K_z:
        player.attack()

9.6.3 输入状态管理

使用状态机来管理输入状态,如按键的按下、按住和释放状态:

class InputState:
    def __init__(self):
        self.keys = {}

    def update(self):
        # 获取当前键盘状态
        current_keys = pygame.key.get_pressed()

        # 更新按键状态
        for key in [pygame.K_SPACE, pygame.K_z, pygame.K_x]:
            if key not in self.keys:
                self.keys[key] = {"pressed": False, "held": False, "released": False}

            # 按键被按下
            if current_keys[key] and not self.keys[key]["held"]:
                self.keys[key]["pressed"] = True
                self.keys[key]["held"] = True
                self.keys[key]["released"] = False
            # 按键被释放
            elif not current_keys[key] and self.keys[key]["held"]:
                self.keys[key]["pressed"] = False
                self.keys[key]["held"] = False
                self.keys[key]["released"] = True
            # 按键保持按下
            elif current_keys[key] and self.keys[key]["held"]:
                self.keys[key]["pressed"] = False
                self.keys[key]["held"] = True
                self.keys[key]["released"] = False
            # 按键保持释放
            else:
                self.keys[key]["pressed"] = False
                self.keys[key]["held"] = False
                self.keys[key]["released"] = False

# 使用输入状态
input_state = InputState()

while running:
    input_state.update()

    if input_state.keys[pygame.K_SPACE]["pressed"]:
        print("空格键被按下")
    elif input_state.keys[pygame.K_SPACE]["held"]:
        print("空格键被按住")
    elif input_state.keys[pygame.K_SPACE]["released"]:
        print("空格键被释放")

9.7 小结

本章介绍了 Pygame 中的用户输入处理功能,包括键盘、鼠标和游戏控制器的输入处理方法。通过学习这些内容,你可以创建更加交互性强的游戏,让玩家能够通过各种输入设备与游戏进行互动。

至此,我们已经完成了 Pygame 的基本教程。通过学习这些内容,你应该已经掌握了 Pygame 的核心功能,可以开始创建自己的游戏了。祝你游戏开发愉快!

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_24330181/article/details/158730709

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--