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.