【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 的较新版本兼容良好。
-
检查 Python 版本:
打开您的命令行终端(Windows 上的 CMD 或 PowerShell,macOS/Linux 上的 Terminal),输入:python --version# 或者python3 --version
如果显示了 Python 3.x.x (例如 Python 3.8.5 或更高版本),则表示 Python 已安装。
-
安装 Python (如果未安装):
如果您的系统中没有 Python,或者版本过低,请访问 Python 官方网站 (https://www.python.org/downloads/
) 下载适合您操作系统的最新稳定版本。在安装过程中,请务必勾选 “Add Python to PATH” (或类似选项),这样可以将 Python添加到系统环境变量中,方便在命令行中直接调用。
1.3 Pygame 安装
Pygame 的安装通常使用 Python 的包管理器 pip 来完成。
-
打开命令行终端。
-
使用 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 组件。
-
验证 Pygame 安装:
安装完成后,您可以通过几种方式验证 Pygame 是否成功安装。-
方法一:通过 pip 查看
在命令行输入:pip show pygame
如果安装成功,会显示 Pygame 的相关信息,如版本号、安装路径等。
-
方法二:通过 Python 解释器导入
打开 Python 解释器 (在命令行输入python
或python3
并回车),然后尝试导入 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 游戏循环包含以下几个关键阶段:
- 事件处理 (Event Handling):检查并响应用户输入(键盘、鼠标点击等)和其他系统事件(如窗口关闭)。
- 游戏逻辑更新 (Game Logic Update):根据用户输入和当前游戏状态,更新游戏世界中的对象(如角色位置、分数、敌人行为等)。
- 渲染画面 (Rendering):将更新后的游戏世界绘制到屏幕上。
- 控制帧率 (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()
vspygame.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
表示退出请求)。- 其他属性:根据事件类型的不同,事件对象还会包含与该事件相关的其他信息。例如:
- 对于
KEYDOWN
和KEYUP
事件:key
: 被按下或释放的键的标识符 (例如pygame.K_SPACE
代表空格键,pygame.K_a
代表 ‘a’ 键)。mod
: 修饰键的状态 (例如pygame.KMOD_SHIFT
表示 Shift 键被按下)。unicode
: 按键产生的 unicode 字符 (对于KEYDOWN
事件)。scancode
: 物理按键的扫描码。
- 对于
MOUSEBUTTONDOWN
和MOUSEBUTTONUP
事件: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
来创建更多自定义事件。这对于定时器触发的动作或模块间的通信非常有用。
-
事件处理的几种方式:
-
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 # 也退出循环
-
pygame.event.poll()
: 从队列中获取单个事件。如果队列为空,它会返回一个类型为pygame.NOEVENT
的“空”事件对象。这在某些特定情况下可能有用,但通常pygame.event.get()
更方便。event = pygame.event.poll() # 获取单个事件if event.type == pygame.QUIT: # 检查事件类型 running = False # 设置退出标志# 注意:poll() 只获取一个事件,如果一帧内有多个事件,需要多次调用或在一个循环内调用
-
pygame.event.wait()
: 从队列中获取单个事件。如果队列为空,它会等待直到有事件发生。这会导致程序阻塞,直到有事件为止,因此在典型的游戏循环中不常用,因为它会阻止游戏逻辑的更新和渲染。但在某些工具或非实时应用中可能有用。 -
pygame.event.clear(type=None)
: 从事件队列中移除特定类型的事件,或者如果type
为None
,则移除所有事件。这在您想忽略某些类型的事件时可能有用,但要小心使用,因为它可能导致错过重要的用户输入。 -
事件过滤与控制:
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 (屏幕): 通过
-
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
,此时会使用Rect
的topleft
坐标。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 (SRCALPHA
和convert_alpha()
) 通常更好。get_colorkey()
: 获取当前设置的颜色键。copy()
: 创建并返回该 Surface 的一个全新副本。subsurface(rect)
: 返回一个新的 Surface,它引用了父 Surface 中由rect
指定的区域的像素数据。对子 Surface 的修改会影响父 Surface,反之亦然。这对于从一个大的雪碧图中提取小块图像非常高效,因为它不复制像素数据。- 像素操作:
get_at((x, y))
: 返回指定坐标处像素的Color
对象。set_at((x, y), color)
: 设置指定坐标处像素的颜色。- 注意:
get_at
和set_at
非常慢,不应在游戏循环中频繁调用以进行大量的像素操作。对于高性能的像素级操作,应使用pygame.PixelArray
对象或pygame.surfarray
模块(后者与 NumPy 集成)。 lock()
和unlock()
: 在直接访问 Surface 的像素数据之前(例如通过PixelArray
或surfarray
),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。
- 软件 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
对象有很多方便的属性,用于获取和设置其位置和尺寸。当您修改其中一个位置属性(如centerx
或right
)时,其他相关的位置属性(如x
,left
)会自动更新,反之亦然。这使得移动和对齐Rect
非常方便。-
位置属性:
x
,y
: 矩形左上角的 x 和 y 坐标。与left
和top
相同。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
: 矩形的宽度和高度。与width
和height
相同。width
: 矩形的宽度。height
: 矩形的高度。size
: 一个元组(width, height)
。
重要:
Rect
的坐标和尺寸值总是整数。如果将浮点数赋给它们,它们会被截断为整数。这对于像素对齐的图形是必要的。如果需要更精确的浮点数位置,您需要自己维护浮点坐标,并在绘制或碰撞检测时将其转换为整数Rect
坐标。 -
-
Rect 的方法:
copy()
: 返回Rect
的一个新副本。因为Rect
对象是可变的,有时需要副本以避免意外修改。move(x, y)
: 返回一个新的Rect
,该Rect
按给定的x
和y
偏移量移动。原Rect
不变。move_ip(x, y)
: “in-place” 移动。直接修改Rect
自身的位置,不返回新的Rect
对象。通常这个更常用,因为它直接更新对象状态。inflate(x, y)
: 返回一个新的Rect
,其尺寸围绕中心点进行缩放。x
和y
是添加到宽度和高度上的值(可以是负数以缩小)。原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)
: 返回一个能完全包含当前Rect
和rect_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,那么flip
和update
的底层行为可能都是基于 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 可以通过多种方式处理颜色:
- RGB 元组:
(R, G, B)
,例如(255, 0, 0)
代表纯红色。 - RGBA 元组:
(R, G, B, A)
,例如(0, 255, 0, 128)
代表半透明的绿色。 pygame.Color
对象: 一个专门用于表示和操作颜色的对象。它提供了更丰富的功能和便利性。- HTML 颜色名称字符串: 如
\"red\"
,\"blue\"
,\"lightgreen\"
。 - 十六进制颜色字符串: 如
\"#FF0000\"
(红色),\"0x00FF00\"
(绿色)。 - 整数: 可以是一个打包的 32 位整数值 (例如,由
Surface.map_rgb()
返回的)。
-
pygame.Color
对象:
pygame.Color
是pygame.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 值
- HSLA (Hue, Saturation, Lightness, Alpha): 色相、饱和度、亮度、透明度。
-
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