mcpField
P
pygame-core

pygame-core

github.com/gamedev-skills开发5.4k5.7k 次安装今天更新
代码仓库

关于 pygame-core

面向使用 Python 开发或调试 2D 游戏的 pygame 基础技能,涵盖主循环、按时间缩放的移动、输入、绘制和精灵管理,目标为 pygame-ce 2.5.8。

主要功能

  1. 01搭建事件、更新与绘制循环
  2. 02使用 delta time 缩放移动
  3. 03处理事件式与轮询式输入
  4. 04使用 Surface 和 Rect 绘制
  5. 05管理精灵与组
  6. 06检测精灵及矩形碰撞

适用场景

  1. 01使用 pygame-ce 开始 Python 游戏
  2. 02修复移动速度随帧率变化的问题
  3. 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。

核心工作流

  1. 安装 pygame-ce,而非旧版 pygame。 使用 pip install pygame-ce——这是维护中的分支,导入名称为 pygame。不要在同一环境中同时安装两者。
  2. 初始化并打开窗口。 调用 pygame.init(),设置 screen = pygame.display.set_mode((w, h)),并创建 clock = pygame.time.Clock()。
  3. 运行单一循环:事件 → 更新 → 绘制 → 翻转。 每帧处理事件队列(for event in pygame.event.get()),更新状态,重新绘制,然后调用 pygame.display.flip()。
  4. 实现与帧率无关的移动。 获取 dt = clock.tick(60) / 1000(秒),并用 dt 缩放所有移动。位置使用浮点数保存;blit 时使用整数矩形。
  5. 用两种方式处理输入: 基于事件的方式(KEYDOWN/MOUSEBUTTONDOWN,用于离散操作)和轮询方式(pygame.key.get_pressed(),用于持续移动)。
  6. 使用 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