pygame Core
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.8 (the actively maintained community fork; same
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 pygameand the project depends onpygame-ce(orpygame).
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.
Core workflow
- Install pygame-ce, not legacy pygame.
pip install pygame-ce— it's the maintained fork and imports aspygame. Don't install both in one environment. - Init and open a window.
pygame.init(),screen = pygame.display.set_mode((w, h)),clock = pygame.time.Clock(). - Run one loop: events → update → draw → flip. Pump the event queue every
frame (
for event in pygame.event.get()), update state, redraw, thenpygame.display.flip(). - Make it frame-rate independent. Get
dt = clock.tick(60) / 1000(seconds) and scale all motion bydt. Keep positions as floats; blit at integer rects. - Handle input two ways: event-based (
KEYDOWN/MOUSEBUTTONDOWN, for discrete actions) and polled (pygame.key.get_pressed(), for held movement). - Organise objects with
Sprite+Group. Subclasspygame.sprite.Spritewithimage/rect;group.update(dt)andgroup.draw(screen)handle the batch. Run it and watch the window before assuming it works.
Patterns
1. Minimal game loop (the skeleton)
2. Delta-time movement (frame-rate independent)
3. Input: events vs polling
4. A Sprite subclass + a Group
5. Collision detection
Pitfalls
- Window freezes / "not responding" → you didn't pump the event queue. Call
pygame.event.get()(orpygame.event.pump()) every frame. - Speed differs on faster machines → you moved by a fixed amount per frame.
Scale by
dt = clock.tick(fps) / 1000and use pixels-per-second values. - Sub-pixel movement snaps/jitters →
rectcoordinates are integers; store the true position as aVector2of floats and assignrect.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()(orupdate()), or you drew beforescreen.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 pygamegot the old one → for the maintained fork usepip install pygame-ce; having both installed causes import conflicts.- Diagonal movement is faster → normalise the direction vector before scaling by speed.
References
- For
Groupvariants (GroupSingle,LayeredUpdatesfor z-order), pixel-perfect collision withmask, slicing a spritesheet, simple animation, sound/music, and text rendering, readreferences/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.


