pygame-core

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

pygame Core

pygame 核心内容

Build the foundation of a pygame game in Python: the main loop, delta-time movement, drawing with
Surface
/
Rect
, input, and
Sprite
/
Group
management. Targets pygame-ce 2.5+ (the actively maintained community fork; same
import pygame
).
使用Python构建pygame游戏的基础:主循环、基于delta-time的移动、
Surface
/
Rect
绘制、输入处理,以及
Sprite
/
Group
管理。 目标框架为pygame-ce 2.5+(由社区积极维护的分支;使用相同的
import pygame
导入方式)。

When to use

适用场景

  • Use when starting a pygame game, fixing the loop, frame-rate-dependent speed, input handling, blitting, or sprite/group collision.
  • Use when code does
    import pygame
    and the project depends on
    pygame-ce
    (or
    pygame
    ).
When not to use: Python language questions unrelated to pygame. 3D rendering (pygame is 2D). For cross-engine save/load use
save-systems
; for rebindable input architecture see
input-systems
.
  • 适用于启动pygame游戏、修复循环问题、解决帧率依赖的速度问题、输入处理、绘制(blit)或精灵/组碰撞检测时。
  • 适用于代码中包含
    import pygame
    且项目依赖
    pygame-ce
    (或
    pygame
    )的场景。
不适用场景: 与pygame无关的Python语言问题。3D渲染(pygame是2D框架)。跨引擎存档/加载请使用
save-systems
;可重绑定输入架构请参考
input-systems

Core workflow

核心工作流程

  1. Install pygame-ce, not legacy pygame.
    pip install pygame-ce
    — it's the maintained fork and imports as
    pygame
    . Don't install both in one environment.
  2. Init and open a window.
    pygame.init()
    ,
    screen = pygame.display.set_mode((w, h))
    ,
    clock = pygame.time.Clock()
    .
  3. Run one loop: events → update → draw → flip. Pump the event queue every frame (
    for event in pygame.event.get()
    ), update state, redraw, then
    pygame.display.flip()
    .
  4. Make it frame-rate independent. Get
    dt = clock.tick(60) / 1000
    (seconds) and scale all motion by
    dt
    . Keep positions as floats; blit at integer rects.
  5. Handle input two ways: event-based (
    KEYDOWN
    /
    MOUSEBUTTONDOWN
    , for discrete actions) and polled (
    pygame.key.get_pressed()
    , for held movement).
  6. Organise objects with
    Sprite
    +
    Group
    .
    Subclass
    pygame.sprite.Sprite
    with
    image
    /
    rect
    ;
    group.update(dt)
    and
    group.draw(screen)
    handle the batch. Run it and watch the window before assuming it works.
  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
    。保持位置为浮点数;绘制时使用整数矩形。
  5. 两种输入处理方式: 基于事件的方式(
    KEYDOWN
    /
    MOUSEBUTTONDOWN
    ,用于离散操作)和轮询方式(
    pygame.key.get_pressed()
    ,用于持续按住的移动)。
  6. 使用
    Sprite
    +
    Group
    组织对象。
    继承
    pygame.sprite.Sprite
    并定义
    image
    /
    rect
    group.update(dt)
    group.draw(screen)
    处理批量操作。运行后先查看窗口效果,不要直接假设代码正常工作。

Patterns

模式示例

1. Minimal game loop (the skeleton)

1. 最小游戏循环(框架)

python
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()
python
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          # 限制帧率为60 FPS;dt为自上一帧以来的秒数
    for event in pygame.event.get():    # 必须清空事件队列,否则系统会认为程序无响应
        if event.type == pygame.QUIT:
            running = False

    # 在此处更新游戏状态,按dt缩放...

    screen.fill((18, 18, 28))           # 每帧清空屏幕
    # 在此处绘制所有内容...
    pygame.display.flip()               # 展示当前帧

pygame.quit()

2. Delta-time movement (frame-rate independent)

2. 基于delta-time的移动(帧率独立)

python
from pygame.math import Vector2

pos = Vector2(100, 100)        # keep position as floats
speed = 220                    # PIXELS PER SECOND, not per frame
python
from pygame.math import Vector2

pos = Vector2(100, 100)        # 保持位置为浮点数
speed = 220                    # 像素/秒,而非像素/帧

inside the loop, after computing dt:

在循环内,计算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
undefined
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() # 对角线移动速度与轴向一致 pos += direction * speed * dt # 向右移动:按dt缩放 screen.blit(player_img, (round(pos.x), round(pos.y))) # 绘制到整数像素位置
undefined

3. Input: events vs polling

3. 输入处理:事件 vs 轮询

python
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)
python
for event in pygame.event.get():
    if event.type == pygame.QUIT:
        running = False
    elif event.type == pygame.KEYDOWN:        # 离散按键:跳跃、菜单、暂停
        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)
undefined
keys = pygame.key.get_pressed() if keys[pygame.K_a]: move_left(dt)
undefined

4. A Sprite subclass + a Group

4. Sprite子类 + Group组

python
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))
python
class Player(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        # 加载时调用convert()可大幅提升绘制速度;_alpha保留透明度。
        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)会为每个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
undefined
all_sprites.update(dt) # 调用每个Sprite的update(dt) all_sprites.draw(screen) # 在每个Sprite的rect位置绘制
undefined

5. Collision detection

5. 碰撞检测

python
undefined
python
undefined

Sprite vs group: e.g. player picking up coins (True = remove collided coins).

Sprite与组碰撞:例如玩家收集金币(True表示移除碰撞的金币)。

collected = pygame.sprite.spritecollide(player, coins, dokill=True) score += len(collected)
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)
hits = pygame.sprite.groupcollide(bullets, enemies, True, True)

Plain rect overlap (no sprites needed):

普通矩形重叠(无需Sprite):

if player.rect.colliderect(door_rect): open_door()
undefined
if player.rect.colliderect(door_rect): open_door()
undefined

Pitfalls

常见陷阱

  • Window freezes / "not responding" → you didn't pump the event queue. Call
    pygame.event.get()
    (or
    pygame.event.pump()
    ) every frame.
  • Speed differs on faster machines → you moved by a fixed amount per frame. Scale by
    dt = clock.tick(fps) / 1000
    and use pixels-per-second values.
  • Sub-pixel movement snaps/jitters
    rect
    coordinates are integers; store the true position as a
    Vector2
    of floats and assign
    rect.center = round(...)
    each frame.
  • Blits are slow / framerate drops → call
    .convert()
    (opaque) or
    .convert_alpha()
    (transparent) on loaded images once; un-converted surfaces blit far slower.
  • Nothing appears → you forgot
    pygame.display.flip()
    (or
    update()
    ), or you drew before
    screen.fill(...)
    so it was cleared away.
  • Wrong draw order → pygame uses painter's order; later blits cover earlier ones. Draw background first, sprites last.
  • pip install pygame
    got the old one
    → for the maintained fork use
    pip install pygame-ce
    ; having both installed causes import conflicts.
  • Diagonal movement is faster → normalise the direction vector before scaling by speed.
  • 窗口冻结/"未响应" → 你没有处理事件队列。每帧都要调用
    pygame.event.get()
    (或
    pygame.event.pump()
    )。
  • 在更快的机器上速度不同 → 你每帧移动固定距离。应使用
    dt = clock.tick(fps) / 1000
    缩放,并使用像素/秒的速度值。
  • 亚像素移动出现跳跃/抖动
    rect
    坐标是整数;将实际位置存储为
    Vector2
    浮点数,每帧赋值
    rect.center = round(...)
  • 绘制(blit)缓慢/帧率下降 → 加载图像时调用一次
    .convert()
    (不透明)或
    .convert_alpha()
    (透明);未转换的Surface绘制速度慢得多。
  • 没有内容显示 → 你忘记调用
    pygame.display.flip()
    (或
    update()
    ),或者在
    screen.fill(...)
    之前绘制,导致内容被清空。
  • 绘制顺序错误 → pygame遵循画家算法;后续绘制会覆盖之前的内容。先绘制背景,最后绘制精灵。
  • pip install pygame
    安装了旧版本
    → 要安装维护中的分支,请使用
    pip install pygame-ce
    ;同时安装两者会导致导入冲突。
  • 对角线移动速度更快 → 在按速度缩放之前,先对方向向量进行归一化。

References

参考资料

  • For
    Group
    variants (
    GroupSingle
    ,
    LayeredUpdates
    for z-order), pixel-perfect collision with
    mask
    , slicing a spritesheet, simple animation, sound/music, and text rendering, read
    references/sprites-and-collision.md
    .
  • 关于
    Group
    的变体(
    GroupSingle
    、用于层级排序的
    LayeredUpdates
    )、基于
    mask
    的像素完美碰撞、精灵切片、简单动画、声音/音乐以及文本渲染,请阅读
    references/sprites-and-collision.md

Related skills

相关技能

  • love2d-core
    — the same loop concepts in LÖVE/Lua.
  • bevy-ecs
    — a heavier ECS engine when a project outgrows pygame.
  • input-systems
    /
    save-systems
    — engine-agnostic input and persistence.
  • platformer
    /
    roguelike
    — genre templates that pair with pygame.
  • love2d-core
    —— 在LÖVE/Lua中使用相同的循环概念。
  • bevy-ecs
    —— 当项目超出pygame的能力范围时,可使用这款更重量级的ECS引擎。
  • input-systems
    /
    save-systems
    —— 与引擎无关的输入和持久化系统。
  • platformer
    /
    roguelike
    —— 与pygame搭配使用的类型模板。