/pygame-core
Structure a pygame (pygame-ce) game in Python: the init/event/update/draw loop, delta-time movement, Surface/Rect blitting, keyboard/mouse input, and Sprite/Group management with collision. Use when building or debugging a pygame game — when the user mentions pygame, pygame-ce,
$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill pygame-core --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/pygame-core
Context preview
The summary Claude sees to decide when to auto-load this skill.
Structure a pygame (pygame-ce) game in Python: the init/event/update/draw loop, delta-time movement, Surface/Rect blitting, keyboard/mouse input, and Sprite/Group management with collision. Use when building or debugging a pygame game — when the user mentions pygame, pygame-ce,
SKILL.md
pygame-core.SKILL.mdname: pygame-core
description: >
Structure a pygame (pygame-ce) game in Python: the init/event/update/draw loop,
delta-time movement, Surface/Rect blitting, keyboard/mouse input, and
Sprite/Group management with collision. Use when building or debugging a pygame
game — when the user mentions pygame, pygame-ce, the game loop, blit, Surface,
Rect, sprite groups, or clock.tick. Targets pygame-ce.
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.7** (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 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`.
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.
Patterns
1. Minimal game loop (the skeleton)
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. Delta-time movement (frame-rate independent)
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 pixels3. Input: events vs polling
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. A Sprite subclass + a 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 rect5. Collision detection
# 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()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 dro
Read more
name: pygame-core description: > Structure a pygame (pygame-ce) game in Python: the init/event/update/draw loop, delta-time movement, Surface/Rect blitting, keyboard/mouse input, and Sprite/Group management with collision. Use when building or debugging a pygame game — when the user mentions pygame, pygame-ce, the game loop, blit, Surface, Rect, sprite groups, or clock.tick. Targets pygame-ce.
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.7** (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 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`.
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.
Patterns
1. Minimal game loop (the skeleton)
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. Delta-time movement (frame-rate independent)
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 pixels3. Input: events vs polling
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. A Sprite subclass + a 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 rect5. Collision detection
# 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()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 dro
<img src="docs/assets/banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.
Repo: gamedev-skills/awesome-gamedev-agent-skills
Other skills on awesome-gamedev-agent-skills.
- /audio-design
Implement game audio practice — bus/mixer architecture and gain in decibels, ducking (sidechain), adaptive/dynamic music via layering and re-sequencing, SFX variation, and beat synchronization. Engine-neutral. Use when the user mentions audio mixing, audio buses,
Open skill - /camera-systems
Build game cameras that feel good — 2D follow with a deadzone, look-ahead, smoothing, and level-bounds clamping; 3D third-person orbit with collision and first-person look; plus multi-target framing and a shake hook. Engine-neutral techniques that pair with the engine's camera
Open skill - /create-game-assets
Plan, generate, source, normalize, and validate cohesive visual game assets. Use for art direction, style bibles, sprites, tilesets, backgrounds, UI art, icons, textures, concept art, or 3D asset briefs.
Open skill - /dialogue-systems
Build branching dialogue and narrative — a node/choice graph with conditions, variables, and localization hooks — and choose between authoring tools Ink and Yarn Spinner or a custom data-driven runner. Engine-neutral. Use when the user mentions dialogue system, branching
Open skill - /game-ai
Design NPC and enemy decision-making with finite state machines, behavior trees, steering behaviors, and A* pathfinding — engine-neutral algorithms that pair with the detected engine's navigation API. Use when building enemy AI, an FSM or behavior tree, steering/flocking, or
Open skill - /game-feel
Add "juice" and game feel that makes actions satisfying — screen shake, hit-stop/freeze frames, tweened/eased motion, squash & stretch, knockback, and layered audio-visual feedback — as engine-neutral techniques that pair with the detected engine's tween, particle, and camera
Open skill

