My First Raspberry Pi Game – Part 12 – Scoring, done!

Parts: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.

Writing your first ever computer program on the Raspberry Pi.

Today, we finish!

Our game is almost done. All we need to do now is let you play several times, and give you a score at the end.

First, because we’re going to use it lots of times, we need to make the ready_screen function set its background colour properly. Open redgreen.py in LeafPad, and add a single line to the function ready_screen, making it look like this:

def ready_screen():
    screen.fill( pygame.Color( "black" ) )
    white = pygame.Color( "white" )
    write_text( screen, "Ready?", white, True )
    pygame.display.flip()

Previously, ready_screen was always the first thing we did, so we got away with not drawing a background colour because it starts off plain black. Now, we need to do it.

Next, let’s do the really interesting part. We want to play the game several times, and whenever we want to do something several times, we need a loop. This time we’ll use a for loop, letting us go through a list of things. Scroll to the very bottom, and change the code to look like this:

# We start from here

start()

for i in range( 10 ):

    ready_screen()

    wait()

    shape()

end()

The new lines are green above, and lines that haven’t changed except being indented by putting four spaces at the beginning are blue.

A for loop lets you run through a list of things, running the same code each time. A for loop always looks like for NAME in LIST where NAME is the name of a new variable, and LIST is a list of things. What we’ve done here is make a list of 10 numbers by calling the range function and giving it an argument of 10, and told Python to put the particular item of the list that we’re working on now into a variable called i.

So, the ready_screen, wait and shape functions will each get called 10 times. Each time they are called, i will be a different number. We’re not using i yet, so all that matters for the moment is that the code runs 10 times. Try it out by opening LXTerminal and typing ./redgreen.py, and you’ll see that you can play the game 10 times, and then it will finish.

Playing 10 times is all very well, but it’s not a lot of fun if I can’t see how well I’ve done at the end. Let’s keep track of our score.

We’ll award the player 1 point for every time they get it right, and no points if they get it wrong. The places where we know which of these has happened are in red_shape and green_shape. Let’s change them to pass back a score (either 1 or 0) depending on what you did:

def green_shape():
    ...the rest of green_shape is still here...

    pressed = shape_wait()

    if pressed:
        green_success()
        return 1
    else:
        green_failure()
        return 0
def red_shape():
    ...the rest of green_shape is still here...

    pressed = shape_wait()

    if pressed:
        red_failure()
        return 0
    else:
        red_success()
        return 1

I’ve abbreviated it above, but we’re not changing anything in these functions except at the very bottom, where we’re adding two return lines to each function.

Whenever the player succeeds, we return a score of 1 point, and whenever they fail we return 0 points.

We’re not doing anything with this score yet. We call the green_shape and red_shape functions from inside shape, so first let’s make sure shape passes back the answer to where we need it:

def shape():
    GREEN = 0
    RED   = 1
    shape = random.choice( [GREEN, RED] )

    if shape == GREEN:
        return green_shape()
    else:
        return red_shape()

shape doesn’t need to do anything special here – just take the answer coming from green_shape or red_shape and use the return statement to pass it back to us.

Now shape is giving us back an answer, we can use it in the main code right at the bottom:

start()

correct = 0

for i in range( 10 ):

    ready_screen()

    wait()

    correct += shape()

end( correct )

We’ve made a variable called correct that keeps hold of how many correct answers we’ve been given (i.e. the score). It starts off as zero, and every time we call shape we add on the answer that comes back. shape will either return 0 or 1, so correct will increase by either 0 or 1 each time.

The last thing we’ve done here is pass the answer (the player’s final score) into the end function so we can display it. To use this answer, we need to change end a bit:

def end( correct ):
    print "You got %d correct answers" % correct
    screen.fill( pygame.Color( "black" ) )
    white = pygame.Color( "white" )
    write_text( screen, "Thanks for playing!", white, True )
    msg = "Score: %d   Press a key to exit" % correct
    write_text( screen, msg, white, False )
    pygame.display.flip()
    pygame.event.clear()
    timed_wait( 0, press_events )

We changed the def line to allow us to pass in the score, giving it the same name we used below, correct. Then we added a line that prints out the answer into the terminal, just for good measure, and we modified the write_text line, splitting it into 2 parts – creating a variable called msg containing our message, and then using it on the next line.

Twice above we’ve used a nice feature of Python that makes building our own messages quite simple. If you write a string like "Score: %d Press a key to exit" you can substitute a number into it using the % “operator” as we’ve done (an operator is something like + or / that combines 2 things). Where the %d appears in the string, it gets replaced by the number inside the variable you supply (correct in our case). You can also substitute in other strings (using %s) and lots of other things if you want to. This allows us to put the score into a string and then print it on the screen.

If you try your game now you will see it counts how many right answers you got and tells you at the end. Wouldn’t it be better, though, if it told you how you were doing all the way through?

Scroll up to the ready_screen function and modify it to take two arguments and use them to keep us informed:

def ready_screen( go_number, correct ):
    screen.fill( pygame.Color( "black" ) )
    white = pygame.Color( "white" )
    write_text( screen, "Ready?", white, True )

    go_number_str = "Turn: %d    Score: %d" % ( ( go_number + 1 ), correct )

    write_text( screen, go_number_str, pygame.Color( "white" ), False )

    pygame.display.flip()

The arguments we take are called go_number and correct. correct will be the current score, as we’ve seen before, and go_number is the counter telling us how far we’ve got.

We use a slightly different form of the % operator here to substitute two values into a string instead of one. To do this, we put a list of values on the right instead of just one: ( ( go_number + 1 ), correct ). We need brackets around the outside so that Python knows it is a list and doesn’t just take the first value on its own. When we use a list like this, the values will be substituted in order, one for each %d (or %s or similar) that is in the string. You must always have the same number of %ds in the string as values in the list.

You may be wondering why we have to add one to go_number. We’ll see in a moment.

To be able to provide the two new arguments to ready_screen we need to change the code right at the bottom to look like this:

start()

correct = 0

for i in range( 10 ):

    ready_screen( i, correct )

    wait()

    correct += shape()

end( correct )

Remember when we made the for loop I mentioned that i would be a different number each time we ran the code inside the loop? We pass that number in to ready_screen where it will be used as the go_number. We also pass in the current score, correct.

The reason why we needed to add 1 to go_number inside ready_screen is that when you have a loop like for i in range( 10 ), the variable i actually gets the values 0, 1, 2, … with the last value being 9, instead of ranging from 1 to 10 as you might expect. The reasoning behind this is kind of lost in the mists of time, and kind of makes perfect sense, depending how you look at it. Anyway, believe me when I tell you that once you’ve got used to it you’re going to find it warm and comforting, but for now you may find it a bit weird.

And, on that typically strange note, we have finished! Try out your program, and you should find it tells you what go you’re on, and what your score is all the way through.

Something else you might like to do now is make your game run in full-screen mode (like many games). You can do that by changing the start function like this:

def start():
    global screen
    pygame.init()
    screen = pygame.display.set_mode( screen_size, pygame.FULLSCREEN )

If you have any problems, compare your version with mine here: redgreen.py

I’ve made a slightly extended version of the game that measures your reaction speed and gives you a score based on how quickly you press. In future I may even add more features. If you’d like to follow the project, you can find it here: redgreen on github.

I’ll be doing more series in the future, some for beginners like this one, and some more advanced topics. If you’d like to find out what I’m doing, subscribe to the blog RSS feed, follow me on Twitter or go to my YouTube page and subscribe.

My First Raspberry Pi Game – Part 09 – Lots more words

Parts: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.

Writing your first ever computer program on the Raspberry Pi.

We’re writing a really simple game – you have to press a key when you see green.

This time we’re going to add lots of instructions on each page so the player knows what to do.

Up until now, we’ve only had one piece of writing – the word “Ready?” when the game starts up. To make the game easy to use we want to write instructions on each page. We’ll take the code we wrote to say “Ready?” and turn it into a function that we can re-use for lots of different writing.

The first step we’re going to do will be “refactoring”, which just means changing our program without changing what it does.

Have a look at this code, that we’ve already got:

def start():
    global screen, ready_text
    pygame.init()
    screen = pygame.display.set_mode( screen_size )
    font = pygame.font.Font( None, screen_height / 5 )
    ready_text = font.render( "Ready?", 1, pygame.Color( "white" ) )

def quit():
    pygame.quit()
    sys.exit()

def ready_screen():
    textpos = ready_text.get_rect(
        centerx = screen.get_width() / 2,
        centery = screen.get_height() / 2
    )

    screen.blit( ready_text, textpos )
    pygame.display.flip()

The blue code here works together to write something on the screen. The top part inside start creates an image called ready_text, and the bottom part in ready_screen works out where to draw it, then draws it.

We’re going to extract these two bits out from where they are, and put them into a function we can re-use whenever we want to write something.

Make a function above start and call it write_text. Cut the lines in blue, and paste them into write_text. Rename the variable ready_text to rend (in three places) because it’s a rendered image of our writing. You should end up with something like this:

def write_text():
    font = pygame.font.Font( None, screen_height / 5 )
    rend = font.render( "Ready?", 1, pygame.Color( "white" ) )
    textpos = rend.get_rect(
        centerx = screen.get_width() / 2,
        centery = screen.get_height() / 2
    )
    screen.blit( rend, textpos )

Now make the code work almost exactly as before by making a call to this function from within ready_screen:

def ready_screen():
    write_text()
    pygame.display.flip()

You can try your program now and you should find it works exactly as it did before.

So far we haven’t make a very useful function, because it always writes “Ready?”. We can change that by adding arguments to the write_text function for the screen to write on, the text to write, and the colour of the writing. Now write_text looks like this:

def write_text( screen, text, color ):
    font = pygame.font.Font( None, screen.get_height() / 5 )
    rend = font.render( text, 1, color )
    pos = rend.get_rect(
        centerx = screen.get_width() / 2,
        centery = screen.get_height() / 2
    )
    screen.blit( rend, pos )

and ready_screen looks like this:

def ready_screen():
    white = pygame.Color( "white" )
    write_text( screen, "Ready?", white )
    pygame.display.flip()

(Notice that I made a variable called white and then passed it in.)

At this point, you can also delete the line above write_text that says ready_text = None because we don’t need that variable any more (we are just using the variable rend inside write_text) and you can remove ready_text from the first line of the start function, so it just reads global screen.

Again, you can try your program now and you should find it works exactly as it did before.

By working in gradual steps like this, we give ourselves small enough chunks of things to think about that we’re relatively unlikely to get confused and screw things up. As you work with longer programs I hope you’ll see how useful this technique can be.

The next step we want to take is to allow two different types of writing: either in the middle, big or at the bottom, smaller. Let’s add another argument, called big and make it control how big the writing is, and where it goes.

def write_text( screen, text, color, big ):
    if big:
        height = screen.get_height() / 5
        up = screen.get_height() / 2
    font = pygame.font.Font( None, height )
    rend = font.render( text, 1, color )
    pos = rend.get_rect(
        centerx = screen.get_width() / 2,
        centery = up
    )
    screen.blit( rend, up )

And let’s pass in True so we behave just as we did before:

def ready_screen():
    white = pygame.Color( "white" )
    write_text( screen, "Ready?", white, True )
    pygame.display.flip()

So far we’ve only said what happens when we pass True for big – if we pass False, height and pos will not be set, and the program will go wrong. Let’s fix that now:

def write_text( screen, text, color, big ):
    if big:
        height = screen.get_height() / 5
        up = screen.get_height() / 2
    else:
        height = screen_height / 12
        up = screen.get_height() - ( screen_height / 24 )
    font = pygame.font.Font( None, height )
    rend = font.render( text, 1, color )
    pos = rend.get_rect(
        centerx = screen.get_width() / 2,
        centery = up
    )
    screen.blit( rend, pos )

So now we can write small text at the bottom of the sreen. Let’s do that on the green_shape page, so the player knows to press a key. Change green_shape to look like:

def green_shape():
    green = pygame.Color( "green" )
    centre = ( screen.get_width() / 2, screen.get_height() / 2 )
    radius = screen.get_width() / 3

    screen.fill( pygame.Color( "white" ) )
    pygame.draw.circle( screen, green, centre, radius, 0 )

    write_text( screen, "Press something!", pygame.Color( "black" ), False )

    pygame.display.flip()

    pressed = shape_wait()

    if pressed:
        green_success()
    else:
        green_failure()

Now we can add some more text to green_success and green_failure saying well done when you won and bad luck when you lost:

def green_success():
    tick()
    green = pygame.Color( "green" )
    white = pygame.Color( "white" )
    write_text( screen, "Well done!", green, True )
    write_text( screen, "You pressed on green!", white, False )
    pygame.display.flip()
    pygame.time.wait( 2000 ) # Can't quit or skip!

def green_failure():
    cross()
    red   = pygame.Color( "red" )
    white = pygame.Color( "white" )
    write_text( screen, "Bad Luck!", red, True )
    write_text( screen, "Green means press something!", white, False )
    pygame.display.flip()
    pygame.time.wait( 2000 ) # Can't quit or skip!

Notice that we have added pygame.display.flip() to the end of each of these sections, so we can remove that line from the end of tick and cross. Also, we need to move our tick and cross pictures up so they’re not on top of the writing, so we’ll divide by 4 instead of 2 to get the middle of the picture:

def tick():
    colour = pygame.Color( "green" )
    w = screen.get_width() / 2
    h = screen.get_height() / 4
    points = (
        ( w - w/5, h - h/9 ),
        ( w,       h + h/5 ),
        ( w + w/3, h - h/3 ),
    )

    screen.fill( pygame.Color( "black" ) )
    pygame.draw.lines( screen, colour, False, points, 20 )
    pygame.display.flip()

def cross():
    colour = pygame.Color( "red" )
    w = screen.get_width() / 2
    h = screen.get_height() / 4
    left   = w - w/3
    right  = w + w/3
    top    = h - h/3
    bottom = h + h/3

    start1 = left, top
    end1   = right, bottom

    start2 = left, bottom
    end2   = right, top

    screen.fill( pygame.Color( "black" ) )
    pygame.draw.line( screen, colour, start1, end1, 20 )
    pygame.draw.line( screen, colour, start2, end2, 20 )
    pygame.display.flip()

And finally, let’s say goodbye at the end. Change end to look like this:

def end():
    screen.fill( pygame.Color( "black" ) )
    white = pygame.Color( "white" )
    write_text( screen, "Thanks for playing!", white, True )
    write_text( screen, "Press a key to exit", white, False )
    pygame.display.flip()

    pygame.event.clear()
    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False

Now we’ve got much more enlightened players of our game!

We’ve made lots of changes in lots of different places today. If something doesn’t work, check your version again mine: redgreen.py.

Next time we’ll show red squares as well as green circles, making the game significantly more interesting.

My First Raspberry Pi Game – Part 08 – Success and failure

Parts: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.

Writing your first ever computer program on the Raspberry Pi.

We’re writing a really simple game – you have to press a key when you see green.

Today we’re going to wait for a key press. If we get one, we’ll tell the player they did well. If not, we’ll tell them they are a bad person.

We’re going to change the green_shape function first, to make it wait for a key press (or give up waiting) and then tell the player what happened.

Find the green_shape function and add the new bit that I’ve highlighted in green, at the end:

def green_shape():
    green = pygame.Color( "green" )
    centre = ( screen.get_width() / 2, screen.get_height() / 2 )
    radius = screen.get_width() / 3

    screen.fill( pygame.Color( "white" ) )
    pygame.draw.circle( screen, green, centre, radius, 0 )

    pygame.display.flip()

    pressed = shape_wait()

    if pressed:
        green_success()
    else:
        green_failure()

green_shape is the function that shows a green shape to the player.

This new code does 2 things. First, it calls a function shape_wait (that we haven’t written yet) that waits for a key press. We are expecting this function to give us back an answer, which we will store inside a new variable, pressed.

Second, it checks the value of pressed, and calls a different function in each case. If a key was pressed, this is good (because we’re showing a green shape, so you’re supposed to press a key) so we call the green_success function (which we haven’t written yet either). If no key was pressed because we gave up waiting, we call the green_failure function (which we haven’t written yet!).

That covers everything we want to do today – all we have to do is write those 3 missing functions.

Let’s start with the hardest one – shape_wait. Go up to just above the green_shape function, and type this:

def shape_wait():
    """
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    """

    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN

    time_to_wait = 2000 # Display the shape for 2 seconds
    finished_waiting_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer( finished_waiting_event_id, time_to_wait )

There are a few things to explain here. First, the writing at the top just below the def line. This is the way we explain in Python what a function does and what it’s for. It’s optional, and we haven’t done it before, but I thought this function was interesting enough for us to provide some explanation. Notice the triple-quotes """ at the beginning and end. That is a way Python allows us to write longer strings of text that cover more than one line. The string starts at the first triple-quote, and ends at the last.

After our documentation string, we create a familiar variable event_types_that_cancel that holds on to all the types of event we are interested in – key presses and mouse clicks. Next we remember how long we are going to wait before giving up in another variable time_to_wait.

After that we do something a bit interesting. Up to now we have been dealing with “events” – things that happen such as mouse clicks, key presses and mouse movements, but we have only been responding to them, not creating them. The next 2 lines are how we create our own event, that we want to respond to later.

What we want to do is make an event happen in 2 seconds’ time, so that we can give up waiting when it comes. The way we do that is first create an “ID” for it. This is just a numeric “name” that we can use to talk about the same type of event later. In PyGame the right ID to choose for an event you created yourself is pygame.USEREVENT + 1 (and higher numbers if you need more than one). We don’t know what number PyGame has stored inside its own variable pygame.USEREVENT, and we don’t care – all we care about is that PyGame says if we use numbers bigger than that, we’ll be fine. If we use smaller numbers, we are going to clash with the built-in events like pygame.KEYDOWN that we have already seen.

Once we have an appropriate ID stored inside finished_waiting_event_id we are ready to ask PyGame to create an event that will happen in 2 seconds’ time. We do that by calling the set_timer function inside pygame.time.

Now continue the function by typing all this:

    pressed = False
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False
            pressed = True
        elif evt.type == finished_waiting_event_id:
            waiting = False

    pygame.time.set_timer( finished_waiting_event_id, 0 )

    return pressed

This is the code that waits for something to happen. It’s quite similar to the loop we saw in part 6, where we were also waiting for something to happen, but it’s slightly more complicated because we have to handle more possibilities.

This function will provide an answer to the code that called it, and the answer is going to be whether or not the player pressed a key. Providing an answer like this is called “returning a value” and we do it by writing a line like the last one here, using the return statement. The first line above creates a variable called pressed, which starts off set to False, meaning they haven’t pressed anything, and somewhere in between it might get set to True, and then the last line returns this answer – True or False for whether or not a key was pressed.

In between we have a loop similar to part 6 – we create a variable called waiting which tells us whether to keep looping, and then we loop using the while line through all the lines indented below it. The inside of the loop (the part that gets repeated) waits for an event to happen with pygame.event.wait and then has a series of if and elif sections, that do different things depending what type of event happened.

First (the if part), we check whether the player closed the window. If so, we call our function quit, that stops everything immediately.

Next (the first elif), we check whether a key or mouse button was pressed. If so, we make sure the value we will return inside pressed is updated to say a key was pressed (i.e. we make it True), and then we set waiting to False so that we will stop looping at the end of this repeat.

Now (the second elif), we check whether what happened was the special event we created earlier when we called set_timer. If so, we need to end the loop (so we set waiting to False), but no key was pressed, so we leave pressed as it was.

Finally, if the event that happened didn’t fit any of our categories (for example it might have been a mouse movement event), we do absolutely nothing because none of the if or elif sections was triggered. We jump straight back to the start of the loop and start waiting for the next event to happen.

So, eventually, either an interesting event happens, or the “we’ve been waiting too long” event we created happens, and we come out of the while loop. The last thing we have to do is cancel the “we’ve been waiting too long” event, just in case it hasn’t happened yet – we don’t want it confusing us later. We do that by calling set_timer again, with the same ID as before, but with 0 for the amount of time to wait – this tells PyGame we’re not interested in that event any more.

Once we’ve done that we return the answer about whether a key was pressed, and we’re done with shape_wait.

Next up are green_success and green_failure. These tell the player whether they succeeded or failed – did they manage to press when they saw green?

They’re both quite simple. Type these just above green_shape:

def green_success():
    tick()
    pygame.time.wait( 2000 ) # Can't quit or skip!

def green_failure():
    cross()
    pygame.time.wait( 2000 ) # Can't quit or skip!

If a key was pressed on green, we want to draw a “tick” mark on the screen, so we call a function tick that we’ll write in a moment. Similarly, if a key wasn’t pressed, we will draw a cross.

Drawing shapes is fairly straightforward, but a bit verbose. Just above green_success type these 2 functions:

def tick():
    colour = pygame.Color( "green" )
    w = screen.get_width() / 2
    h = screen.get_height() / 2
    points = (
        ( w - w/5, h - h/9 ),
        ( w,       h + h/5 ),
        ( w + w/3, h - h/3 ),
    )

    screen.fill( pygame.Color( "black" ) )
    pygame.draw.lines( screen, colour, False, points, 20 )
    pygame.display.flip()


def cross():
    colour = pygame.Color( "red" )
    w = screen.get_width() / 2
    h = screen.get_height() / 2
    left   = w - w/3
    right  = w + w/3
    top    = h - h/3
    bottom = h + h/3

    start1 = left, top
    end1   = right, bottom

    start2 = left, bottom
    end2   = right, top

    screen.fill( pygame.Color( "black" ) )
    pygame.draw.line( screen, colour, start1, end1, 20 )
    pygame.draw.line( screen, colour, start2, end2, 20 )
    pygame.display.flip()

Both of these functions get some variables ready, do some maths on them to decide where on the screen to start and end the lines they are drawing, and then draw the lines (after making a black background with screen.fill).

The tick is drawn by passing in a list of 3 points on the screen to the pygame.draw.lines function, and the cross is drawn using two separate calls to pygame.draw.line, one for each line. After we’ve drawn our lines, we call pygame.display.flip as normal to show them on the screen.

With those two functions in place, we’re ready to try it out. Open LXTerminal in the usual way, and type our usual incantation:

./redgreen.py

If all has gone well, you should see the green shape as before, but when you press a key a tick should appear. If you don’t press a key, after a while a red cross should appear.

If that doesn’t happen, check your typing really carefully, and compare your version with mine: redgreen.py.

Next time, we’ll add some writing explaining what you should do at each step.

My First Raspberry Pi Game – Part 07 – A green circle

Parts: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.

Writing your first ever computer program on the Raspberry Pi.

We’re going to write a game that tests your reactions – press a key when you see green, but don’t when you see red.

Today we see some of what we have been waiting for – a genuine bona-fide green circle, made by you!

We’re going to need some random numbers, so edit your program in LeafPad, and add a line, just before import pygame near the top:

import random

This makes the “random” module available to us so we can make some numbers later.

Remember we had a function called “wait”, but it never did anything? It was supposed to wait for a random amount of time before we showed our green or red shape. Let’s write it now. Find the empty wait function and replace it with:

def wait():
    time_to_wait = random.randint( 1500, 3000 ) # Between 1.5 and 3 seconds
    pygame.time.wait( time_to_wait ) # Note bug: can't quit during this time

The first line makes a variable time_to_wait and puts a random number into it. The random.randint function gives us a random number between the two numbers we supplied, so here between 1,500 and 3,000. time_to_wait is a time in milliseconds, so this means between one and half and three seconds.

After the closing bracket, we have a hash symbol #, and then some writing. This is a “comment”, and it is completely ignored by Python. It’s just for us.

[As time goes on, I hope you will begin to see programming more and more as talking to other people, not just to the computer. It’s fairly easy to write a computer program, but much harder to understand one written by someone else. Most programs live a long time, and people need to understand them to keep them up-to-date, so making them as easy to understand as possible is very important. Comments are one way to help people understand, but in a way they are a last resort – if possible, the code itself should be so easy to understand that you don’t need many comments. Here, I thought that the translation between seconds and milliseconds might be helpful to someone looking at this later.]

The next line uses a function inside PyGame’s time module to wait for the amount of time we give it (in milliseconds, stored in time_to_wait). Note that this is not the same wait function we have seen before, pygame.event.wait. That one waits forever for an event to happen, but this one waits (and can’t be interrupted) for the amount of time we say.

I’ve added another comment to this line saying that there’s a bug in our program: if we write it like this, you can’t actually quit the game by closing the window while we’re waiting. The pygame.time.wait function won’t be interrupted by the window being closed, so we’ll ignore it. This is almost unbearably rude, but don’t worry – we’ll fix it soon (ish).

And now for the really exciting part: we’re going to draw a green shape on the screen. Let’s make a function, just above the shape function, called green_shape:

def green_shape():
    green = pygame.Color( "green" )
    centre = ( screen.get_width() / 2, screen.get_height() / 2 )
    radius = screen.get_width() / 3

    screen.fill( pygame.Color( "white" ) )
    pygame.draw.circle( screen, green, centre, radius, 0 )

    pygame.display.flip()

This code makes a variable green holding onto the colour green, one called centre holding the co-ordinates of the centre of the screen, and one called radius holding the size of the circle we want to draw.

Then it uses the fill function on screen to colour in the screen white, and then draws our circle with a call to pygame.draw.circle, using the variables we have prepared as arguments, telling it where to draw the circle, in what colour, and what size.

Finally it uses flip as before to tell PyGame we have finished.

The last piece of today’s jigsaw is just to call the function we created above. Find the empty shape function, and make it look like this:

def shape():
    green_shape()

This literally just means run the green_shape function.

Take a deep breath, prepare to be excited, open LXTerminal and run our new program in the usual way:

./redgreen.py

If all has gone well, the ready screen will appear for a couple of seconds, before a white screen with a big green circle on it appears. This will then go away when you press a key.

If something goes wrong, check back what you typed, and compare your version against mine: redgreen.py.

Next time, we’ll find out whether you pressed a key or were too slow!

My First Raspberry Pi Game – Part 06 – A better class of waiting

Parts: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.

Writing your first ever computer program on the Raspberry Pi.

Today we wait, but better, while we show the ready screen we made last time. In the process we cover two of the most important things you need to understand to write computer programs.

The program we’ve written so far can wait around for something to happen, but it can’t distinguish between the various possible “somethings” that might happen. Specifically, it finishes very abruptly if we even move our mouse over the window!

In this part we’ll teach our program to ignore the movements of the mouse, but keep looking out for events it is interested in. We’ll also make it able to exit quickly if someone closes its window.

First, we need to get hold of the sys object (actually it’s called a “module”) which allows us to do things with the system, like quit, which is what we’re going to do with it.

Open up LeadPad again, and immediately above the import pygame line, add this:

import sys

Now we’re going to write a new function that allows us to exit the game immediately, whenever we want to. Just above the def ready_screen() line, add this new function:

def quit():
    pygame.quit()
    sys.exit()

This function tells PyGame we are finishing (by calling pygame.quit), then exits: sys.exit() stops everything dead.

Finally, we make our waiting a bit posher, by editing the end function look like this:

def end():
    pygame.event.clear()
    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False

There’s quite a lot here, but we’ll go through it line by line.

First, we clear the events as before using pygame.event.clear to make sure anything that has already happened doesn’t interfere with us.

Next we make a variable called event_types_that_cancel that is a list of all the types of events that we care about. They are key presses (KEYDOWN), and mouse clicks (MOUSEBUTTONDOWN). We’ll use this variable later.

Now we make another variable, called waiting. We use this to decide when we’ve finished. We set it to True, which means we are waiting, i.e. we haven’t finished.

The next line while waiting is the start of a loop, which means a piece of code we run lots of times. A “while” loop keeps on running until something happens. In this case, the lines after while waiting: will run again and again until the waiting variable becomes False.

The rest of the lines are all “inside” the loop, meaning they run again and again until the loop finishes. Notice that they are indented by a further 4 spaces to show that they are inside. If we had more lines later that were not indented, they would run after the loop had finished.

The first line creates a variable called evt that holds on to what came back when we called the function pygame.event.wait(). This function waits until an event happens. When an event happens, it could be a mouse movement, or a key press, or one of several other types of events. The old version of this code just called pygame.event.wait(), but didn’t look at what type of event happened. That’s why it finished even when you moved the mouse over the window. This new version captures the event that happened in the evt variable, and then does different things depending on what type of event it was.

The line starting with if lets us decide what to do based on what is happening. The first decision we make is evt.type == pygame.QUIT, which means if the type of event we have found is a special QUIT event. This type of event happens if the user closes the window by clicking the “X” in the top right-hand corner. If this happened, we go on to the next line, which calls the quit() function we created earlier. If this didn’t happen, we skip that line, and go on to the next one that is only indented as much as the original if line.

The next line after we skip is the one starting with elif. The “condition” we are checking (decision we are making) is whether evt.type in event_types_that_cancel which means is the event we have seen one of the ones we are interested it – is its type one of the things in event_types_that_cancel. event_types_that_cancel is a list containing the event type for mouse clicks, and the one for key presses, so if something interesting happened we will go to the next line, which is waiting = False. This sets waiting to False, meaning we will stop the loop. If the event was a different type (e.g. a mouse movement), we will skip the waiting = False line as well.

If neither of the conditions was true (so the event was not the user closing the window, or pressing a key or clicking the mouse) then we do nothing in the if and elif bit, and because we’re in a loop, we go back to the beginning and wait() again for another event. This goes on forever until one of the events we’re interested in happens. When it does, we either quit() immediately, or stop looping because waiting becomes False (meaning the while line lets us escape). When we’ve stopped looping we carry on, leaving the end function, and get to the end of the program.

If you can understand this you have just grasped two of the most important parts of programming – looping and conditional execution.

I suggest a long cup of tea.

While you’re sipping, try out our new program by opening LXTerminal as before, and typing the usual incantation:

./redgreen.py

Notice this time it doesn’t quit when you move the mouse, but it does if you click, or press a key, or close the window.

If it doesn’t work compare your program with my version: redgreen.py.

Next time we’ll make an actual green shape appear, as promised a long time ago!