Game Scene Manager in Python + pyame

I found an excellent article in Spanish that shows how to build a Scene Manager for your games, which makes things easier once you start adding different screens to your little creation. It’s a great way to avoid headaches.

Just listen to Doge: “Many chaos, much scenes, such headaches, wow!”

Before we start, the article can be found here, if you are interested and can read it (more info at the end).

My intention is to translate the original into English as well as I can since I’m not a native speaker, and second, use it and apply it to games made on Invent Your Own Computer Games with Python. I won’t obviously re-post every game found on that tutorial… or maybe yes, if I can find a way to refine them and improve them, and learn something along the way. So, lets get into it!

Managing Scenes with Pygame

A video game is not formed by only one screen, but many, like an introductory menu, a map screen, an inventory screen, scores screen. This screens are called scenes, and each one of them represent something specific in our game.

Changing a scene can be complicated, remember you have to keep the game loop active while doing so. The problem is that the logic, events and draw processes on each scene might be, and normally are, different. For example, on a menu the program will have to manage events as pushing buttons and showing score rankings, while another will have to draw different things.

The Director object and the master Scene

Being different between them, all the scenes are going to share the features of the game loop, meaning that they must be initialized, updated, managed and drawn on the screen. Therefore, we could create a superclass class that would take care of doing all that with the active scene, regardless of which one is it.

The Director object

Instead of managing the game loop through the main function of our program, we are going to create a Director class that will be in charge of managing the game loop, which will receive any scene and will execute the scene’s methods for updating, events and drawing processes.

# -*- encoding: utf-8 -*-

# Modules
import pygame, sys

class Director:
    """Represents the main object of the game.

    The Director object keeps the game on, and takes care of updating it,
    drawing it and propagate events.

    This object must be used with Scene objects that are defined later."""

    def __init__(self):
        self.screen = pygame.display.set_mode((640, 480))
        pygame.display.set_caption("Game Name")
        self.scene = None
        self.quit_flag = False
        self.clock = pygame.time.Clock()

    def loop(self):
        "Main game loop."

        while not self.quit_flag:
            time = self.clock.tick(60)

            # Exit events
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.quit()
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        self.quit()

            # Detect events
            self.scene.on_event()

            # Update scene
            self.scene.on_update()

            # Draw the screen
            self.scene.on_draw(self.screen)
            pygame.display.flip()

    def change_scene(self, scene):
        "Changes the current scene."
        self.scene = scene

    def quit(self):
        self.quit_flag = True

This object must be created within our main function instead of a game loop. Let’s explain this:

The class constructor creates the screen with pygame, we give it a title and we set up the clock. We also define some variables, like self.scene = None
which will be the scene we would like it to represent, none for the moment. Also self.quit_flag = False, which will help us to know when to quit the loop.

Then we have the loop method, which contains the game loop under the condition self.quit_flag, when this condition is True, the loop will break, to do so, we only have to call the quit method.

Once in the loop, the first thing is to set the frame rate, in this  case 60 frames per second. After this we check for if any exit event has occurred, if yes, we call self.quit().

Afterwards we call self.scene.in_event(), but we have to assign a scene first, which leads us to our next section.

The Scene class

The attributes of this class must be inherited by all the scenes that we use in our game.

class Scene:
     """Represents a scene of the game.

     Scenes must be created inheriting this class attributes
     in order to be used afterwards as menus, introduction screens,
     etc."""

     def __init__(self, director):
         self.director = director

     def on_update(self):
         "Called from the director and defined on the subclass."

         raise NotImplementedError("on_update abstract method must be defined in subclass.")

     def on_event(self, event):
         "Called when a specific event is detected in the loop."

         raise NotImplementedError("on_event abstract method must be defined in subclass.")

     def on_draw(self, screen):
         "Se llama cuando se quiere dibujar la pantalla."

         raise NotImplementedError("on_draw abstract method must be defined in subclass.")

A Scene object receives a Director instance during instantiation, and it is this class that implements the methods (abstract ones in this case) on_update, on_event and on_draw, expecting the subclass to define them, otherwise it will raise an exception We are going to define a super simple Scene subclass and fill those methods (with nothing, for now):

class SceneHome(scene.Scene):
    """ Welcome screen of the game, the first one to be loaded."""

    def __init__(self, director):
        scene.Scene.__init__(self, director)

    def on_update(self):
        pass

    def on_event(self):
        pass

    def on_draw(self):
        pass

Our scene has inherited from Scene superclass (scene.Scene syntax depends on where you coded the Scene class). We implement the three methods, but it will do nothing so far.

Let’s initialize a Scene, we’ll do it on the main file:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Modules
import pygame
import director
import scene_home

def main():
    dir = director.Director()
    scene = scene_home.SceneHome(dir)
    dir.change_scene(scene)
    dir.loop()

if __name__ == '__main__':
    pygame.init()
    main()

This is the main file of our project, as we can see we import three modules, pygame, the director module containing the Director class, and the scene_home module with the Scene subclass that we defined before and that will be the first one used.

On the main function we instantiate a Director with dir = director.Director(). Afterwards we create our Scene: scene = scene_home.SceneHome(dir) and we call the method change_scene of the Director instance using the new Scene as a parameter.

Afterwards, dir.loop() will initiate the game loop.

About the Director object

On a final note, the Director object is the one who makes the calls of the Scene object on_event, on_update, on_draw and later the pygame.display.flip() to draw the screen.

Now we have a way to load any scene we want from our Director object with its own methods for updating, managing events and drawing. To change the scene we only need to call the dir.change_scene(scene) method passing the new scene as a parameter.

Final notes

So this is it. I’ve tried to translate the article as well as I can, but maybe you can spot some mistakes. Feel free to inform me through the comments, and feel free to use this examples on your own code, since I guess that was the author’s intention from the beginning.

This post is a translation (with slight touches here and there) of this.

The author is Adrián Guerra Marrero.

Also, the code on the original article was intended for Python 2.X, I think it should work on Python 3.X, but again, if you spot a mistake just let me know so I can fix it.

My next step is to put this code to use and create a simple game with it. Let’s see how that goes.

Advertisements
Tagged , , , , , ,

3 thoughts on “Game Scene Manager in Python + pyame

  1. adrigmadrigm says:

    Hello, I am the original author. The system is inspired by the original Python Cocos2D library. That has now become so famous with the IOS version. Utlizando yet still manage this system.

    A greeting.

    • nihdez says:

      Hey! Thanks for your reply! I was a fan of your site “Razón Artificial”. It’s a pity you don’t update it anymore. Hope everything is ok for you!

  2. Congratulations, both to the original author and you Nicolás. This is a brief yet great scene manager.

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s

Advertisements
%d bloggers like this: