Skip to content
BetaDE
Lesson 2 of 4Programming games

Programming games · Lesson 2 · 45 minutes

Project: catch game

What you'll learn

  • Make things appear at random places with random
  • Check whether two rectangles touch (collision)
  • Count points, take away lives and end the game with game.stop()

Catch the stars!

Stars fall from the top. Your basket moves left and right at the bottom. Every caught star gives a point. For this you need three new things: randomness, collision and a score.

Randomness: where does the next star come from?

random.randint(0, 380) gives a random whole number from 0 to 380. So every star falls at a different place.

import random
from codelix import game

game.window(400, 300)
star = {"x": 190, "y": 0}


def update():
    star["y"] = star["y"] + 5
    if star["y"] > 300:
        star["y"] = 0
        star["x"] = random.randint(0, 380)
    game.rect(star["x"], star["y"], 20, 20, "gold")


game.run(update)

Collision: do two rectangles touch?

Does the basket catch the star? That’s the most important question in almost every game. Two rectangles overlap if they cross both left-right and top-bottom.

They cross left-right if both are true:

  • a starts left of the right edge of b: a["x"] < b["x"] + b["w"]
  • a ends right of the left edge of b: a["x"] + a["w"] > b["x"]

The same works for top-bottom with y and the height h. Try it with two rectangles:

a = {"x": 0, "y": 0, "w": 50, "h": 30}
b = {"x": 40, "y": 20, "w": 30, "h": 30}

left_right = a["x"] < b["x"] + b["w"] and a["x"] + a["w"] > b["x"]
top_bottom = a["y"] < b["y"] + b["h"] and a["y"] + a["h"] > b["y"]

print("cross left-right:", left_right)
print("cross top-bottom:", top_bottom)
print("collision:", left_right and top_bottom)

Change b["x"] to 60. Then left-right is False, and there is no collision any more.

Lives and game over

A real game ends at some point. game.stop() stops it. The last picture stays on the screen. Here three stars fall through – then it’s over:

from codelix import game

game.window(400, 300)
star = {"y": 0}
score = {"lives": 3}


def update():
    star["y"] = star["y"] + 15
    if star["y"] > 300:
        star["y"] = 0
        score["lives"] = score["lives"] - 1
    game.rect(190, star["y"], 20, 20, "gold")
    game.text(10, 10, f"Lives: {score['lives']}")
    if score["lives"] == 0:
        game.text(130, 130, "Game over!", "tomato", 28)
        game.stop()


game.run(update)
print("Thanks for playing!")

After game.stop() your program keeps running. That’s why the text still appears in the output at the end.

Keep building: When your task works, add lives: if a star falls through at the bottom, you lose one. At 0 lives, the game is over. Even harder: with every point the stars fall a little faster.

Your task

Your turn

Write the function hits(a, b). It gets two rectangles as dictionaries with x, y, w (width) and h (height). It returns True if the two overlap, otherwise False. If only the edges touch, that doesn't count. Then use hits in update(): when the basket catches the star, you get one point and the star starts at the top again (y = 0).

import random
from codelix import game

game.window(400, 300)
basket = {"x": 170, "y": 270, "w": 60, "h": 20}
star = {"x": 190, "y": 0, "w": 20, "h": 20}
score = {"points": 0}


def hits(a, b):
    return False


def update():
    if game.key("left") and basket["x"] > 0:
        basket["x"] = basket["x"] - 6
    if game.key("right") and basket["x"] < 340:
        basket["x"] = basket["x"] + 6

    star["y"] = star["y"] + 4
    if star["y"] > 300:
        star["y"] = 0
        star["x"] = random.randint(0, 380)

    # Here: did the basket catch the star?

    game.rect(basket["x"], basket["y"], basket["w"], basket["h"], "sienna")
    game.rect(star["x"], star["y"], star["w"], star["h"], "gold")
    game.text(10, 10, f"Points: {score['points']}")


game.run(update)

Solve it without hints to earn a ⭐ on your progress page.

Show solution

Try it yourself first – mistakes teach you the most. Here is one possible solution:

import random
from codelix import game

game.window(400, 300)
basket = {"x": 170, "y": 270, "w": 60, "h": 20}
star = {"x": 190, "y": 0, "w": 20, "h": 20}
score = {"points": 0}


def hits(a, b):
    return (a["x"] < b["x"] + b["w"] and a["x"] + a["w"] > b["x"]
            and a["y"] < b["y"] + b["h"] and a["y"] + a["h"] > b["y"])


def update():
    if game.key("left") and basket["x"] > 0:
        basket["x"] = basket["x"] - 6
    if game.key("right") and basket["x"] < 340:
        basket["x"] = basket["x"] + 6

    star["y"] = star["y"] + 4
    if star["y"] > 300:
        star["y"] = 0
        star["x"] = random.randint(0, 380)

    if hits(basket, star):
        score["points"] = score["points"] + 1
        star["y"] = 0
        star["x"] = random.randint(0, 380)

    game.rect(basket["x"], basket["y"], basket["w"], basket["h"], "sienna")
    game.rect(star["x"], star["y"], star["w"], star["h"], "gold")
    game.text(10, 10, f"Points: {score['points']}")


game.run(update)