Create a simple game menu with pygame pt. 1 – Writing the menu options to the screen

Please note that I used python 2.7 for this tutorial. If you are using python3.x, this code may not work. I have adapted this tutorial to Python3 in this blog here.

On my quest to write my own game using the pygame library, I noticed that there is not a single tutorial on how to write your own Game Menu interface. There are two separate modules (python-pygame-menu and KezMenu) that allow you to create a menu, but I wanted to have a learning curve and decided to write it myself.

I share my solution in an attempt to get the ball rolling again. I am aware that my solution may not be elegant in places (or in fact good code), but this is what I came up with. If you think you can do better, write your own tutorial and link it in the comments, I am more than happy to read it and learn a thing or two. This is my first tutorial of any kind, so hopefully I can give it a nice structure. Ok, enough waffling, let’s hit it!

Tutorial

First things first. I will assume that you are familiar with python and with pygame. If not, best come back when you worked through a bunch of basic tutorials (Sorry for the link. I had to :D).

 

For those of you who learn best by reading code, the entire code for this segment is given below.

We shall start by setting the basic scene, i.e. create a black window that doesn’t terminate due to an endless loop. You will hopefully have seen all this already in some of the pygame beginner’s tutorials.

#!/usr/bin/python

import pygame

pygame.init()

class GameMenu():
    def __init__(self, screen, bg_color=(0,0,0)):

        self.screen = screen
        self.bg_color = bg_color
        self.clock = pygame.time.Clock()

    def run(self):
        mainloop = True
        while mainloop:
            # Limit frame speed to 50 FPS
            self.clock.tick(50)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    mainloop = False

            # Redraw the background
            self.screen.fill(self.bg_color)
            pygame.display.flip()

if __name__ == "__main__":
    # Creating the screen
    screen = pygame.display.set_mode((640, 480), 0, 32)
    pygame.display.set_caption('Game Menu')
    gm = GameMenu(screen)
    gm.run()

This is our starting point, from there we need to think, what we actually need in a bog standard Game Menu. Generally, there are options such as Start, Settings and Quit. In order to do that, we will need to make them appear on the screen. For this, we shall use the pygame’s Font module.

So, we define a tuple of strings (here Start and Quit) that should be in the menu. We shall do this in the “if __name__ …” block before the definition of the gm variable. Other conceivable options could be Settings or Highscore, for example.
Of course, we shall then pass this to the GameMenu class.

class GameMenu():
    def __init__(self, screen, items, bg_color=(0,0,0)):

        ...
        self.items = items

...
if __name__ == '__main__':
    ...
    menu_items = ('Start', 'Quit')
    ...
    gm = GameMenu(screen, menu_items)

Now that this is settled, we can finally use the pygame.font module to do what we came here for. So, let’s get to it, to add flexibility, I will give the user the option to define a font, the font colour and a font size, but set it to a default.

class GameMenu():
    def __init__(self, screen, items, bg_color=(0,0,0), font=None, font_size=30,
                    font_color=(255, 255, 255)):

        ...
        self.font = pygame.font.SysFont(font, font_size)
        self.font_color = font_color

Now let’s get to the part, where we make it appear on screen. For this, we need to make use of the font module’s render() method. Let’s just do this in a line below self.font_color.

class GameMenu():
    def __init__(self, screen, items, bg_color=(0,0,0), font=None, font_size=30,
                    font_color=(255, 255, 255)):

        ...
        self.items = []
        for item in items:
            label = self.font.render(item, 1, font_color)
            self.items.append(label)

Now in order to get these texts to the screen, we need the blit() method. Let’s try that.

class GameMenu():
    ...
    def run(self):
        mainloop = True
        while mainloop:
            ...
            # Redraw the background
            self.screen.fill(self.bg_color)

            for label in self.items:
                self.screen.blit(label, (100, 100))

            pygame.display.flip()

I am sure, you have already spotted the problem. All the texts are rendered to the same spot. We do want them centred in all aspects. This requires a little maths and the dimensions of the self.screen and of the rendered Font objects. As you may have already guessed, we will make use of the get_rect() method to obtain both width and height. The rest, I am sure, you will figure out for yourselves.
To store all these values in place, we shall use a list of lists. I found it good practice to store general values such as name and dimensions, because I very often noticed that I needed them later, anyway. In addition, adding the string of the Font object is very useful for debugging.

class GameMenu():
    def __init__(self, screen, items, bg_color=(0,0,0), font=None, font_size=30,
                    font_color=(255, 255, 255)):
        self.screen = screen
        self.scr_width = self.screen.get_rect().width
        self.scr_height = self.screen.get_rect().height

        ...

        self.items = []
        for index, item in enumerate(items):
            label = self.font.render(item, 1, font_color)

            width = label.get_rect().width
            height = label.get_rect().height

            posx = (self.scr_width / 2) - (width / 2)
            # t_h: total height of text block
            t_h = len(items) * height
            posy = (self.scr_height / 2) - (t_h / 2) + (index * height)

            self.items.append(item, [label, (width, height), (posx, posy)])

Now, before this works, we need to, of course, readjust our blit() method.

class GameMenu():
    ...
    def run(self):
        mainloop = True
        while mainloop:
            ...
            for name, label, (width, height), (posx, posy) in self.items:
                self.screen.blit(label, (posx, posy))

And now, we successfully brought our two menu options to the screen. In the next part of this tutorial series, I will implement how to recognise the mouse cursor and highlight the choice.

I hope this section was helpful. If you have any comments, suggestions or improvements, please feel free to comment below. I am always happy to hear constructive criticism.

——————————————————

Here’s the entire code:

#!/usr/bin/python

import pygame

pygame.init()

class GameMenu():
    def __init__(self, screen, items, bg_color=(0,0,0), font=None, font_size=30,
                    font_color=(255, 255, 255)):
        self.screen = screen
        self.scr_width = self.screen.get_rect().width
        self.scr_height = self.screen.get_rect().height

        self.bg_color = bg_color
        self.clock = pygame.time.Clock()

        self.items = items
        self.font = pygame.font.SysFont(font, font_size)
        self.font_color = font_color

        self.items = []
        for index, item in enumerate(items):
            label = self.font.render(item, 1, font_color)

            width = label.get_rect().width
            height = label.get_rect().height

            posx = (self.scr_width / 2) - (width / 2)
            # t_h: total height of text block
            t_h = len(items) * height
            posy = (self.scr_height / 2) - (t_h / 2) + (index * height)

            self.items.append([item, label, (width, height), (posx, posy)])

    def run(self):
        mainloop = True
        while mainloop:
            # Limit frame speed to 50 FPS
            self.clock.tick(50)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    mainloop = False

            # Redraw the background
            self.screen.fill(self.bg_color)

            for name, label, (width, height), (posx, posy) in self.items:
                self.screen.blit(label, (posx, posy))

            pygame.display.flip()

if __name__ == "__main__":
    # Creating the screen
    screen = pygame.display.set_mode((640, 480), 0, 32)

    menu_items = ('Start', 'Quit')

    pygame.display.set_caption('Game Menu')
    gm = GameMenu(screen, menu_items)
    gm.run()
Advertisements

4 thoughts on “Create a simple game menu with pygame pt. 1 – Writing the menu options to the screen

  1. Pingback: Create a simple game menu with pygame pt. 2 – Recognising the mouse | (Auto-)Didactic Programming

  2. Great post! I know this post is a few years old now, but I’m just coming across it for the first time. I’m wondering — why not use a dict, instead of a list of lists, for storing the item data?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s