← Python functions and events

Optional: Game levels

Turn the game into a real game with screens, levels and a turtle that moves.

Every game is a loop that updates its data and redraws, and a few screens it moves between.

Your game starts as soon as you press Start and just stops at the end. Real games have screens (a title, the game, game over), get harder as you play, and have things that move. Each is a small change to the game you already built.

PredictRead the code and say what it will do before you run anything. A wrong guess is fine: it shows you what to look for.

Predict

levels.py
from turtle import *
from ui import inside, draw_button

speed(0)
penup()
hideturtle()

xs = [-120, 0, 120, 120, 0, -120]
ys = [60, 110, 60, -50, -100, -50]
place = 0
tx = 0
ty = 0
score = 0
showing = False
time_left = 30
state = "title"


def big(text, x, y):
    goto(x, y)
    write(text, False, "left", ("Arial", 28, "bold"))


def draw_scene():
    clear()
    if state == "title":
        big("Catch the turtle", -140, 40)
        draw_button(-60, -180, 120, 40, "Play")
    if state == "play":
        goto(-190, 160)
        write(score, False, "left", ("Arial", 20, "normal"))
        goto(150, 160)
        write(time_left, False, "left", ("Arial", 20, "normal"))
    if state == "over":
        big("Game over", -90, 40)
        big(score, -10, -10)
        draw_button(-60, -180, 120, 40, "Again")
    goto(tx, ty)


def hop():
    global place, tx, ty, showing
    if state == "play":
        tx = xs[place]
        ty = ys[place]
        place = (place + 1) % len(xs)
        goto(tx, ty)
        showturtle()
        showing = True
        ontimer(hide, 700)
        ontimer(hop, 1000)


def hide():
    global showing
    if showing:
        showing = False
        hideturtle()


def tick():
    global time_left, state
    if state == "play":
        time_left = time_left - 1
        if time_left == 0:
            state = "over"
            hideturtle()
        else:
            ontimer(tick, 1000)
        draw_scene()


def start():
    global score, time_left, place, state
    score = 0
    time_left = 30
    place = 0
    state = "play"
    draw_scene()
    hop()
    ontimer(tick, 1000)


def clicked(x, y):
    global score, showing
    if state == "play" and showing and inside(x, y, tx - 20, ty - 20, 40, 40):
        score = score + 1
        showing = False
        hideturtle()
        draw_scene()
    if state != "play" and inside(x, y, -60, -180, 120, 40):
        start()


draw_scene()
onscreenclick(clicked)
listen()
mainloop()

state is "title", "play" or "over". draw_scene draws a different screen for each one.

The 30 seconds run out. What is on the screen?

RunRun it and compare the result with your prediction.

Run

Play a game to the end, then play again.

levels.py
from turtle import *
from ui import inside, draw_button

speed(0)
penup()
hideturtle()

xs = [-120, 0, 120, 120, 0, -120]
ys = [60, 110, 60, -50, -100, -50]
place = 0
tx = 0
ty = 0
score = 0
showing = False
time_left = 30
state = "title"


def big(text, x, y):
    goto(x, y)
    write(text, False, "left", ("Arial", 28, "bold"))


def draw_scene():
    clear()
    if state == "title":
        big("Catch the turtle", -140, 40)
        draw_button(-60, -180, 120, 40, "Play")
    if state == "play":
        goto(-190, 160)
        write(score, False, "left", ("Arial", 20, "normal"))
        goto(150, 160)
        write(time_left, False, "left", ("Arial", 20, "normal"))
    if state == "over":
        big("Game over", -90, 40)
        big(score, -10, -10)
        draw_button(-60, -180, 120, 40, "Again")
    goto(tx, ty)


def hop():
    global place, tx, ty, showing
    if state == "play":
        tx = xs[place]
        ty = ys[place]
        place = (place + 1) % len(xs)
        goto(tx, ty)
        showturtle()
        showing = True
        ontimer(hide, 700)
        ontimer(hop, 1000)


def hide():
    global showing
    if showing:
        showing = False
        hideturtle()


def tick():
    global time_left, state
    if state == "play":
        time_left = time_left - 1
        if time_left == 0:
            state = "over"
            hideturtle()
        else:
            ontimer(tick, 1000)
        draw_scene()


def start():
    global score, time_left, place, state
    score = 0
    time_left = 30
    place = 0
    state = "play"
    draw_scene()
    hop()
    ontimer(tick, 1000)


def clicked(x, y):
    global score, showing
    if state == "play" and showing and inside(x, y, tx - 20, ty - 20, 40, 40):
        score = score + 1
        showing = False
        hideturtle()
        draw_scene()
    if state != "play" and inside(x, y, -60, -180, 120, 40):
        start()


draw_scene()
onscreenclick(clicked)
listen()
mainloop()
Try it yourselfYour edits are saved in this browser.

InvestigateTrace how each line of code connects to what you see.

Investigate

Find every line that changes state. Which event causes each change?

levels.py
from turtle import *
from ui import inside, draw_button

speed(0)
penup()
hideturtle()

xs = [-120, 0, 120, 120, 0, -120]
ys = [60, 110, 60, -50, -100, -50]
place = 0
tx = 0
ty = 0
score = 0
showing = False
time_left = 30
state = "title"


def big(text, x, y):
    goto(x, y)
    write(text, False, "left", ("Arial", 28, "bold"))


def draw_scene():
    clear()
    if state == "title":
        big("Catch the turtle", -140, 40)
        draw_button(-60, -180, 120, 40, "Play")
    if state == "play":
        goto(-190, 160)
        write(score, False, "left", ("Arial", 20, "normal"))
        goto(150, 160)
        write(time_left, False, "left", ("Arial", 20, "normal"))
    if state == "over":
        big("Game over", -90, 40)
        big(score, -10, -10)
        draw_button(-60, -180, 120, 40, "Again")
    goto(tx, ty)


def hop():
    global place, tx, ty, showing
    if state == "play":
        tx = xs[place]
        ty = ys[place]
        place = (place + 1) % len(xs)
        goto(tx, ty)
        showturtle()
        showing = True
        ontimer(hide, 700)
        ontimer(hop, 1000)


def hide():
    global showing
    if showing:
        showing = False
        hideturtle()


def tick():
    global time_left, state
    if state == "play":
        time_left = time_left - 1
        if time_left == 0:
            state = "over"
            hideturtle()
        else:
            ontimer(tick, 1000)
        draw_scene()


def start():
    global score, time_left, place, state
    score = 0
    time_left = 30
    place = 0
    state = "play"
    draw_scene()
    hop()
    ontimer(tick, 1000)


def clicked(x, y):
    global score, showing
    if state == "play" and showing and inside(x, y, tx - 20, ty - 20, 40, 40):
        score = score + 1
        showing = False
        hideturtle()
        draw_scene()
    if state != "play" and inside(x, y, -60, -180, 120, 40):
        start()


draw_scene()
onscreenclick(clicked)
listen()
mainloop()
Why does this game use state with three values instead of playing = True or False?

ModifyChange the code on purpose and check the result.

Levels and a moving turtle

Every 5 points, go up a level and make the turtle faster. Show the level at the top.

Need a hint?

In clicked, after score = score + 1: if score % 5 == 0 and delay > 400: level = level + 1, delay = delay - 150.

Another hint

Use ontimer(hop, delay) and ontimer(hide, delay - 300), and reset level and delay in start().

Make the turtle walk to each spot instead of jumping, in 10 small steps. It is harder to catch, and you can see where it is heading.

Need a hint?

move(): if state == "play" and steps > 0: tx = tx + dx, ty = ty + dy, goto(tx, ty), steps = steps - 1, ontimer(move, 50).

Another hint

hop(): dx = (xs[place] - tx) / 10, the same for dy, steps = 10, then move() instead of goto.

Try it yourselfYour edits are saved in this browser.

Every 5 points the level goes up and the turtle gets faster.

    Run your program. This ticks when your code does all of the above.

    The turtle walks to each spot in small steps.

      Run your program. This ticks when your code does all of the above.

      MakeBuild something new using what you have learned. Expect a few tries; that is normal.

      Your own game rule

      Add a rule that makes the game your own. Where this leads: MakeCode Arcade and microStudio give you sprites that move by themselves and screens to switch between. Under the hood they run the same loop you just built: update the data, check for hits, redraw.

      Try it yourselfYour edits are saved in this browser.
      Need a hint?

      Boss turtle: every tenth spot needs three clicks. Count hits in a variable and only score when it reaches 3.

      Another hint

      High score: a best variable that only changes when a game ends with a higher score, shown on the over screen.

      For teachers

      An optional lesson in the games direction. Students add screens (title, play, game over), levels that speed up and a turtle that walks between spots, the way game engines move sprites. About 45 minutes.

      Open the teacher guide: answers, solutions, slides and a worksheet.

      Students will be able to

      Before class

      state holds one of three words: "title", "play" or "over". It replaces True and False because a game has three screens, and two values cannot tell them apart. The moving turtle takes 10 small steps, one every 50 milliseconds, instead of jumping. (An animated move with speed() would make the program ignore clicks until the move finished.)

      Running the lesson

      1. Predict: when time runs out, tick sets state to 'over' and draw_scene draws the game-over screen.
      2. Investigate: find every line that changes state and say which event causes it.
      3. Levels: every 5 points, the level goes up and delay drops by 150, but never below 400. Ask why a minimum matters.
      4. Moving turtle: move() takes one step and asks the timer to run it again in 50 ms, until steps reaches 0. This is how game loops move things.
      5. Make: a high score or a boss turtle that needs three clicks. Both need one new variable.

      The hard part (on purpose)

      A state variable deciding which screen runs. Ask them to say which state the game is in after each click, and check the spelling is the same everywhere.

      When students get stuck

      Stretch and support

      Stretch: Add a boss turtle every tenth spot that needs three clicks, with its own hit counter.

      Support: Do Levels together; leave Moving turtle as an extra for those who finish.

      Where it leads

      MakeCode Arcade and microStudio give you sprites that move by themselves and screens to switch between. Underneath they run the same loop: update the data, check for hits, redraw.

      What each step is for

      Now you can
      You reached the endBack to Python functions and events