The Hullet Bells – Part 4 (Insert Title Here)

The title screen! At this point in game development, it’s probably a waste of time, especially if you’ve already decided to implement a handler system that will let you slot the title screen in at the end. But what’s done is done, so here it is!

The title screen!
They should have sent a poet.

Obviously a work of art. In this post, I’d like to talk a bit about the menu system and the Drawable class. The latter forms an important part of the game too, so it’s worth examining.

The menu system is fairly basic. The title screen handler has a collection of Buttons, with each Button containing a function that will be called when it is chosen. These buttons inherit from the TitleScreenElement class, which in turn inherits from the Drawable class.

class Drawable(object):
  def __init__(self, x, y):
    self.x = x
    self.y = y

  def setX(self, x):
    self.x = x

  def setY(self, y):
    self.y = y

...

class TitleScreenElement(drawable.Drawable):
  def __init__(self, x, y, text):
    super(TitleScreenHandler.TitleScreenElement, self).__init__(x, y)
    self.text = text
    font = pygame.font.Font(None, 24)
    self.renderText = font.render(text, 1, (255,255,255))
    self.textPos = self.renderText.get_rect(centerx=x, centery=y)

  def setFontSize(self, size):
    font = pygame.font.Font(None, size)
    self.renderText = font.render(self.text, 1, (255,255,255))
    self.textPos = self.renderText.get_rect(centerx=self.x, centery=self.y)

class Button(TitleScreenElement):
  def __init__(self, x, y, text, func):
    super(TitleScreenHandler.Button, self).__init__(x, y, text)
    self.text = text
    self.selected = False
    self.func = func

  def run(self):
    if self.func:
      self.func()
    else:
      print("No stuff!")

  def toggleSelect(self):
    self.selected = not self.selected
    if (self.selected):
      self.setFontSize(28)
    else:
      self.setFontSize(24)

The title itself is a TitleScreenElement, while the menu items are Buttons. As you may have noticed, each Button has a notion of being ‘selected’, which in turn affects how it appears on the screen. The title screen handler itself also contains a variable that tracks the index of the currently selected Button. It might be more accurate to call it the ‘hovered’ Button, as selection implies that it has been chosen. When a different menu item is chosen, the buttons toggle their selected status and change their font sizes.

Navigating the menu and activating a menu item is managed by an InputHandler (which we’ll discuss next time) and a number of functions. These functions adjust both the currently selected Button according to both the buttons and the title screen handler, as well as actually running the associated function. In the current version, one button will change the current handler to the Game handler, while the other terminates the game cleanly by ending the main loop.

... In the title screen handler ...
  self.buttons = [TitleScreenHandler.Button(self.game.xRes // 2, 300, "Start Game", self._startGame),
                  TitleScreenHandler.Button(self.game.xRes // 2, 400, "Quit", self._inputQuit)]
...
  def _inputQuit(self):
    self.running = False

  def _startGame(self):
    fadeToHandler(self.game.screen, 0.1, GameScreenHandler(self.game), self.game)

The logic for changing the selected button looks like this (without the corresponding _selectionDown function):

...
  self.inputHandler.addEventCallback(self._selectionUp, pygame.K_UP, pygame.KEYDOWN)
...
  def _selectionUp(self):
    self.buttons[self.selected].toggleSelect()
    self.selected -= 1
    self.selected = (len(self.buttons) - 1) if (self.selected < 0) else self.selected
    self.buttons[self.selected].toggleSelect()
    print(self.selected)
...

That covers the higher-level stuff for the title screen, so what about the Drawable base class we talked about earlier? Drawables are currently very simple: They contain an x and y coordinate, and nothing more. In the future, they will also contain Animations, the foundation for all displayed images. For Buttons, we currently create the text on the fly via the pygame.font library, but these will eventually be replaced by images if we’re going to be making a seriously gorgeous title screen.
Animations are only partially implemented, but will be heavily ‘inspired’ by the implementation in my platform game. An Animation is a set of one or more pygame surfaces, with the current image cycled every N frames. Animations may loop (not necessarily from the first frame). A Drawable will contain a collection of Animations, as well as an index to the current animation. It’s a very simple system, and if we need to layer something more fancy on top of it later (particle effects, lighting..) it won’t be too nightmarish.

Next time we’ll look at the input handling framework. The write-up is bit of a beast, but the actual logic is pretty simple! If you haven’t already, you can see the list of all Hullet Bells articles here. There’s even a link to the Github repo, if you can’t wait for the play-by-play on this blog.