关于 pygame-core
面向使用 Python 开发或调试 2D 游戏的 pygame 基础技能,涵盖主循环、按时间缩放的移动、输入、绘制和精灵管理,目标为 pygame-ce 2.5.8。
主要功能
- 01搭建事件、更新与绘制循环
- 02使用 delta time 缩放移动
- 03处理事件式与轮询式输入
- 04使用 Surface 和 Rect 绘制
- 05管理精灵与组
- 06检测精灵及矩形碰撞
适用场景
- 01使用 pygame-ce 开始 Python 游戏
- 02修复移动速度随帧率变化的问题
- 03整理精灵并检测碰撞
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ gamedev-skills/ awesome-gamedev-agent-skills --skill pygame-core安装后在 Claude Code 中输入/pygame-core确认已加载。
附带文件references/sprites-and-collision.md
详细介绍
pygame Core
使用 Python 构建 pygame 游戏的基础:主循环、基于增量时间的移动、使用 Surface/Rect 绘制、输入处理,以及 Sprite/Group 管理。
目标版本为 pygame-ce 2.5.8(积极维护的社区分支;导入方式相同,均为 import pygame)。
适用场景
- 开始开发 pygame 游戏,或修复循环、帧率相关的速度问题、输入处理、blit 绘制,或精灵/组碰撞时使用。
- 代码使用
import pygame且项目依赖pygame-ce(或pygame)时使用。
不适用场景: 与 pygame 无关的 Python 语言问题。3D 渲染(pygame 是 2D 库)。跨引擎存档/读档请使用 save-systems;可重新绑定的输入架构请参阅 input-systems。
核心工作流
- 安装 pygame-ce,而非旧版 pygame。 使用
pip install pygame-ce——这是维护中的分支,导入名称为pygame。不要在同一环境中同时安装两者。 - 初始化并打开窗口。 调用
pygame.init(),设置screen = pygame.display.set_mode((w, h)),并创建clock = pygame.time.Clock()。 - 运行单一循环:事件 → 更新 → 绘制 → 翻转。 每帧处理事件队列(
for event in pygame.event.get()),更新状态,重新绘制,然后调用pygame.display.flip()。 - 实现与帧率无关的移动。 获取
dt = clock.tick(60) / 1000(秒),并用dt缩放所有移动。位置使用浮点数保存;blit 时使用整数矩形。 - 用两种方式处理输入: 基于事件的方式(
KEYDOWN/MOUSEBUTTONDOWN,用于离散操作)和轮询方式(pygame.key.get_pressed(),用于持续移动)。 - 使用
Sprite+Group组织对象。 继承pygame.sprite.Sprite,并设置image/rect;group.update(dt)和group.draw(screen)可处理批量操作。运行程序并观察窗口,确认效果后再判断其是否正常。
模式
1. 最简游戏循环(骨架)
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()
running = True
while running:
dt = clock.tick(60) / 1000 # cap at 60 FPS; dt = seconds since last frame
for event in pygame.event.get(): # MUST drain the queue or the OS thinks it hung
if event.type == pygame.QUIT:
running = False
# update game state here, scaled by dt ...
screen.fill((18, 18, 28)) # clear each frame
# draw everything here ...
pygame.display.flip() # present the frame
pygame.quit()
2. 基于增量时间的移动(与帧率无关)
from pygame.math import Vector2
pos = Vector2(100, 100) # keep position as floats
speed = 220 # PIXELS PER SECOND, not per frame
# inside the loop, after computing dt:
keys = pygame.key.get_pressed()
direction = Vector2(
keys[pygame.K_RIGHT] - keys[pygame.K_LEFT],
keys[pygame.K_DOWN] - keys[pygame.K_UP],
)
if direction.length_squared() > 0:
direction = direction.normalize() # equal speed on diagonals
pos += direction * speed * dt # RIGHT: dt-scaled
screen.blit(player_img, (round(pos.x), round(pos.y))) # blit at integer pixels
3. 输入:事件与轮询
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN: # discrete press: jump, menu, pause
if event.key == pygame.K_SPACE:
jump()
elif event.key == pygame.K_ESCAPE:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
shoot_at(event.pos) # event.pos = (x, y)
# Polled state (read once per frame) for continuous/held input:
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
move_left(dt)
4. Sprite 子类与 Group
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
# convert() once at load makes blits much faster; _alpha keeps transparency.
self.image = pygame.image.load("player.png").convert_alpha()
self.rect = self.image.get_rect(center=(x, y))
self.pos = pygame.math.Vector2(self.rect.center)
self.speed = 240
def update(self, dt): # Group.update(dt) calls this per sprite
keys = pygame.key.get_pressed()
self.pos.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * self.speed * dt
self.rect.center = (round(self.pos.x), round(self.pos.y))
all_sprites = pygame.sprite.Group()
all_sprites.add(Player(400, 300))
# in the loop:
all_sprites.update(dt) # calls each sprite's update(dt)
all_sprites.draw(screen) # blits each sprite at its rect
5. 碰撞检测
# Sprite vs group: e.g. player picking up coins (True = remove collided coins).
collected = pygame.sprite.spritecollide(player, coins, dokill=True)
score += len(collected)
# Group vs group: bullets vs enemies (kill both on hit).
hits = pygame.sprite.groupcollide(bullets, enemies, True, True)
# Plain rect overlap (no sprites needed):
if player.rect.colliderect(door_rect):
open_door()
常见问题
- 窗口冻结/“未响应” → 没有处理事件队列。每帧调用
pygame.event.get()(或pygame.event.pump())。 - 速度在较快的机器上有所不同 → 移动距离固定为每帧的值。使用
dt = clock.tick(fps) / 1000缩放,并采用每秒像素数作为速度值。 - 亚像素移动出现跳变/抖动 →
rect坐标是整数;将真实位置保存为浮点数Vector2,并在每帧设置rect.center = round(...)。 - blit 速度慢/帧率下降 → 加载图像后调用一次
.convert()(不透明)或.convert_alpha()(透明);未转换的 surface 执行 blit 时会慢得多。 - 什么都没显示 → 忘记调用
pygame.display.flip()(或update()),或者在screen.fill(...)之前绘制,导致内容被清除。 - 绘制顺序不正确 → pygame 使用画家算法;后执行的 blit 会覆盖先执行的内容。先绘制背景,最后绘制精灵。
pip install pygame安装了旧版 → 若要使用维护中的分支,请运行pip install pygame-ce;同时安装两者会导致导入冲突。- 对角移动更快 → 在按速度缩放之前,对方向向量进行归一化。
参考资料
- 有关
Group的变体(用于 z 轴顺序的GroupSingle、LayeredUpdates)、使用mask进行像素级精确碰撞、切分精灵表、简单动画、音效/音乐和文本渲染,请阅读references/sprites-and-collision.md。
相关技能
love2d-core— 在 LÖVE/Lua 中实现相同的循环概念。bevy-ecs— 当项目超出 pygame 的适用范围时,可使用更复杂的 ECS 引擎。input-systems/save-systems— 与引擎无关的输入和持久化方案。platformer/roguelike— 可与 pygame 配合使用的游戏类型模板。
评论
支持 Markdown