Docs › game library

sprite.touches()

Find out when two sprites bump into each other. It is how you catch a coin, get hit by a rock, or crash into a wall.

How to write it

a.touches(b)
a
Any sprite: something made with game.sprite(), game.box(), game.circle() or game.label().
b
The other sprite to check against. Just one sprite, not a list (see lots of enemies below).

It gives back True if the two overlap right now, and False if they don't. It works both ways round: a.touches(b) and b.touches(a) always give the same answer.

A full example

Move the chicken with the arrow keys. Grab all the coins, and dodge the spinning rocks. The red boxes are switched on with game.debug(True) so you can see exactly what is being checked:

import game
import random

game.window(480, 360, background="#0b1020")
game.debug(True)            # red boxes: what touches() checks

player = game.sprite("chicken", 240, 300, size=50)
board = game.label("Coins: 0", 70, 24)

coins = []
for i in range(8):
    coin = game.sprite("coin", random.randint(30, 450), random.randint(60, 200), size=30)
    coins.append(coin)

rocks = []
for i in range(2):
    rock = game.sprite("asteroid", random.randint(30, 450), -40 - i * 200, size=44)
    rocks.append(rock)

score = 0

while game.playing():
    if game.pressed("left"):
        player.x = player.x - 6
    if game.pressed("right"):
        player.x = player.x + 6
    if game.pressed("up"):
        player.y = player.y - 6
    if game.pressed("down"):
        player.y = player.y + 6

    for coin in coins[:]:          # [:] loops over a COPY of the list
        if player.touches(coin):
            score = score + 1
            board.content = "Coins: " + str(score)
            game.sound("coin")
            coin.remove()          # gone from the screen
            coins.remove(coin)     # and gone from the list

    for rock in rocks:
        rock.y = rock.y + 4
        rock.angle = rock.angle + 5
        if rock.y > 400:
            rock.x = random.randint(30, 450)
            rock.y = -40
        if player.touches(rock):
            game.game_over("Bonk! Coins: " + str(score), retry=True)

    if len(coins) == 0:
        game.game_over("You got them all!", retry=True)

    game.frame()

Happy with the game? Delete the game.debug(True) line (or change it to game.debug(False)) and the red boxes disappear.

How it checks

touches() does not look at the picture itself. Every sprite has an invisible collision box around it, and touches() checks whether two boxes overlap.

  • Emoji and built-in sprites get a box a little smaller than their size (about 80%), so a near miss feels fair. Some built-in sprites get a shape that fits them better: the car is wide and low, the egg is tall.
  • game.box() and game.circle() use their own width and height. A circle still checks a square box around it.
  • Turning and stretching count. The box spins with .angle and grows with .size, .scale_x and .scale_y. That is why the rocks in the example have tilted red boxes.
  • Sprites from the Asset studio get a box that hugs the drawing, not the empty space around it.

Turn on game.debug(True) right after game.window(...) any time a collision feels wrong. Each sprite gets a red outline, and the outline is exactly the box touches() uses.

Make the box fit better

If a sprite gets hit when it clearly wasn't, its box is too big. Give it your own size with .hitbox, as (width, height):

player.hitbox = (30, 36)    # a smaller, fairer box
player.hitbox = None        # back to the automatic size

Your box still spins and stretches with the sprite.

Lots of coins, lots of enemies

Keep many sprites in a list, then check each one with a for loop, like the rocks in the example. When something should disappear after it's touched (a coin, an egg, a dead alien), do two things:

  • loop over a copy of the list, written coins[:], and
  • call both coin.remove() (off the screen) and coins.remove(coin) (out of the list).
for coin in coins[:]:
    if player.touches(coin):
        coin.remove()
        coins.remove(coin)

Why the copy? Removing things from a list while a for loop is walking through it makes the loop skip the next item. Walking through a copy, while removing from the real list, keeps every coin checked.

Why isn't my touches() working?

  • The score goes up by loads at once. touches() is True on every frame the two overlap, not just the first one. Move the coin away, or remove it, as soon as it's caught.
  • A hidden or removed sprite still gets hit. hide() and remove() only take it off the screen. The sprite still has its x and y, so touches() can still say True. Take it out of your list too, so you stop checking it.
  • Only some coins get collected. You removed from the list while looping over the same list. Loop over coins[:] instead.
  • You wrote player.touches(coins). touches() takes one sprite, not a whole list. Loop over the list and check each one.
  • It's hit before the sprites even meet, or they overlap and nothing happens. Turn on game.debug(True) and look at the red boxes, then fix the size with .hitbox.
  • A long label doesn't seem to collide. A label's box is a small square in the middle of the text (about 80% of its size), not the whole sentence. Use a game.box() for big targets.