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:
astarts left of the right edge ofb:a["x"] < b["x"] + b["w"]aends right of the left edge ofb: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)- Something to think about
Two rectangles overlap if a starts left of b's right edge AND a's right edge is right of b's left edge – and the same for top/bottom.
- Which command helps?
Use
<and>(not<=and>=), then edges that just touch don't count. All four conditions must be joined with and.Write in
update(): ifhits(basket, star): – then increasescore["points"]and setstar["y"]=0.You only get the point inside if
hits(basket, star). - Gap code – fill in the ___
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 ___ ___ 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 ___: score["points"] = ___ 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)
Solve it without hints to earn a ⭐ on your progress page.
Show solution🔒 Unlocks after all hints or 2 checks
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)