LEARN PYTHON | Pygame: Sprites, Collisions And Scores

Lesson objective

Create Sprite objects and Groups, synchronise image rectangles with float positions and handle collision outcomes without repeated scoring.

Learn

08.4 | PYGAME: SPRITES, COLLISIONS AND SCORES

01 | WHEN A RECTANGLE BECOMES AN OBJECT

A game with one player and several falling objects quickly needs more than one position variable. A sprite is a game object with a drawable appearance and associated behaviour. In Pygame’s Sprite system, it commonly has an image Surface and a rect defining its position and bounds.

A Group manages sprite membership and supports operations such as updating or drawing several sprites. Inheritance from pygame.sprite.Sprite supplies group-related behaviour; it does not automatically invent movement, scoring or collision rules.

Think of a sprite as a performer and a Group as a cast list. Your update code decides what the performer does, and your collision rule decides what an encounter means.

02 | A SMALL COMPLETE COLLECTION GAME

Run this file with Pygame installed. Three gold circles fall once, and the player moves left and right. A caught circle is worth ten points. It is a learning prototype, not yet the complete arcade project: there are no new waves, lives or restart screen.

super().__init__() initialises Sprite membership support. image.get_rect creates a position rectangle based on the image size. Groups call each sprite’s update with the supplied arguments.

spritecollide compares the player against the star group. With dokill=True, collided stars are removed from their groups so the same overlap cannot score again on the next frame.

import pygame

pygame.init()
try:
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    font = pygame.font.Font(None, 28)

    class FallingStar(pygame.sprite.Sprite):
        def __init__(self, x, y):
            super().__init__()
            self.image = pygame.Surface((24, 24), pygame.SRCALPHA)
            pygame.draw.circle(self.image, (255, 217, 94), (12, 12), 12)
            self.rect = self.image.get_rect(topleft=(x, y))
            self.y = float(y)

        def update(self, dt):
            self.y += 140.0 * dt
            self.rect.y = round(self.y)
            if self.rect.top >= 480:
                self.kill()

    class Player(pygame.sprite.Sprite):
        def __init__(self):
            super().__init__()
            self.image = pygame.Surface((60, 24))
            self.image.fill((36, 152, 143))
            self.rect = self.image.get_rect(midbottom=(320, 450))
            self.x = float(self.rect.x)

        def update(self, dt, direction):
            self.x = max(0.0, min(580.0, self.x + direction * 300.0 * dt))
            self.rect.x = round(self.x)

    player = Player()
    stars = pygame.sprite.Group(FallingStar(100, 0), FallingStar(300, 40), FallingStar(500, 80))
    players = pygame.sprite.GroupSingle(player)
    score = 0
    running = True
    while running:
        dt = min(clock.tick(60) / 1000.0, 0.05)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                running = False
        if not running:
            break
        keys = pygame.key.get_pressed()
        direction = int(keys[pygame.K_RIGHT]) - int(keys[pygame.K_LEFT])
        player.update(dt, direction)
        stars.update(dt)
        hits = pygame.sprite.spritecollide(player, stars, dokill=True)
        score += 10 * len(hits)
        screen.fill((18, 28, 38))
        stars.draw(screen)
        players.draw(screen)
        screen.blit(font.render(f"Score: {score}", True, (255, 255, 255)), (16, 16))
        pygame.display.flip()
finally:
    pygame.quit()

03 | IMAGE AND RECT HAVE DIFFERENT JOBS

image holds pixels. rect holds integer position and dimensions. Group.draw uses each sprite’s image and rect; merely changing a separate y variable does not update the drawn location.

The falling star keeps self.y as a float, adds speed * dt, then assigns round(self.y) to self.rect.y. This preserves fractional movement while supplying the integer coordinates expected by the rectangle.

A transparent Surface lets a circular drawing have transparent corners. Its rect still covers the full square image area. Transparent pixels are not automatically excluded from a rectangle collision test.

04 | GROUPS SUPPORT LIFECYCLE

A sprite can belong to more than one Group. For example, a star might belong to both all_drawable and collectibles. Calling kill removes it from all its current groups.

kill does not guarantee immediate destruction of the Python object. If another reference still points to it, that object can remain alive. The method changes membership, which is what drawing and collision loops use.

Remove objects that leave the screen, otherwise an endless spawning game can keep increasing the number of updates. Lifecycle includes creation, active updates and deliberate removal.

05 | COLLISION DETECTION IS NOT THE OUTCOME

Detection answers whether shapes overlap. The outcome may add score, reduce lives, bounce, stop movement or trigger a scene change. Keep those rules separate so a detection function does not secretly decide every game policy.

Pygame’s default sprite collision uses rectangles. Rectangles that only touch along an edge do not overlap under the usual rectangle rule. Testing just before, at and just after the boundary makes the decision visible.

Mask-based checks can compare non-transparent pixels more precisely. They need suitable masks and still require an outcome policy. Use the simplest collision shape that fits the game rather than claiming all rectangle overlaps match the visible art exactly.

06 | SCORE ONCE PER COLLECTED OBJECT

If a star overlaps for several frames and remains active, adding ten each frame gives repeated points for one encounter. Consume or otherwise mark it after the accepted collection.

The prototype removes collided stars through dokill=True, then adds ten times the number of returned hits. Two distinct caught stars can therefore give twenty points in one frame.

Keep score in application state. Render a text Surface from that score when drawing, but do not update score inside the rendering operation. A paused redraw should not award points.

07 | HAZARDS NEED A DAMAGE POLICY

The complete game introduces rocks and three lives. After an accepted rock hit it gives a short invulnerability interval, preventing several overlapping rocks from instantly taking every life.

Invulnerability is state: a remaining duration reduced by dt. The collision still consumes an overlapping rock, but it does not reduce lives again while protection is active. State the rule, because another game might keep the hazard and use a different response.

At high speeds, a moving object can pass from one side of another to the other between updates without an overlapping sampled position. Smaller bounded steps reduce this tunnelling risk; continuous collision techniques address it more directly.

08 | TEST A RECTANGLE OVERLAP

The player occupies x=100 to 148. A 24-pixel star whose left edge is exactly 148 only touches the player’s right edge. Move it one pixel left and compare the result. The illustration assumes matching vertical ranges.

09 | SEPARATE RULES FROM ART

Use simple shapes while validating movement and collisions. Later replace an image, recalculate its rect intentionally and inspect whether the hitbox still matches the intended gameplay. Centre-preserving changes may be preferable to silently moving a sprite when the art changes size.

The final project deliberately uses drawn shapes and Pygame’s default font so the download has no missing art or audio files. Its rules are in a pure Python model; its renderer does not require every object to inherit Sprite.

Sprite and Group are useful organisation tools, not a requirement that every game architecture must use them. This lesson teaches them directly, then the capstone demonstrates a separate model with explicit drawing.

10 | TEST A COLLECTION, NOT JUST A PICTURE

Check one hit, two distinct hits, no overlap, exact edge contact and an off-screen object. After collection, update again and verify the score does not rise for the same star.

For hazard logic, check the first damage event, another collision during protection and one after protection expires. A well-drawn game can still have unfair rules; tests should examine the changing state as well as the visible scene.

OFFICIAL REFERENCE

Use the worked examples first, then consult the official documentation to check widget, input or drawing options. Match it to the version selected in your project environment.

PUT YOUR SKILLS TO WORK

Use the Section 8 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.

Section 8 challenges Python course navigation

MATCH THE PYTHON TERM TO ITS MEANING

Terminology

Terminology

Sprite

An object representing a drawable game entity and its behaviour.

image

The Surface supplying a sprite’s pixels.

rect

A rectangle supplying position and bounds.

Group

A collection managing sprite membership and common operations.

GroupSingle

A Pygame group holding at most one sprite.

update

A method advancing an object’s state.

draw

An operation rendering objects without defining their game-rule changes.

Collision detection

Checking whether chosen shapes intersect.

Collision outcome

The rule applied after a detected encounter.

Hitbox

The shape used to decide collisions.

Mask

A pixel-level occupancy representation used for more precise collision tests.

kill

A Sprite operation removing it from all groups.

Lifecycle

Creation, active use and removal of an object.

Score

A game-state value recording points.

Invulnerability

A temporary state preventing further damage under a rule.

Tunnelling

Passing through a collision region between sampled updates.

Bounding rectangle

An axis-aligned rectangular extent around an object.

Consume

Remove or mark an object after an accepted interaction.

Questions

Questions

CHECK YOUR UNDERSTANDING

Select all correct choices. Each exact set earns one point.

1. Which attributes does Group.draw commonly use?
2. What must follow changing a sprite’s float y?
3. What does Sprite.kill do?
4. What does dokill=True help with collection?
5. What happens when boxes only touch at an edge under the rule taught here?
6. Why might a circle collide at a transparent corner?
7. What should render do to score?
8. What is tunnelling?

EXPLAIN AND COMPARE

Write your answers before revealing each sample.

1. Explain image and rect.

2. Describe float-position synchronisation.

3. Explain repeated-score prevention.

4. Compare detection and outcome.

5. Explain why transparent art still has a rectangle.

6. Describe what kill means.

7. Explain damage protection.

8. Evaluate sampled collision at high speed.

CODE IT | YOUR INDEPENDENT CHALLENGE

Add collectibles with collision outcomes and removal. Prove one object cannot score repeatedly.

Work in your Python editor. Keep a working version, explain one design choice and record at least two tests. Use the Learn tab for worked examples before comparing your result.

Flashcards

Flashcards

Click to flip. Select the ideas you need to revisit.

0 cards selected for revision.

    Selections are kept while this page is open.

    Workbook

    Workbook

    COMING SOON

    The workbook for Pygame: Sprites, Collisions And Scores is coming soon. Complete the matching task and written questions in your exercise book.