> 技术文档 > 【Python】Pygame从零开始学习_pygame教程

【Python】Pygame从零开始学习_pygame教程


模块一:Pygame入门与核心基础

本模块将引导您完成 Pygame 的安装,并深入理解 Pygame 应用程序的基石——游戏循环、事件处理、Surface 与 Rect 对象、显示控制以及颜色管理。

第一章:Pygame 概览与环境搭建

1.1 什么是 Pygame?

Pygame 是一组专为编写视频游戏而设计的 Python 模块。它构建在优秀的 SDL (Simple DirectMedia Layer) 库之上,允许您使用 Python 语言创建功能齐全的游戏和多媒体应用程序。SDL 库本身提供了跨平台的底层硬件访问,包括音频、键盘、鼠标、游戏杆和图形硬件(通过 OpenGL/DirectX)。Pygame 通过 Python 封装了这些功能,使得游戏开发更加便捷和高效。

Pygame 的核心优势在于:

  • 易学性:对于有 Python 基础的开发者来说,Pygame 的 API 设计直观,上手相对容易。
  • 跨平台性:基于 SDL,Pygame 游戏可以轻松地在 Windows、macOS、Linux 等多种操作系统上运行。
  • 免费与开源:Pygame 是在 LGPL 许可下发布的,您可以免费使用它进行商业或非商业项目。
  • 社区支持:拥有庞大且活跃的社区,提供了丰富的教程、示例和第三方库。

虽然 Pygame 主要用于 2D 游戏开发,但通过结合其他库(如 PyOpenGL),也可以实现 3D 图形渲染。然而,本教程将主要聚焦于 Pygame 在 2D 游戏开发中的应用。

1.2 Python 环境准备 (简述)

在安装 Pygame 之前,您需要确保您的系统中已经安装了 Python。Pygame 通常与 Python 3 的较新版本兼容良好。

  1. 检查 Python 版本
    打开您的命令行终端(Windows 上的 CMD 或 PowerShell,macOS/Linux 上的 Terminal),输入:

    python --version# 或者python3 --version

    如果显示了 Python 3.x.x (例如 Python 3.8.5 或更高版本),则表示 Python 已安装。

  2. 安装 Python (如果未安装):
    如果您的系统中没有 Python,或者版本过低,请访问 Python 官方网站 (https://www.python.org/downloads/) 下载适合您操作系统的最新稳定版本。在安装过程中,请务必勾选 “Add Python to PATH” (或类似选项),这样可以将 Python添加到系统环境变量中,方便在命令行中直接调用。

1.3 Pygame 安装

Pygame 的安装通常使用 Python 的包管理器 pip 来完成。

  1. 打开命令行终端

  2. 使用 pip 安装 Pygame
    输入以下命令并执行:

    pip install pygame

    或者,如果您同时安装了 Python 2 和 Python 3,或者为了更明确指定,可以使用:

    python -m pip install pygame# 或者python3 -m pip install pygame

    pip 会自动从 PyPI (Python Package Index) 下载 Pygame 包并进行安装。安装过程可能需要一些时间,具体取决于您的网络速度。

    内部机制: pip install pygame 命令执行时,pip 会:

    • 连接到 PyPI(默认的 Python 包仓库)。
    • 搜索名为 pygame 的包。
    • 根据您的 Python 版本和操作系统,选择合适的 Pygame 发行版(通常是预编译的 wheel 文件,.whl)。这避免了在用户端编译 SDL 和 Pygame C 代码的复杂过程。
    • 下载选定的文件。
    • 将包内容解压并安装到 Python 环境的 site-packages 目录下。
    • 如果 Pygame 有依赖项(虽然 Pygame 本身对 Python 来说是独立的,但它依赖 SDL),pip 也会尝试解析和安装它们,不过 Pygame 的 wheel 文件通常包含了所需的 SDL 组件。
  3. 验证 Pygame 安装
    安装完成后,您可以通过几种方式验证 Pygame 是否成功安装。

    • 方法一:通过 pip 查看
      在命令行输入:

      pip show pygame

      如果安装成功,会显示 Pygame 的相关信息,如版本号、安装路径等。

    • 方法二:通过 Python 解释器导入
      打开 Python 解释器 (在命令行输入 pythonpython3 并回车),然后尝试导入 Pygame:

      import pygame # 导入 pygame 模块print(pygame.ver) # 打印 pygame 的版本号pygame.quit() # 退出 pygame(虽然这里还没初始化,但好习惯)exit() # 退出 Python 解释器

      如果导入成功并且打印出版本号(例如 ‘2.1.2’),则说明 Pygame 已正确安装。

    • 方法三:运行 Pygame 内置示例 (推荐)
      Pygame 自带了一些示例游戏,可以用来测试其功能。在命令行中运行:

      python -m pygame.examples.aliens

      如果一切正常,您应该会看到一个名为 “Aliens” 的小游戏窗口运行起来。您可以玩一下这个游戏,然后关闭窗口。这不仅验证了 Pygame 的安装,还确认了其图形和声音子系统能正常工作。
      其他可用示例包括:

      python -m pygame.examples.stars # 星空效果python -m pygame.examples.chimp # 大猩猩捶打的经典示例python -m pygame.examples.fonty # 字体渲染示例
1.4 遇到安装问题怎么办?

尽管 Pygame 的安装通常很顺利,但偶尔也可能遇到问题。以下是一些常见问题及其可能的解决方案:

  • pip 命令未找到
    • 原因:Python 或 pip 未正确添加到系统环境变量 PATH 中。
    • 解决:重新安装 Python,确保勾选 “Add Python to PATH”。或者手动将 Python 的 Scripts 目录 (其中包含 pip.exe) 和 Python 的安装目录添加到 PATH 环境变量。
  • SSL 错误 (SSL: CERTIFICATE_VERIFY_FAILED)
    • 原因:网络环境问题或 Python 的 SSL 证书配置问题,导致无法安全连接到 PyPI。
    • 解决
      • 尝试使用 --trusted-host 参数信任 PyPI 的主机:
        pip install pygame --trusted-host pypi.org --trusted-host files.pythonhosted.org
      • 检查您的网络代理设置。
      • 在某些情况下,更新 pip 和 setuptools 可能有帮助:
        python -m pip install --upgrade pip setuptools
  • 编译错误 (如果从源码安装或找不到合适的 wheel)
    • 原因:您的系统缺少编译 Pygame (及其依赖 SDL) 所需的开发库和编译器。
    • 解决:通常不推荐普通用户从源码编译。首选方案是确保 pip 版本较新,能够下载预编译的 wheel 文件。如果必须从源码编译,您需要根据 Pygame 官方文档安装所有依赖的开发库(如 SDL2, SDL2_image, SDL2_mixer, SDL2_ttf, SDL2_gfx 等的开发包)。这在不同操作系统上步骤不同,通常较为复杂。
  • 版本不兼容
    • 原因:安装的 Pygame 版本与您的 Python 版本不兼容。
    • 解决:Pygame 2.x 系列主要支持 Python 3。确保您的 Python 版本是最新的 Python 3 版本。如果遇到特定版本的兼容性问题,可以尝试安装特定版本的 Pygame:
      pip install pygame==2.1.0 # 安装指定版本 2.1.0

      通常,最新稳定版是最佳选择。

如果遇到上述未列出的问题,建议将完整的错误信息复制下来,在搜索引擎(如 Google、Bing)或 Pygame 社区论坛中搜索解决方案。通常,您遇到的问题其他人也可能遇到过。

第二章:Pygame 的核心概念

在开始编写第一个 Pygame 程序之前,理解其核心概念至关重要。这些概念构成了所有 Pygame 应用程序的基础。

2.1 Pygame 初始化与退出

任何 Pygame 程序都需要在使用 Pygame 的功能之前进行初始化,并在程序结束时正确退出。

  • pygame.init():
    这个函数会初始化所有导入的 Pygame 模块(如 display, font, mixer 等)。它会检查每个模块的依赖关系,并尝试初始化它们。如果某个模块初始化失败,它通常不会引发异常,但可能会返回一个包含成功和失败初始化模块数量的元组。
    内部机制: pygame.init() 实际上是迭代调用 Pygame 内部注册的各个子模块的 init() 函数。例如,它会尝试调用 pygame.display.init()pygame.font.init() 等。如果某个模块之前被显式地 quit() 过,init() 可能会重新初始化它。这是一个“尽力而为”的函数,旨在简化启动过程。

  • pygame.quit():
    这个函数是 pygame.init() 的反操作。它会卸载所有先前已初始化的 Pygame 模块。在 Python 程序退出前调用此函数是一个好习惯,以确保所有 Pygame 资源(如窗口、音频设备)被妥善释放。
    内部机制: 与 init() 类似,pygame.quit() 会调用各个子模块的 quit() 函数,例如 pygame.display.quit()pygame.font.quit()

  • 特定模块的初始化与退出:
    除了全局的 pygame.init()pygame.quit(),您也可以单独初始化或退出特定的 Pygame 模块。例如:

    • pygame.font.init() / pygame.font.quit()
    • pygame.mixer.init() / pygame.mixer.quit()
      这样做的好处是,如果您的应用程序不需要所有 Pygame 功能(例如,一个不需要声音的游戏),您可以只初始化所需的模块,从而可能减少启动时间和资源占用。然而,对于大多数初学者和中小型项目,直接使用 pygame.init()pygame.quit() 是最简单和推荐的方式。

    获取模块初始化状态:
    可以使用 pygame..get_init() 来检查特定模块是否已初始化。例如,pygame.font.get_init() 如果字体模块已初始化则返回 True,否则返回 False

代码示例:基本的 Pygame 程序结构

import pygame # 导入 pygame 库# 尝试初始化 Pygame 的所有模块# pygame.init() 返回一个元组 (n_success, n_fail),表示成功和失败初始化的模块数量# 我们通常不直接使用这个返回值,但了解它是有用的num_successes, num_failures = pygame.init() # 初始化所有导入的 pygame 模块print(f\"成功初始化 { num_successes} 个模块, 失败 { num_failures} 个模块\") # 打印初始化结果# 检查特定模块的初始化状态if pygame.display.get_init(): # 检查显示模块是否已初始化 print(\"显示模块已成功初始化!\") # 如果已初始化,则打印信息else: print(\"显示模块初始化失败!\") # 如果未初始化,则打印信息# 游戏主逻辑将在这里 (目前为空)# ...pygame.quit() # 卸载所有 pygame 模块,释放资源print(\"Pygame 已退出\") # 打印退出信息# Python 程序本身的退出 (可选,脚本结束时会自动退出)# import sys# sys.exit()
2.2 游戏循环 (Game Loop)

游戏循环是任何交互式游戏或应用程序的核心。它是一个持续运行的循环,负责处理用户输入、更新游戏状态以及在屏幕上渲染游戏画面。一个典型的 Pygame 游戏循环包含以下几个关键阶段:

  1. 事件处理 (Event Handling):检查并响应用户输入(键盘、鼠标点击等)和其他系统事件(如窗口关闭)。
  2. 游戏逻辑更新 (Game Logic Update):根据用户输入和当前游戏状态,更新游戏世界中的对象(如角色位置、分数、敌人行为等)。
  3. 渲染画面 (Rendering):将更新后的游戏世界绘制到屏幕上。
  4. 控制帧率 (Frame Rate Control):确保游戏以稳定的速度运行,不过快也不过慢。

基本游戏循环结构

import pygame # 导入 pygame 库# 1. 初始化 Pygamepygame.init() # 初始化所有 pygame 模块# 2. 设置显示窗口screen_width = 800 # 设置屏幕宽度为 800 像素screen_height = 600 # 设置屏幕高度为 600 像素# 创建一个屏幕对象 (Surface),尺寸为 screen_width x screen_heightscreen = pygame.display.set_mode((screen_width, screen_height))pygame.display.set_caption(\"我的第一个 Pygame 窗口\") # 设置窗口标题# 3. 游戏循环控制变量running = True # 布尔变量,控制游戏循环是否继续# 4. 游戏循环开始while running: # 当 running 为 True 时,循环继续 # 5. 事件处理层 for event in pygame.event.get(): # 遍历 Pygame 事件队列中的所有事件 if event.type == pygame.QUIT: # 如果事件类型是 QUIT (用户点击了关闭按钮) running = False # 将 running 设置为 False,以便退出循环 # 6. 游戏逻辑更新层 (目前为空,后续章节会填充) # 例如: player.update(), enemy.update() # ... # 7. 渲染层 screen.fill((0, 0, 0)) # 用黑色填充整个屏幕 (R, G, B) # 在这里绘制游戏元素 (目前为空,后续章节会填充) # 例如: screen.blit(player_image, player_rect) # ... pygame.display.flip() # 更新整个屏幕的内容到显示器# 8. 游戏循环结束,退出 Pygamepygame.quit() # 卸载所有 pygame 模块# sys.exit() # 确保程序完全退出 (如果需要)

游戏循环的内部机制和重要性

  • 持续性:游戏循环必须持续运行,才能使游戏看起来是动态和响应式的。如果循环停止,游戏将冻结。
  • 帧 (Frame):循环的每一次完整迭代通常被称为一“帧”。游戏画面的流畅度通常用“帧率”(FPS - Frames Per Second)来衡量。
  • pygame.event.get():此函数从 Pygame 内部的事件队列中获取所有待处理的事件。这是一个关键步骤,因为如果不处理事件,操作系统可能会认为您的应用程序没有响应。特别是 pygame.QUIT 事件,它在用户尝试关闭窗口时产生。
  • screen.fill():在每一帧的开始,通常会用背景色填充整个屏幕。这是为了清除上一帧绘制的内容,否则新的绘制会叠加在旧的上面,产生拖影效果。
  • pygame.display.flip() vs pygame.display.update():
    • pygame.display.flip(): 更新整个屏幕的内容。如果您的游戏使用了硬件加速和双缓冲(Pygame 默认情况下可能会尝试使用),flip() 会交换前后缓冲区。双缓冲是一种图形技术,后台缓冲区用于绘制新帧,然后一次性交换到前台显示,以避免屏幕撕裂和闪烁。
    • pygame.display.update(): 更加灵活。如果无参数调用 pygame.display.update(),其行为类似于 pygame.display.flip()(在软件显示模式下)。但是,您可以传递一个或多个 Rect 对象(或一个 Rect 列表)给 update(),这样 Pygame 就只会更新屏幕上指定的这些矩形区域。这对于优化性能非常有用,特别是当屏幕上只有小部分内容发生变化时(称为“脏矩形”渲染)。
      内部机制: flip() 通常与 SDL 的 SDL_RenderPresent() (如果使用 Renderer) 或 SDL_GL_SwapWindow() (如果使用 OpenGL) 或 SDL_UpdateWindowSurface() (如果使用软件表面) 相关联。update() 允许更细致的控制,对应于 SDL_UpdateWindowSurfaceRects()。现代 Pygame (2.x) 更多地依赖于 SDL2 的渲染后端,这使得 flip() 的行为更加一致和高效。
2.3 事件 (Events)

事件是 Pygame 中用于与用户和系统交互的核心机制。当用户执行操作(如按下键盘按键、移动鼠标、点击鼠标按钮)或发生系统级事件(如窗口关闭请求、窗口大小改变、游戏手柄连接/断开)时,Pygame 会将这些事件放入一个内部队列中。您的游戏循环需要定期从这个队列中检索并处理这些事件。

  • 事件队列 (Event Queue):Pygame 在内部维护一个先进先出 (FIFO) 的事件队列。pygame.event.get() 函数会从队列中取出所有当前事件,并返回一个包含这些事件对象的列表。取出后,这些事件会从队列中移除。

  • 事件对象 (Event Object):队列中的每个事件都是一个 pygame.event.Event 类型的对象。这个对象至少有两个重要的属性:

    • type: 一个整数,表示事件的类型(例如 pygame.KEYDOWN 表示键盘按键按下,pygame.QUIT 表示退出请求)。
    • 其他属性:根据事件类型的不同,事件对象还会包含与该事件相关的其他信息。例如:
      • 对于 KEYDOWNKEYUP 事件:
        • key: 被按下或释放的键的标识符 (例如 pygame.K_SPACE 代表空格键, pygame.K_a 代表 ‘a’ 键)。
        • mod: 修饰键的状态 (例如 pygame.KMOD_SHIFT 表示 Shift 键被按下)。
        • unicode: 按键产生的 unicode 字符 (对于 KEYDOWN 事件)。
        • scancode: 物理按键的扫描码。
      • 对于 MOUSEBUTTONDOWNMOUSEBUTTONUP 事件:
        • pos: 一个元组 (x, y),表示鼠标事件发生时的光标位置。
        • button: 整数,表示哪个鼠标按钮被按下或释放 (1=左键, 2=中键, 3=右键, 4=滚轮向上, 5=滚轮向下)。
      • 对于 MOUSEMOTION 事件:
        • pos: 一个元组 (x, y),表示鼠标当前的光标位置。
        • rel: 一个元组 (dx, dy),表示相对于上次 MOUSEMOTION 事件的移动量。
        • buttons: 一个元组 (left, middle, right),表示鼠标按钮的当前状态 (1 表示按下, 0 表示未按下)。
  • 常见的事件类型
    Pygame 定义了许多事件类型,所有这些类型都以常量形式存在于 pygame 模块中。一些最常用的事件类型包括:

    • QUIT: 用户点击窗口关闭按钮或系统请求关闭。
    • ACTIVEEVENT: 当 Pygame 显示窗口获得或失去输入焦点,或者窗口被图标化/恢复时。
      • gain: 如果为 1,表示获得焦点或取消图标化;如果为 0,表示失去焦点或图标化。
      • state: 指示事件的具体类型(例如,APPINPUTFOCUS 表示输入焦点)。
    • KEYDOWN: 键盘按键按下。
    • KEYUP: 键盘按键释放。
    • MOUSEMOTION: 鼠标移动。
    • MOUSEBUTTONDOWN: 鼠标按钮按下。
    • MOUSEBUTTONUP: 鼠标按钮释放。
    • JOYAXISMOTION: 游戏手柄的轴移动 (例如摇杆)。
    • JOYBALLMOTION: 游戏手柄的轨迹球移动。
    • JOYHATMOTION: 游戏手柄的方向键 (POV hat) 移动。
    • JOYBUTTONDOWN: 游戏手柄的按钮按下。
    • JOYBUTTONUP: 游戏手柄的按钮释放。
    • VIDEORESIZE: 当显示窗口大小被用户改变时 (需要设置窗口为可调整大小 pygame.RESIZABLE)。
      • size: 新的窗口尺寸 (width, height)
      • w, h: 新的宽度和高度。
    • VIDEOEXPOSE: 当部分窗口需要重绘时 (通常由操作系统触发)。
    • USEREVENT: Pygame 允许您定义自定义事件类型。pygame.USEREVENT 是第一个可用的自定义事件 ID,您可以使用 pygame.USEREVENT + n 来创建更多自定义事件。这对于定时器触发的动作或模块间的通信非常有用。
  • 事件处理的几种方式

    1. pygame.event.get(): 获取所有事件并清空队列。这是最常用的方法。

      for event in pygame.event.get(): # 遍历事件列表 if event.type == pygame.QUIT: # 检查是否是退出事件 running = False # 设置 running 为 False 以退出循环 elif event.type == pygame.KEYDOWN: # 检查是否是键盘按下事件 if event.key == pygame.K_ESCAPE: # 如果按下的是 ESC 键 running = False # 也退出循环
    2. pygame.event.poll(): 从队列中获取单个事件。如果队列为空,它会返回一个类型为 pygame.NOEVENT 的“空”事件对象。这在某些特定情况下可能有用,但通常 pygame.event.get() 更方便。

      event = pygame.event.poll() # 获取单个事件if event.type == pygame.QUIT: # 检查事件类型 running = False # 设置退出标志# 注意:poll() 只获取一个事件,如果一帧内有多个事件,需要多次调用或在一个循环内调用
    3. pygame.event.wait(): 从队列中获取单个事件。如果队列为空,它会等待直到有事件发生。这会导致程序阻塞,直到有事件为止,因此在典型的游戏循环中不常用,因为它会阻止游戏逻辑的更新和渲染。但在某些工具或非实时应用中可能有用。

    4. pygame.event.clear(type=None): 从事件队列中移除特定类型的事件,或者如果 typeNone,则移除所有事件。这在您想忽略某些类型的事件时可能有用,但要小心使用,因为它可能导致错过重要的用户输入。

    5. 事件过滤与控制

      • pygame.event.set_blocked(type_or_types): 阻止指定类型的一个或多个事件进入事件队列。
      • pygame.event.set_allowed(type_or_types): 允许先前被阻止的指定类型的一个或多个事件进入事件队列。
      • pygame.event.get_blocked(type): 检查特定事件类型是否被阻止。
      • pygame.key.set_repeat(delay, interval): 控制按键重复事件。当一个键被按住时,KEYDOWN 事件会首先在 delay 毫秒后产生,然后每隔 interval 毫秒重复产生。调用 pygame.key.set_repeat() (无参数) 或 pygame.key.set_repeat(0, 0) 可以禁用按键重复。
      • pygame.mouse.set_visible(boolean): 设置鼠标光标是否可见。

代码示例:扩展事件处理

import pygame # 导入 pygame 模块import sys # 导入 sys 模块,用于退出程序# 1. 初始化pygame.init() # 初始化 pygame# 2. 显示设置screen_width = 800 # 屏幕宽度screen_height = 600 # 屏幕高度screen = pygame.display.set_mode((screen_width, screen_height)) # 创建屏幕对象pygame.display.set_caption(\"事件处理演示\") # 设置窗口标题# 定义颜色 (后续章节会详细讲解)BLACK = (0, 0, 0) # 定义黑色WHITE = (255, 255, 255) # 定义白色RED = (255, 0, 0) # 定义红色GREEN = (0, 255, 0) # 定义绿色BLUE = (0, 0, 255) # 定义蓝色# 游戏状态变量current_bg_color = WHITE # 当前背景颜色,默认为白色message = \"按 R, G, B 键改变背景颜色, ESC 退出\" # 显示的消息# 字体设置 (后续章节会详细讲解)try: font = pygame.font.Font(None, 36) # 使用默认字体,字号 36 # 或者指定字体文件: font = pygame.font.Font(\"your_font.ttf\", 36)except pygame.error as e: # 捕获可能的字体错误 print(f\"加载字体失败: { e}\") # 打印错误信息 font = pygame.font.Font(pygame.font.get_default_font(), 36) # 加载一个绝对默认的字体# 3. 游戏循环running = True # 循环控制标志clock = pygame.time.Clock() # 创建一个 Clock 对象用于控制帧率while running: # 主循环开始 # 4. 事件处理 for event in pygame.event.get(): # 遍历事件队列 if event.type == pygame.QUIT: # 如果是退出事件 running = False # 设置 running 为 False,准备退出循环 if event.type == pygame.KEYDOWN: # 如果是键盘按键按下事件 print(f\"按键按下: key={ event.key}, unicode=\'{ event.unicode}\', mod={ event.mod}, scancode={ event.scancode}\") # 打印按键信息 if event.key == pygame.K_ESCAPE: # 如果按下的是 ESC 键 running = False # 设置 running 为 False elif event.key == pygame.K_r: # 如果按下的是 R 键 current_bg_color = RED # 将背景颜色设置为红色 message = \"背景色: 红色\" # 更新消息 elif event.key == pygame.K_g: # 如果按下的是 G 键 current_bg_color = GREEN # 将背景颜色设置为绿色 message = \"背景色: 绿色\" # 更新消息 elif event.key == pygame.K_b: # 如果按下的是 B 键 current_bg_color = BLUE # 将背景颜色设置为蓝色 message = \"背景色: 蓝色\" # 更新消息 if event.type == pygame.KEYUP: # 如果是键盘按键释放事件 print(f\"按键释放: key={ event.key}\") # 打印释放的按键信息 if event.type == pygame.MOUSEBUTTONDOWN: # 如果是鼠标按钮按下事件 # event.button: 1-左键, 2-中键, 3-右键, 4-滚轮上, 5-滚轮下 print(f\"鼠标按钮按下: button={ event.button}, pos={ event.pos}\") # 打印鼠标按钮和位置信息 message = f\"鼠标在 { event.pos} 按下按钮 { event.button}\" # 更新消息 if event.type == pygame.MOUSEBUTTONUP: # 如果是鼠标按钮释放事件 print(f\"鼠标按钮释放: button={ event.button}, pos={ event.pos}\") # 打印鼠标按钮和位置信息 message = f\"鼠标在 { event.pos} 释放按钮 { event.button}\" # 更新消息 if event.type == pygame.MOUSEMOTION: # 如果是鼠标移动事件 # event.buttons: (左键状态, 中键状态, 右键状态) - 1 为按下, 0 为未按下 # event.pos: 当前鼠标位置 (x, y) # event.rel: 相对上次移动 (dx, dy) # 我们通常只在需要拖拽或持续绘制时才频繁处理 MOUSEMOTION # print(f\"鼠标移动: pos={event.pos}, rel={event.rel}, buttons={event.buttons}\") # 打印鼠标移动信息 (会产生大量输出) pass # 暂时不处理详细的鼠标移动,避免过多打印 # 5. 游戏逻辑更新 (本示例中无复杂逻辑) # ... # 6. 渲染 screen.fill(current_bg_color) # 用当前背景色填充屏幕 # 渲染文本消息 if font: # 确保字体对象有效 text_surface = font.render(message, True, BLACK if current_bg_color != BLACK else WHITE) # 创建文本 Surface,抗锯齿,颜色为黑色 (如果背景不是黑色) 或白色 text_rect = text_surface.get_rect(center=(screen_width // 2, screen_height // 2)) # 获取文本的 Rect 对象,并将其居中 screen.blit(text_surface, text_rect) # 将文本 Surface 绘制到屏幕上 pygame.display.flip() # 更新整个屏幕 # 7. 控制帧率 clock.tick(60) # 尝试将游戏帧率维持在每秒 60 帧# 8. 退出pygame.quit() # 卸载 Pygame 模块print(\"Pygame 演示已退出\") # 打印退出消息sys.exit() # 确保程序完全退出

内部机制 - 事件队列和SDL: Pygame 的事件系统是基于 SDL 的事件处理机制。SDL 负责从操作系统捕获底层的输入事件(键盘、鼠标、窗口事件等),并将它们放入一个内部队列。pygame.event.get() 等函数实际上是在查询和操作这个由 SDL 管理的队列。这意味着 Pygame 的事件响应能力和类型直接受到 SDL 支持的限制,但 SDL 本身是一个非常成熟和跨平台的库,因此 Pygame 的事件系统功能强大且可靠。

2.4 Surface 对象

Surface 是 Pygame 中用于表示图像和屏幕的最核心对象。你可以把 Surface 理解为一个空白的画布或一张图片,你可以在上面绘制形状、加载图片、写入文字等。屏幕本身也是一个特殊的 Surface 对象。

  • 创建 Surface:

    • 主显示 Surface (屏幕): 通过 pygame.display.set_mode((width, height), flags=0, depth=0) 创建。这是唯一一个直接显示在用户屏幕上的 Surface
      • flags: 可选参数,用于指定额外的显示特性,如:
        • pygame.FULLSCREEN: 创建全屏显示。
        • pygame.DOUBLEBUF: 启用双缓冲(通常推荐,可以减少闪烁)。现代 Pygame 通常会自动处理好这个。
        • pygame.HWSURFACE: 尝试在显存中创建 Surface (硬件加速)。
        • pygame.SRCALPHA: 表示 Surface 可以拥有每像素的 alpha 透明度。这对于创建带有透明背景的图像(如 PNG)或进行 alpha 混合非常重要。
        • pygame.RESIZABLE: 创建一个用户可以调整大小的窗口。
      • depth: 像素的色深(位数)。通常让 Pygame 自动选择最佳色深 (设为 0)。
    • 普通 Surface (内存中的图像): 通过 pygame.Surface((width, height), flags=0, depth=0) 或从 pygame.image.load(\"filename.png\") 加载图像来创建。这些 Surface 存在于内存中,需要通过“blit”操作绘制到其他 Surface (通常是屏幕 Surface) 上才能显示。
      • flags: 同样可以使用 SRCALPHA 来创建带 alpha 通道的 Surface。
      • depth: 可以指定色深,或者从另一个 Surface (如屏幕 Surface) 获取以确保格式兼容。
  • Surface 的属性与方法:

    • get_width(): 返回 Surface 的宽度 (像素)。
    • get_height(): 返回 Surface 的高度 (像素)。
    • get_size(): 返回一个元组 (width, height)
    • get_rect(**kwargs): 返回一个覆盖整个 Surface 的 pygame.Rect 对象。这个 Rect 对象的 topleft 默认为 (0, 0),但可以通过关键字参数 (如 center=(x,y), topright=(x,y)) 来改变其位置。这非常有用,因为 Rect 对象常用于定位和碰撞检测。
    • fill(color, rect=None, special_flags=0): 用指定的颜色填充 Surface。可以提供一个可选的 rect 参数来只填充 Surface 的一部分。special_flags 可用于混合模式 (如 pygame.BLEND_ADD)。
    • blit(source_surface, dest_pos, area=None, special_flags=0): 这是将一个 Surface (源 source_surface) 绘制(“blit”是 Block Image Transfer 的缩写)到另一个 Surface (目标,即调用此方法的 Surface) 上的核心方法。
      • source_surface: 要绘制的源图像。
      • dest_pos: 一个 (x, y) 坐标元组,表示源 Surface 的左上角在目标 Surface 上的绘制位置。也可以是一个 Rect,此时会使用 Recttopleft 坐标。
      • area: 一个可选的 Rect 对象,指定源 Surface 中的哪个部分被绘制。如果为 None,则绘制整个源 Surface。这对于雪碧图动画非常有用。
      • special_flags: 可用于混合模式,如 pygame.BLEND_RGBA_ADD, pygame.BLEND_RGBA_MULT 等,实现特殊的视觉效果。
    • convert(): 返回一个新的 Surface 副本,但其像素格式已转换为与显示器最匹配的格式。这是一个非常重要的优化。当您加载图像(如 PNG, JPG)后,立即对其调用 convert() (如果图像没有 alpha 透明度) 或 convert_alpha() (如果图像有 alpha 透明度),可以显著提高后续 blit 操作的速度。因为如果源和目标 Surface 的像素格式不同,Pygame 在每次 blit 时都需要进行昂贵的实时转换。
    • convert_alpha(): 类似于 convert(),但会保留原 Surface 的 alpha 透明度信息,并确保新 Surface 的像素格式也支持 alpha。对于需要透明效果的图像(如 PNG 文件),应使用此方法。
    • set_alpha(value, flags=0): 设置整个 Surface 的 alpha 透明度(0=完全透明, 255=完全不透明)。这会影响该 Surface 如何与其他 Surface 混合。需要 Surface 本身支持 alpha (例如创建时使用 SRCALPHA 标志,或者是一个 convert_alpha() 后的 Surface)。
    • get_alpha(): 获取整个 Surface 的 alpha 值。
    • set_colorkey(color, flags=0): 设置颜色键透明。指定一种颜色,当此 Surface 被 blit 到其他 Surface 时,所有匹配该颜色的像素都将变为透明。color 可以是 None 来移除颜色键。这是一个较旧的透明度处理方式,对于需要每像素 alpha 的复杂透明效果,使用带有 alpha 通道的 Surface (SRCALPHAconvert_alpha()) 通常更好。
    • get_colorkey(): 获取当前设置的颜色键。
    • copy(): 创建并返回该 Surface 的一个全新副本。
    • subsurface(rect): 返回一个新的 Surface,它引用了父 Surface 中由 rect 指定的区域的像素数据。对子 Surface 的修改会影响父 Surface,反之亦然。这对于从一个大的雪碧图中提取小块图像非常高效,因为它不复制像素数据。
    • 像素操作:
      • get_at((x, y)): 返回指定坐标处像素的 Color 对象。
      • set_at((x, y), color): 设置指定坐标处像素的颜色。
      • 注意: get_atset_at 非常慢,不应在游戏循环中频繁调用以进行大量的像素操作。对于高性能的像素级操作,应使用 pygame.PixelArray 对象或 pygame.surfarray 模块(后者与 NumPy 集成)。
      • lock()unlock(): 在直接访问 Surface 的像素数据之前(例如通过 PixelArraysurfarray),Surface 可能需要被锁定。这可以防止操作系统在您操作像素时移动或修改 Surface。操作完成后需要解锁。Pygame 通常会自动处理锁定/解锁,但在某些高级情况下需要手动控制。
  • Surface 的内部机制:

    • 像素数据: Surface 对象的核心是其内部存储的一块内存,其中包含了所有像素的颜色信息。这些数据的组织方式(像素格式)可能因 Surface 的创建方式、色深和是否有 alpha 通道而异。例如,一个 24 位 RGB Surface 的每个像素用 3 字节表示,而一个 32 位 RGBA Surface 的每个像素用 4 字节表示。
    • 软件 Surface vs 硬件 Surface:
      • 软件 Surface (pygame.SWSURFACE): 像素数据存储在系统内存 (RAM) 中。所有的绘制操作由 CPU 完成。
      • 硬件 Surface (pygame.HWSURFACE): Pygame 会尝试将像素数据存储在显存 (VRAM) 中。某些绘制操作(尤其是 blit)可能会由显卡硬件加速,从而提高性能。然而,并非所有操作都能在硬件 Surface 上得到加速,并且硬件 Surface 的行为可能因显卡驱动和操作系统而异。直接像素访问(如 set_at)到硬件 Surface 可能非常慢,因为数据可能需要从显存传回系统内存。
      • 在现代 Pygame (基于 SDL2) 中,HWSURFACE 的概念与 SDL 的 Texture 和 Renderer 更相关。pygame.display.set_mode() 创建的屏幕 Surface 通常会尝试利用硬件加速。普通内存中的 Surface 对象通常是软件 Surface。
    • convert()convert_alpha() 的重要性: 当你 blit 一个 Surface 到另一个 Surface 时,如果它们的像素格式不匹配,Pygame 必须在 CPU 上逐像素转换格式。这个过程非常耗时。convert() 将 Surface 转换为与主显示 Surface 最兼容的格式(通常是屏幕的自然格式),这样后续的 blit 操作就变成了直接的内存复制,速度快得多。convert_alpha() 做同样的事情,但保留了 alpha 通道。因此,加载图像后立即转换它们是 Pygame 中的一个关键性能优化技巧。

代码示例:使用 Surface 和 Blitting

import pygame # 导入 pygame 模块import sys # 导入 sys 模块# 1. 初始化pygame.init() # 初始化 pygame# 2. 显示设置screen_width = 800 # 屏幕宽度screen_height = 600 # 屏幕高度# 创建主显示 Surface (屏幕),启用 SRCALPHA 以便更好地处理透明度screen = pygame.display.set_mode((screen_width, screen_height)) #, pygame.SRCALPHA)pygame.display.set_caption(\"Surface 和 Blit 演示\") # 设置窗口标题# 3. 定义颜色BLACK = (0, 0, 0) # 黑色WHITE = (255, 255, 255) # 白色RED = (255, 0, 0) # 红色GREEN = (0, 255, 100) # 绿色,稍亮的绿色BLUE = (0, 0, 255) # 蓝色YELLOW_TRANSPARENT = (255, 255, 0, 128) # 黄色,半透明 (RGBA)# 4. 创建一些 Surface 对象# 4.1 一个红色的矩形 Surfacesurface_red = pygame.Surface((100, 80)) # 创建一个 100x80 像素的 Surfacesurface_red.fill(RED) # 用红色填充这个 Surface# 4.2 一个带有 Alpha 透明度的绿色圆形 Surface# 为了使用每像素 alpha,Surface 需要以 SRCALPHA 标志创建surface_green_circle = pygame.Surface((150, 150), pygame.SRCALPHA) # 创建支持 alpha 的 Surfacesurface_green_circle.fill((0,0,0,0)) # 用完全透明填充背景 (R,G,B,A)# 在这个 surface_green_circle 上绘制一个半透明绿色圆# pygame.draw.circle(surface, color, center, radius, width=0)# width=0 表示填充圆形, color 可以是 (R,G,B) 或 (R,G,B,A)pygame.draw.circle(surface_green_circle, (0, 255, 0, 180), (75, 75), 70) # 绘制一个绿色半透明圆# 4.3 加载一张图片 (假设你有一张名为 \"player.png\" 的图片在脚本同目录下)# 为确保示例可独立运行,我们创建一个简单的 Surface 代替加载图片try: # 尝试加载图片 - 如果你有图片,取消下面这行的注释 # player_image_original = pygame.image.load(\"player.png\") # 加载图片 # 为了演示,我们创建一个替代 Surface player_image_original = pygame.Surface((60, 90), pygame.SRCALPHA) # 创建一个带 alpha 通道的 Surface player_image_original.fill((0,0,0,0)) # 完全透明背景 pygame.draw.rect(player_image_original, BLUE, (10, 10, 40, 70)) # 在上面画一个蓝色矩形代表玩家 pygame.draw.circle(player_image_original, YELLOW_TRANSPARENT, (30,20), 15) # 画一个黄色半透明头部 # 关键优化:转换图像格式以匹配屏幕格式 # 如果 player.png 有 alpha 通道,使用 convert_alpha() player_image = player_image_original.convert_alpha() # 转换格式以优化 blitting 速度,保留 alpha # 如果 player.png 没有 alpha 通道 (例如 JPG),使用 convert() # player_image = player_image_original.convert() player_rect = player_image.get_rect(center=(screen_width // 2, screen_height // 2)) # 获取图像的 rect 并居中except pygame.error as e: # 捕获 pygame 错误,例如文件未找到 print(f\"加载或创建玩家图像失败: { e}\") # 打印错误 player_image = None # 将 player_image 设为 None player_rect = None # 将 player_rect 设为 None# 4.4 创建一个使用颜色键透明的 Surfacesurface_colorkey = pygame.Surface((120, 120)) # 创建 Surfacesurface_colorkey.fill(GREEN) # 用绿色填充pygame.draw.circle(surface_colorkey, BLUE, (60, 60), 50) # 在上面画一个蓝色圆surface_colorkey.set_colorkey(GREEN) # 将绿色设置为透明色# 注意: convert() 之后再 set_colorkey() 效果更好,因为它能确保颜色值精确匹配# surface_colorkey = surface_colorkey.convert()# surface_colorkey.set_colorkey(GREEN)# 5. 游戏循环running = True # 循环控制标志clock = pygame.time.Clock() # 时钟对象angle = 0 # 用于旋转的角度while running: # 主循环 # 6. 事件处理 for event in pygame.event.get(): # 遍历事件 if event.type == pygame.QUIT: # 如果是退出事件 running = False # 结束循环 if event.type == pygame.KEYDOWN: # 如果是按键按下事件 if event.key == pygame.K_ESCAPE: # 如果是 ESC 键 running = False # 结束循环 # 7. 游戏逻辑更新 if player_image and player_rect : # 如果玩家图像和矩形都存在 # 简单移动:让 \"玩家\" 图像左右来回移动 player_rect.x += 2 # 每次循环向右移动2像素 if player_rect.right > screen_width or player_rect.left < 0: # 如果碰到屏幕边缘 player_rect.x -=2 # 移回 # (这是一个非常基础的移动,可以改进为反向移动等) angle = (angle + 1) % 360 # 角度递增,用于旋转 # 8. 渲染 screen.fill(BLACK) # 用黑色填充屏幕背景 # Blit 第一个红色 Surface screen.blit(surface_red, (50, 50)) # 将 surface_red 绘制到屏幕的 (50,50) 位置 # Blit 带有 Alpha 透明度的绿色圆形 Surface # 它会与背景和其他已绘制的物体正确混合 screen.blit(surface_green_circle, (200, 50)) # 将 surface_green_circle 绘制到 (200,50) # Blit 玩家图像 (如果存在) if player_image and player_rect: # 检查图像是否存在 # 演示旋转: pygame.transform.rotate(surface, angle) # 注意:旋转会创建一个新的 Surface,并且可能改变尺寸 # 频繁旋转未优化的 Surface 会影响性能 # rotated_player_image = pygame.transform.rotate(player_image, angle) # 旋转玩家图像 # rotated_rect = rotated_player_image.get_rect(center=player_rect.center) # 获取旋转后图像的新 rect,保持中心不变 # screen.blit(rotated_player_image, rotated_rect) # 绘制旋转后的图像 screen.blit(player_image, player_rect) # 绘制原始(但已 convert_alpha 的)玩家图像 # Blit 使用颜色键透明的 Surface screen.blit(surface_colorkey, (50, 200)) # 将 surface_colorkey 绘制到 (50,200) # 演示 Subsurface: 从屏幕 Surface 创建一个子 Surface if screen_width > 200 and screen_height > 200: # 确保屏幕够大 sub = screen.subsurface(pygame.Rect(0, 0, 100, 100)) # 获取屏幕左上角 100x100 的区域作为子 Surface sub.fill(RED) # 用红色填充这个子 Surface 区域 (会直接修改屏幕对应区域) # 注意:直接修改屏幕的 subsurface 并不总是推荐的做法,通常 subsurface 用于从大图像中提取部分而不复制数据 pygame.display.flip() # 更新整个屏幕显示 # 9. 控制帧率 clock.tick(60) # 保持 60 FPS# 10. 退出pygame.quit() # 卸载 pygamesys.exit() # 退出程序
2.5 Rect 对象 (pygame.Rect)

pygame.Rect 对象用于存储和操作矩形区域。它在 Pygame 中无处不在,主要用于:

  • 表示 Surface 对象在屏幕上的位置和尺寸。
  • 进行碰撞检测。
  • 定义需要重绘的屏幕区域 (脏矩形)。
  • 存储任何需要矩形坐标和尺寸的实体(如游戏角色、障碍物、UI 元素等)。

Rect 对象并不包含任何像素数据;它仅仅是一组描述矩形位置和大小的数字。

  • 创建 Rect 对象:

    • my_rect = pygame.Rect(left, top, width, height): 这是最直接的创建方式。
      • left: 矩形左边缘的 x 坐标。
      • top: 矩形上边缘的 y 坐标。
      • width: 矩形的宽度。
      • height: 矩形的高度。
    • my_rect = pygame.Rect((left, top), (width, height)): 使用位置元组和尺寸元组。
    • my_rect = pygame.Rect(object): 可以传入任何具有 rect 属性的对象(如一个 Sprite 对象)。
    • my_rect = surface.get_rect(**kwargs): 从一个 Surface 对象获取其边界矩形。这是非常常用的方法。关键字参数 **kwargs 可以用来直接设置 Rect 的位置属性,例如:
      • player_rect = player_surface.get_rect(center=(screen_width/2, screen_height/2)) # 获取 Rect 并将其中心置于屏幕中心。
      • enemy_rect = enemy_surface.get_rect(topleft=(100, 50)) # 左上角在 (100, 50)。
      • 其他可用的位置属性作为关键字参数:x, y, top, left, bottom, right, midtop, midleft, midbottom, midright, centerx, centery
  • Rect 的虚拟属性 (Virtual Attributes):
    Rect 对象有很多方便的属性,用于获取和设置其位置和尺寸。当您修改其中一个位置属性(如 centerxright)时,其他相关的位置属性(如 x, left)会自动更新,反之亦然。这使得移动和对齐 Rect 非常方便。

    • 位置属性:

      • x, y: 矩形左上角的 x 和 y 坐标。与 lefttop 相同。
      • left: 矩形左边缘的 x 坐标。
      • right: 矩形右边缘的 x 坐标。 (left + width)
      • top: 矩形上边缘的 y 坐标。
      • bottom: 矩形下边缘的 y 坐标。 (top + height)
      • centerx: 矩形中心的 x 坐标。
      • centery: 矩形中心的 y 坐标。
      • topleft: 一个元组 (left, top)
      • bottomleft: 一个元组 (left, bottom)
      • topright: 一个元组 (right, top)
      • bottomright: 一个元组 (right, bottom)
      • midtop: 一个元组 (centerx, top)
      • midleft: 一个元组 (left, centery)
      • midbottom: 一个元组 (centerx, bottom)
      • midright: 一个元组 (right, centery)
      • center: 一个元组 (centerx, centery)
    • 尺寸属性:

      • w, h: 矩形的宽度和高度。与 widthheight 相同。
      • width: 矩形的宽度。
      • height: 矩形的高度。
      • size: 一个元组 (width, height)

    重要: Rect 的坐标和尺寸值总是整数。如果将浮点数赋给它们,它们会被截断为整数。这对于像素对齐的图形是必要的。如果需要更精确的浮点数位置,您需要自己维护浮点坐标,并在绘制或碰撞检测时将其转换为整数 Rect 坐标。

  • Rect 的方法:

    • copy(): 返回 Rect 的一个新副本。因为 Rect 对象是可变的,有时需要副本以避免意外修改。
    • move(x, y): 返回一个新的 Rect,该 Rect 按给定的 xy 偏移量移动。原 Rect 不变。
    • move_ip(x, y): “in-place” 移动。直接修改 Rect 自身的位置,不返回新的 Rect 对象。通常这个更常用,因为它直接更新对象状态。
    • inflate(x, y): 返回一个新的 Rect,其尺寸围绕中心点进行缩放。xy 是添加到宽度和高度上的值(可以是负数以缩小)。原 Rect 不变。
    • inflate_ip(x, y): “in-place” 缩放。直接修改 Rect 自身的尺寸。
    • clamp(Rect): 返回一个新的 Rect,该 Rect 被移动到完全包含在传入的 Rect 参数内部。如果原 Rect 比参数 Rect 大,则新 Rect 会被居中并裁剪。
    • clamp_ip(Rect): “in-place” 版本。
    • clip(Rect): 返回一个新的 Rect,表示当前 Rect 与参数 Rect 相交的部分。如果它们不相交,则返回一个宽度或高度为0的 Rect (通常是 (x, y, 0, 0))。
    • union(Rect): 返回一个能完全包含当前 Rect 和参数 Rect 的最小新 Rect
    • unionall(rect_sequence): 返回一个能完全包含当前 Rectrect_sequence 中所有 Rect 的最小新 Rect
    • fit(Rect): 返回一个新的 Rect,该 Rect 按比例缩放并移动,以适应参数 Rect 内部,同时保持其宽高比。
    • normalize(): 如果 Rect 的宽度或高度为负,则修正它(通过调整 left/top 并使 width/height 为正)。
    • 碰撞检测方法:
      • colliderect(Rect): 如果当前 Rect 与参数 Rect 有任何重叠,则返回 True,否则返回 False。这是最常用的矩形碰撞检测方法。
      • collidelist(list_of_rects): 检查当前 Rect 是否与 list_of_rects 中的任何一个 Rect 相撞。如果相撞,返回它在列表中的第一个碰撞 Rect 的索引。如果不与任何 Rect 相撞,则返回 -1
      • collidelistall(list_of_rects): 检查当前 Rect 是否与 list_of_rects 中的任何一个 Rect 相撞。返回一个包含所有碰撞 Rect 在列表中的索引的列表。如果无碰撞,返回空列表。
      • collidepoint((x, y))collidepoint(x, y): 如果给定的点 (x, y)Rect 内部(包括边界),则返回 True,否则返回 False
      • contains(Rect): 如果参数 Rect 完全被包含在当前 Rect 内部,则返回 True,否则返回 False

代码示例:使用 Rect 对象

import pygame # 导入 pygame 模块pygame.init() # 初始化 pygame (虽然在这个纯 Rect 示例中非必需,但好习惯)# 1. 创建 Rect 对象rect1 = pygame.Rect(10, 20, 100, 50) # left=10, top=20, width=100, height=50print(f\"Rect1: { rect1}\") # 打印 rect1 的信息,输出 rect2 = pygame.Rect((50, 60), (150, 70)) # 使用元组创建: ((left, top), (width, height))print(f\"Rect2: left={ rect2.left}, top={ rect2.top}, width={ rect2.width}, height={ rect2.height}\") # 访问属性# 2. 访问和修改 Rect 属性print(f\"Rect1.x = { rect1.x}, Rect1.y = { rect1.y}\") # 访问 x, y (左上角)print(f\"Rect1.center = { rect1.center}\") # 访问中心点 (centerx, centery)print(f\"Rect1.right = { rect1.right}, Rect1.bottom = { rect1.bottom}\") # 访问右边缘和下边缘rect1.centerx = 100 # 修改中心 x 坐标rect1.bottom = 200 # 修改下边缘 y 坐标print(f\"Rect1 修改后: { rect1}\") # 打印修改后的 rect1,其他相关属性会自动更新print(f\"Rect1 修改后.topleft = { rect1.topleft}\") # 检查 topleft 是否已更新# 3. Rect 的方法# 3.1 移动 Rectrect_orig = pygame.Rect(0, 0, 30, 30) # 创建原始矩形moved_rect = rect_orig.move(10, 15) # move() 返回一个新 Rect,原 Rect 不变print(f\"Rect_orig (移动前): { rect_orig}\") # 打印原始矩形print(f\"Moved_rect: { moved_rect}\") # 打印移动后的新矩形rect_orig.move_ip(5, -5) # move_ip() \"in-place\" 修改原 Rectprint(f\"Rect_orig (move_ip后): { rect_orig}\") # 打印就地移动后的原始矩形# 3.2 缩放 Rectrect_to_inflate = pygame.Rect(100, 100, 50, 50) # 创建待缩放矩形inflated_rect = rect_to_inflate.inflate(20, 10) # 宽度增加20 (两边各10), 高度增加10 (两边各5)print(f\"Rect_to_inflate (缩放前): { rect_to_inflate}\") # 打印原始矩形print(f\"Inflated_rect: { inflated_rect}\") # 打印缩放后的新矩形# 注意 inflate 的参数 x, y 是分别加到 width 和 height 上的总值# 矩形会从中心向外扩展 (x/2, y/2)rect_to_inflate.inflate_ip(-10, -20) # \"in-place\" 缩小print(f\"Rect_to_inflate (inflate_ip后): { rect_to_inflate}\") # 打印就地缩放后的原始矩形# 3.3 碰撞检测collider1 = pygame.Rect(0, 0, 50, 50) # 创建碰撞体1collider2 = pygame.Rect(40, 40, 50, 50) # 创建碰撞体2,与 collider1 部分重叠collider3 = pygame.Rect(100, 100, 20, 20) # 创建碰撞体3,不与 collider1 重叠if collider1.colliderect(collider2): # 检查 collider1 和 collider2 是否碰撞 print(\"Collider1 和 Collider2 发生碰撞!\") # 如果碰撞则打印else: print(\"Collider1 和 Collider2 未发生碰撞.\") # 如果未碰撞则打印if collider1.colliderect(collider3): # 检查 collider1 和 collider3 是否碰撞 print(\"Collider1 和 Collider3 发生碰撞!\") # 如果碰撞则打印else: print(\"Collider1 和 Collider3 未发生碰撞.\") # 如果未碰撞则打印point_inside = (25, 25) # 定义一个在 collider1 内部的点point_outside = (60, 60) # 定义一个在 collider1 外部的点if collider1.collidepoint(point_inside): # 检查点是否在 collider1 内部 print(f\"点 { point_inside} 在 Collider1 内部.\") # 如果在内部则打印else: print(f\"点 { point_inside} 不在 Collider1 内部.\") # 如果不在内部则打印if collider1.collidepoint(point_outside): # 检查点是否在 collider1 内部 print(f\"点 { point_outside} 在 Collider1 内部.\") # 如果在内部则打印else: print(f\"点 { point_outside} 不在 Collider1 内部.\") # 如果不在内部则打印# 3.4 包含检测container_rect = pygame.Rect(0, 0, 200, 200) # 定义一个容器矩形contained_rect = pygame.Rect(50, 50, 30, 30) # 定义一个完全在容器内的矩形partially_contained_rect = pygame.Rect(150, 150, 100, 100) # 定义一个部分在容器内的矩形if container_rect.contains(contained_rect): # 检查 contained_rect 是否完全在 container_rect 内部 print(\"Contained_rect 完全在 Container_rect 内部.\") # 如果是则打印else: print(\"Contained_rect 不完全在 Container_rect 内部.\") # 如果不是则打印if container_rect.contains(partially_contained_rect): # 检查 partially_contained_rect 是否完全在 container_rect 内部 print(\"Partially_contained_rect 完全在 Container_rect 内部.\") # 如果是则打印else: print(\"Partially_contained_rect 不完全在 Container_rect 内部.\") # 如果不是则打印# 3.5 Unionrect_a = pygame.Rect(0,0,20,20) # 定义矩形Arect_b = pygame.Rect(30,30,20,20) # 定义矩形Bunion_ab = rect_a.union(rect_b) # 计算A和B的并集矩形print(f\"Union of A { rect_a} and B { rect_b} is { union_ab}\") # 打印并集矩形# 4. Rect 与 Surface 结合# 通常,Surface 的位置由其 Rect 决定# example_surface = pygame.Surface((50, 50)) # 创建一个 Surface# example_rect = example_surface.get_rect(center=(300, 200)) # 获取 Surface 的 Rect 并设置其中心位置# 在游戏循环中绘制时: screen.blit(example_surface, example_rect)# example_rect.x += 5 # 移动 Rect,从而间接移动 Surface 的绘制位置pygame.quit() # 退出 pygame

Rect 的整数坐标: 一个非常重要的细节是 Rect 的所有坐标和尺寸值(如 x, y, width, height, centerx, bottomright 等)都是整数。如果您将一个浮点数赋给这些属性,Pygame 会自动将其截断(不是四舍五入)为整数。
例如:

my_rect = pygame.Rect(0,0,10,10) # 创建矩形my_rect.x = 10.7 # 设置 x 坐标为浮点数print(my_rect.x) # 输出将会是 10my_rect.centerx = 20.5 # 设置中心x坐标为浮点数print(my_rect.centerx) # 输出将会是 20 (因为 (left+right)/2 然后取整,或者 left + width/2 取整)

这种行为对于像素级精确的屏幕定位是必需的,因为屏幕像素坐标本身就是整数。然而,如果您在游戏逻辑中需要更平滑的移动或物理计算(例如,速度、加速度是浮点数),您应该在单独的浮点变量中存储精确的位置,然后在每一帧准备绘制或进行碰撞检测之前,将这些浮点位置转换为整数 Rect 坐标。

例如,一个简单的物体类可能这样处理:

class GameObject: def __init__(self, x_float, y_float, surface): self.x_float = x_float # 存储精确的浮点 x 坐标 self.y_float = y_float # 存储精确的浮点 y 坐标 self.surface = surface # 物体的图像 Surface self.rect = self.surface.get_rect() # 获取物体的 Rect self.update_rect_position() # 初始化 Rect 的位置 def update_rect_position(self): # 将浮点坐标转换为 Rect 的整数坐标 (例如,更新 topleft) self.rect.topleft = (int(self.x_float), int(self.y_float)) # 使用 int() 截断 # 或者,如果你想让 Rect 的中心点对应浮点坐标: # self.rect.center = (int(self.x_float), int(self.y_float)) def move(self, dx_float, dy_float): self.x_float += dx_float # 更新浮点坐标 self.y_float += dy_float # 更新浮点坐标 self.update_rect_position() # 根据新的浮点坐标更新 Rect def draw(self, screen_surface): screen_surface.blit(self.surface, self.rect) # 使用 Rect 进行绘制

这种模式允许您在后台进行平滑的浮点运算,同时确保与 Pygame 的整数坐标系统正确交互。

2.6 显示模块 (pygame.display)

pygame.display 模块用于控制 Pygame 程序的显示窗口或屏幕。它是创建可见游戏界面、管理窗口属性以及更新屏幕内容的核心。

  • 初始化显示模式:

    • pygame.display.set_mode(resolution=(width, height), flags=0, depth=0, display=0, vsync=0): 这是最重要的函数,用于创建一个用于显示的 Surface 对象(通常称为屏幕 Surface)。
      • resolution: 一个 (width, height) 元组,指定窗口的尺寸(以像素为单位)。
      • flags: 可选的附加标志,用于控制显示特性。一些常用的标志包括:
        • pygame.FULLSCREEN: 创建一个全屏显示。
        • pygame.RESIZABLE: 允许用户调整窗口大小。如果设置此标志,当用户调整窗口时,会产生 VIDEORESIZE 事件。
        • pygame.NOFRAME: 创建一个没有边框和标题栏的窗口。
        • pygame.OPENGL: 创建一个支持 OpenGL 渲染的窗口(用于 3D 图形)。
        • pygame.HWSURFACE: (已不推荐直接指定,Pygame 会尝试自动使用)尝试使用硬件加速的 Surface。
        • pygame.DOUBLEBUF: (已不推荐直接指定,Pygame 会尝试自动使用)启用双缓冲。
        • pygame.SCALED (Pygame 2.0+): 允许 Pygame 在高 DPI 显示器上进行逻辑大小的缩放,或者当窗口大小与请求的逻辑分辨率不同时进行缩放。
      • depth: 请求的色深(每个像素的位数)。通常设置为 0,让 Pygame 自动选择最佳或与当前桌面匹配的色深。
      • display: (高级) 如果系统有多个显示器,指定使用哪个显示器(索引从 0 开始)。
      • vsync: (Pygame 2.0.1+) 如果设置为 1,尝试启用垂直同步 (Vsync)。Vsync 可以减少屏幕撕裂,但可能会引入输入延迟,并将帧率限制为显示器的刷新率。
        返回值: 返回一个代表屏幕的 Surface 对象。所有绘制到这个 Surface 上的内容最终会显示出来。
        内部机制: 调用 set_mode 时,Pygame (通过 SDL) 会与操作系统的窗口管理器交互,请求创建一个具有指定属性的窗口。它还会分配必要的图形资源。如果请求硬件加速或双缓冲,SDL 会尝试配置这些特性。返回的 Surface 是特殊的,因为它的内容可以直接或间接映射到显卡帧缓冲区。
  • 更新显示内容:

    • pygame.display.flip(): 将整个屏幕 Surface 的内容更新到实际显示器上。如果使用了双缓冲,它会交换前后缓冲区。这是最简单直接的更新方式。
    • pygame.display.update(rectangle_or_rectangles=None): 更新屏幕的部分区域。
      • 如果不带参数调用 (pygame.display.update()),它的行为类似于 pygame.display.flip()(在软件模式下)或更新整个窗口。
      • 如果传递一个 Rect 对象或一个 Rect 对象列表,则只有这些指定的矩形区域会被更新到屏幕上。这对于优化性能非常有用,称为“脏矩形渲染”(Dirty rect animation),即只重绘屏幕上发生变化的部分。
        内部机制:
        flip() 通常对应于 SDL 的 SDL_RenderPresent() (使用 Renderer API 时) 或 SDL_GL_SwapWindow() (使用 OpenGL 时) 或 SDL_UpdateWindowSurface() (使用旧式 Surface API 时)。它隐含了整个屏幕内容的更新。
        update(rects) 对应于 SDL 的 SDL_UpdateWindowSurfaceRects(),允许更精细地控制哪些区域需要从后备存储(屏幕 Surface)复制到实际的显示硬件。在现代 SDL2 中,如果 Pygame 使用了 SDL_Renderer,那么 flipupdate 的底层行为可能都是基于 Renderer 的呈现,但 update(rects) 仍然可以作为一种提示,告诉渲染器哪些区域“脏了”。
  • 获取显示信息:

    • pygame.display.get_surface(): 返回当前显示模式下代表屏幕的 Surface 对象。这通常与 set_mode() 返回的 Surface 是同一个对象。
    • pygame.display.Info(): 返回一个 VideoInfo 对象,其中包含有关当前显示硬件和模式的详细信息,如硬件加速状态、窗口管理器信息、当前分辨率、色深等。
      info = pygame.display.Info() # 获取 VideoInfo 对象print(f\"硬件加速可用: {  info.hw}\") # 检查是否支持硬件 surfaceprint(f\"窗口系统: {  info.wm}\") # 检查窗口管理器信息print(f\"当前屏幕宽度: {  info.current_w}\") # 获取当前屏幕宽度print(f\"当前屏幕高度: {  info.current_h}\") # 获取当前屏幕高度print(f\"Bits per pixel: {  info.bitsize}\") # 获取每个像素的位数print(f\"Bytes per pixel: {  info.bytesize}\") # 获取每个像素的字节数
    • pygame.display.get_active(): 如果显示窗口当前是活动的(即拥有焦点),返回 True
    • pygame.display.get_init(): 如果 pygame.display 模块已初始化,返回 True
  • 窗口管理:

    • pygame.display.set_caption(title, icontitle=None): 设置窗口的标题栏文本。icontitle (可选) 用于设置窗口最小化时的图标标题 (并非所有系统都支持)。
    • pygame.display.get_caption(): 返回一个包含 (title, icontitle) 的元组。
    • pygame.display.set_icon(surface): 设置窗口的图标。需要传入一个小的 Surface 对象作为图标图像(通常是 32x32 像素)。图标 Surface 最好带有颜色键透明或 alpha 透明度。
    • pygame.display.iconify(): 最小化(图标化)游戏窗口。返回 True 如果成功。
    • pygame.display.toggle_fullscreen(): 在全屏和窗口模式之间切换。
    • pygame.display.get_window_size() (Pygame 2.0+): 获取当前窗口的实际尺寸。这在窗口可调整大小 (RESIZABLE) 或使用 SCALED 模式时特别有用,因为它可能与 set_mode 请求的逻辑尺寸不同。
    • pygame.display.get_desktop_sizes() (Pygame 2.0.1+): 返回一个列表的列表,每个子列表 [width, height] 表示一个可用显示器的桌面尺寸。
  • 高级显示模式查询:

    • pygame.display.list_modes(depth=0, flags=pygame.FULLSCREEN, display=0): 返回一个列表,其中包含在特定色深 (depth) 和标志 (flags) 下可用的显示分辨率 (width, height) 元组。如果 depth 为 0,则返回当前显示器支持的所有分辨率。如果列表为空,则表示没有可用的模式。如果返回 -1,则表示任何分辨率都可用(例如在窗口模式下)。这对于在全屏前检查支持的分辨率很有用。
    • pygame.display.mode_ok(size, flags=0, depth=0, display=0): 检查给定的尺寸 size=(width, height)、标志和色深是否是一个有效的显示模式。如果有效,返回最接近该模式的匹配色深。如果无效,返回 0

代码示例:pygame.display 模块的运用

import pygame # 导入 pygame 模块import sys # 导入 sys 模块# 1. 初始化 Pygamepygame.init() # 初始化所有 pygame 模块# 2. 查询显示能力print(\"查询可用的全屏模式 (默认色深):\") # 打印信息try: modes = pygame.display.list_modes(0, pygame.FULLSCREEN) # 获取所有可用的全屏模式 if not modes: # 如果没有模式可用 print(\" 没有找到可用的全屏模式。\") # 打印信息 elif modes == -1: # 如果任何分辨率都可用 print(\" 任何分辨率都可以在全屏模式下使用 (可能指窗口化全屏或桌面分辨率)。\") # 打印信息 else: for mode in modes[:5]: # 遍历前5个可用模式 (避免过多输出) print(f\" - { mode[0]}x{ mode[1]}\") # 打印模式的宽度和高度except pygame.error as e: # 捕获 pygame 错误 print(f\"查询 list_modes 失败: { e}\") # 打印错误信息# 检查一个特定模式是否可行target_mode = (1024, 768) # 目标模式if pygame.display.mode_ok(target_mode, pygame.FULLSCREEN): # 检查目标模式是否可用 (全屏) print(f\"模式 { target_mode} 在全屏下是可行的。\") # 打印信息else: print(f\"模式 { target_mode} 在全屏下可能不可行或不推荐。\") # 打印信息# 3. 设置显示模式screen_width = 800 # 屏幕宽度screen_height = 600 # 屏幕高度flags = pygame.RESIZABLE # 设置窗口可调整大小的标志# flags = 0 # 普通窗口# flags = pygame.FULLSCREEN # 全屏模式# flags = pygame.NOFRAME # 无边框窗口try: screen = pygame.display.set_mode((screen_width, screen_height), flags) # 创建屏幕对象,并设置标志 print(f\"成功设置显示模式: { screen.get_size()} flags={ flags}\") # 打印成功信息和屏幕尺寸except pygame.error as e: # 捕获 pygame 错误 print(f\"设置显示模式失败: { e}\") # 打印错误信息 pygame.quit() # 退出 pygame sys.exit() # 退出程序# 4. 设置窗口标题和图标pygame.display.set_caption(\"Pygame Display 演示\", \"Display Demo\") # 设置窗口标题和图标标题# 尝试设置图标 (需要一个 Surface 对象)try: # 创建一个简单的 32x32 Surface 作为图标 icon_surface = pygame.Surface((32, 32), pygame.SRCALPHA) # 创建带 alpha 通道的 Surface icon_surface.fill((0,0,0,0)) # 用完全透明填充 pygame.draw.circle(icon_surface, (255, 0, 0, 200), (16, 16), 14, 0) # 在上面画一个红色半透明圆 pygame.draw.circle(icon_surface, (255, 255, 255, 255), (16, 16), 8, 0) # 在上面画一个白色实心小圆 pygame.display.set_icon(icon_surface) # 设置窗口图标 print(\"窗口图标已设置。\") # 打印信息except pygame.error as e: # 捕获 pygame 错误 print(f\"设置图标失败: { e}\") # 打印错误信息except ImportError: # 有些环境可能缺少图像处理后端导致 Surface 创建失败 print(\"创建图标 Surface 失败,可能缺少依赖。\") # 打印信息# 5. 获取显示信息display_info = pygame.display.Info() # 获取显示信息对象print(\"\\n--- 显示信息 ---\") # 打印标题print(f\" 硬件加速 (HWSURFACE): { \'是\' if display_info.hw else \'否\'}\") # 打印硬件加速信息print(f\" 窗口管理器信息可用: { \'是\' if display_info.wm else \'否\'}\") # 打印窗口管理器信息print(f\" 视频内存 (MB,如果可用): { display_info.video_mem if hasattr(display_info, \'video_mem\') else \'N/A\'}\") # 打印视频内存信息print(f\" 当前窗口尺寸: { pygame.display.get_window_size() if hasattr(pygame.display, \'get_window_size\') else screen.get_size()}\") # 打印当前窗口尺寸print(f\" 屏幕 Surface: { pygame.display.get_surface()}\") # 打印屏幕 Surface 对象print(\"------------------\\n\") # 打印分隔线# 6. 游戏循环running = True # 循环控制标志clock = pygame.time.Clock() # 时钟对象bg_color_cycle = [(255,0,0), (0,255,0), (0,0,255)] # 背景颜色循环列表color_index = 0 # 当前颜色索引frame_count = 0 # 帧计数器rect_to_update_1 = pygame.Rect(50, 50, 100, 100) # 定义第一个更新区域rect_to_update_2 = pygame.Rect(200, 150, 150, 80) # 定义第二个更新区域dirty_rects = [] # 用于存储脏矩形的列表while running: # 主循环 dirty_rects.clear() # 每帧开始前清空脏矩形列表 # 事件处理 for event in pygame.event.get(): # 遍历事件 if event.type == pygame.QUIT: # 如果是退出事件 running = False # 结束循环 if event.type == pygame.KEYDOWN: # 如果是按键按下事件 if event.key == pygame.K_ESCAPE: # 如果是 ESC 键 running = False # 结束循环 if event.key == pygame.K_f: # 如果是 F 键 pygame.display.toggle_fullscreen() # 切换全屏/窗口模式 print(f\"切换全屏状态。当前窗口尺寸: { pygame.display.get_window_size() if hasattr(pygame.display, \'get_window_size\') else screen.get_size()}\") # 打印信息 if event.key == pygame.K_i: # 如果是 I 键 if pygame.display.get_active(): # 检查窗口是否活动  pygame.display.iconify() # 最小化窗口  print(\"窗口已最小化 (如果系统支持)。\") # 打印信息 if event.type == pygame.VIDEORESIZE: # 如果是窗口大小调整事件 (仅当设置 RESIZABLE 标志时) screen_width, screen_height = event.size # 获取新的窗口尺寸 screen = pygame.display.set_mode((screen_width, screen_height), flags) # 重新设置显示模式以适应新尺寸 print(f\"窗口大小已调整为: { event.size}, 新的 screen surface: { screen}\") # 打印信息 # 当窗口大小改变时,整个屏幕都需要重绘 dirty_rects.append(screen.get_rect()) # 将整个屏幕区域添加到脏矩形列表 # 游戏逻辑更新 frame_count += 1 # 帧计数器加1 if frame_count % 60 == 0: # 每 60 帧 (约1秒) 切换一次背景颜色 color_index = (color_index + 1) % len(bg_color_cycle) # 更新颜色索引 # 当背景颜色改变时,整个屏幕都需要重绘 dirty_rects.append(screen.get_rect()) # 添加整个屏幕到脏矩形 # 渲染 current_bg_color = bg_color_cycle[color_index] # 获取当前背景颜色 screen.fill(current_bg_color) # 用当前背景色填充屏幕 # 绘制一些东西来演示 update() pygame.draw.rect(screen, (255, 255, 0), rect_to_update_1) # 在区域1绘制黄色矩形 dirty_rects.append(rect_to_update_1.copy()) # 将区域1添加到脏矩形列表 (用copy避免后续修改影响) pygame.draw.ellipse(screen, (255, 0, 255), rect_to_update_2) # 在区域2绘制品红色椭圆 dirty_rects.append(rect_to_update_2.copy()) # 将区域2添加到脏矩形列表 # 更新显示 if frame_count % 120 < 60 : # 每两秒中的第一秒使用 flip() pygame.display.flip() # 更新整个屏幕 if frame_count % 120 == 1: print(\"使用 pygame.display.flip()\") # 首次使用时打印信息 else: # 每两秒中的第二秒使用 update() 和脏矩形 # 过滤掉不在屏幕内的脏矩形,并合并重叠的脏矩形(简化版,实际合并更复杂) valid_dirty_rects = [r for r in dirty_rects if screen.get_rect().contains(r) or screen.get_rect().colliderect(r)] if valid_dirty_rects: # 如果有有效的脏矩形 pygame.display.update(valid_dirty_rects) # 只更新脏矩形区域 else: # 如果没有特定脏矩形(例如窗口大小调整后整个屏幕是脏的) pygame.display.update() # 更新整个屏幕作为回退 if frame_count % 120 == 61: print(f\"使用 pygame.display.update() 更新区域: { valid_dirty_rects}\") # 首次使用时打印信息 # 控制帧率 clock.tick(60) # 保持 60 FPS# 7. 退出pygame.quit() # 卸载 pygame 模块print(\"Pygame display 演示已退出。\") # 打印退出信息sys.exit() # 退出程序
2.7 颜色 (pygame.Color)

在 Pygame 中,颜色通常由代表红 ®、绿 (G)、蓝 (B) 三原色分量的数值来定义。每个分量的范围通常是 0 到 255。有时还会包含第四个分量 Alpha (A),用于表示透明度(0 表示完全透明,255 表示完全不透明)。

Pygame 可以通过多种方式处理颜色:

  1. RGB 元组: (R, G, B),例如 (255, 0, 0) 代表纯红色。
  2. RGBA 元组: (R, G, B, A),例如 (0, 255, 0, 128) 代表半透明的绿色。
  3. pygame.Color 对象: 一个专门用于表示和操作颜色的对象。它提供了更丰富的功能和便利性。
  4. HTML 颜色名称字符串: 如 \"red\", \"blue\", \"lightgreen\"
  5. 十六进制颜色字符串: 如 \"#FF0000\" (红色), \"0x00FF00\" (绿色)。
  6. 整数: 可以是一个打包的 32 位整数值 (例如,由 Surface.map_rgb() 返回的)。
  • pygame.Color 对象:
    pygame.Colorpygame.color.Color 的别名。

    • 创建 Color 对象:

      • color_obj = pygame.Color(R, G, B, A=255): 例如 pygame.Color(255, 0, 0)pygame.Color(0, 255, 0, 100)
      • color_obj = pygame.Color(\"colorname\"): 例如 pygame.Color(\"magenta\")
      • color_obj = pygame.Color(\"#RRGGBBAA\")pygame.Color(\"#RRGGBB\"): 例如 pygame.Color(\"#FF00FF\") (洋红色), pygame.Color(\"#00FF0080\") (半透明绿色)。
      • color_obj = pygame.Color(packed_int): 使用整数表示。
    • Color 对象的属性:
      可以直接访问或修改其 RGBA 分量:

      • r: 红色分量 (0-255)。
      • g: 绿色分量 (0-255)。
      • b: 蓝色分量 (0-255)。
      • a: Alpha 透明度分量 (0-255)。
      my_color = pygame.Color(\"dodgerblue\") # 创建一个名为 \"dodgerblue\" 的颜色对象print(f\"初始颜色: R={  my_color.r}, G={  my_color.g}, B={  my_color.b}, A={  my_color.a}\") # 打印颜色分量my_color.r = 255 # 修改红色分量my_color.a = 128 # 修改 alpha 分量print(f\"修改后颜色: R={  my_color.r}, G={  my_color.g}, B={  my_color.b}, A={  my_color.a}\") # 再次打印
    • Color 对象的规范化属性:
      可以将颜色分量表示为 0.0 到 1.0 之间的浮点数:

      • normalize(): 返回一个 (r_float, g_float, b_float, a_float) 元组,其中每个值都在 0.0 到 1.0 之间。
      norm_rgba = my_color.normalize() # 获取规范化后的 RGBA 元组print(f\"规范化 RGBA: {  norm_rgba}\") # 打印规范化后的值
    • Color 对象的其他表示法:

      • HSLA (Hue, Saturation, Lightness, Alpha): 色相、饱和度、亮度、透明度。
        • hsla: 获取或设置 (h, s, l, a) 元组。h (0-359 degrees), s (0-100%), l (0-100%), a (0-100%).
      • HSVA (Hue, Saturation, Value, Alpha): 色相、饱和度、明度(亮度)、透明度。
        • hsva: 获取或设置 (h, s, v, a) 元组。h (0-359 degrees), s (0-100%), v (0-100%), a (0-100%).
      • CMY (Cyan, Magenta, Yellow): 青色、品红色、黄色(减色模型)。
        • cmy: 获取或设置 (c, m, y) 元组。值范围 0.0-1.0。
      • I1I2I3 (Luminance-Chrominance for NTSC):
        • i1i2i3: 获取或设置 (i1, i2, i3) 元组。值范围不固定。
      my_color = pygame.Color(255, 128, 64) # 创建一个颜色对象 (橙色)h, s, l, a = my_color.hsla # 获取 HSLA 值print(f\"HSLA: H={  h:.2f}, S={  s:.2f}%, L={  l:.2f}%, A={  a:.2f}%\") # 打印 HSLA 值my_color.hsla = ( (h + 30) % 360, s, l, a) # 改变色相 (Hue shift)print(f\"改变色相后 RGBA: ({  my_color.r}, {  my_color.g}, {  my_color.b}, {  my_color.a})\") # 打印改变色相后的 RGBA 值
    • Color 对象的方法:

      • correct_gamma(gamma): 返回一个新的 Color 对象,其颜色分量根据给定的 gamma 值进行了校正。
      • lerp(other_color, amount) (Pygame 2.0+): 返回一个新的 Color 对象,它是当前颜色和 other_color 之间的线性插值。amount 是一个 0.0 到 1.0 之间的浮点数,0.0 表示当前颜色,1.0 表示 other_color
  • 何时使用 pygame.Color 对象 vs 元组:

    • 元组: 对于简单的、静态的颜色定义,如 BLACK = (0,0,0),元组轻量且方便。Pygame 的大多数接受颜色的函数(如 screen.fill(), pygame.draw.rect())都可以直接使用元组。
    • pygame.Color 对象:
      • 当需要动态修改颜色分量(如改变亮度、饱和度、透明度)时。
      • 当需要使用不同的颜色模型 (HSLA, HSVA) 时。
      • 当需要从颜色名称字符串或十六进制字符串创建颜色时。
      • 当希望代码更具可读性,明确表示这是一个颜色实体时。
      • 进行颜色插值等高级操作时。
  • 颜色常量:
    Pygame 没有内置大量的颜色名称常量 (如 pygame.RED)。您需要自己定义它们,或者使用 pygame.Color(\"red\")。通常的做法是在代码开头定义常用的颜色元组:

    BLACK = (0, 0, 0) # 定义黑色WHITE = (255, 255, 255) # 定义白色RED = (255, 0, 0) # 定义红色GREEN = (0, 255, 0) # 定义绿色BLUE = (0, 0, 255) # 定义蓝色

代码示例:使用颜色

import pygame # 导入 pygame 模块import sys # 导入 sys 模块pygame.init() # 初始化 pygamescreen_width = 600 # 屏幕宽度screen_height = 400 # 屏幕高度screen = pygame.display.set_mode((screen_width, screen_height)) # 创建屏幕对象pygame.display.set_caption(\"Pygame 颜色演示\") # 设置窗口标题# 1. 定义颜色# 1.1 使用元组 (R, G, B)COLOR_BLACK = (0, 0, 0) # 定义黑色COLOR_WHITE = (255, 255, 255) # 定义白色COLOR_CRIMSON = (220, 20, 60) # 定义深红色 (Crimson)# 1.2 使用元组 (R, G, B, A) - 用于支持 alpha 的 SurfaceCOLOR_SEAGREEN_SEMITRANSPARENT = (46, 139, 87, 150) # 定义海绿色,半透明# 1.3 使用 pygame.Color 对象color_obj_gold = pygame.Color(\"gold\") # 从名称创建颜色对象 \"gold\"print(f\"Gold (从名称): R={ color_obj_gold.r}, G={ color_obj_gold.g}, B={ color_obj_gold.b}, A={ color_obj_gold.a}\") # 打印颜色分量color_obj_hex = pygame.Color(\"#8A2BE2\") # 从十六进制创建颜色对象 (BlueViolet)print(f\"BlueViolet (从HEX): R={ color_obj_hex.r}, G={ color_obj_hex.g}, B={ color_obj_hex.b}, A={ color_obj_hex.a}\") # 打印颜色分量color_obj_rgba = pygame.Color(100, 100, 255, 200) # 从 RGBA 值创建颜色对象print(f\"自定义 RGBA: R={ color_obj_rgba.r}, G={ color_obj_rgba.g}, B={ color_obj_rgba.b}, A={ color_obj_rgba.a}\") # 打印颜色分量# 修改 Color 对象的属性color_obj_mutable = pygame.Color(\"lightblue\") # 创建可变颜色对象 \"lightblue\"original_r = color_obj_mutable.r # 保存原始红色分量color_obj_mutable.r = 0 # 将红色分量设置为 0color_obj_mutable.a = 200 # 设置 alpha 值为 200 (更透明)# 使用 HSLAcolor_hsl_demo = pygame.Color(\"orange\") # 创建颜色对象 \"orange\"h_orig, s_orig, l_orig, a_orig = color_hsl_demo.hsla # 获取原始 HSLA 值print(f\"Orange HSLA: H={ h_orig