Skip to content

Framework Walkthrough

skytreader edited this page Sep 19, 2012 · 36 revisions

A.K.A, how to make anything work? This will show you how by discussing almost everything in core.py

To get started, first you need to create a...

GameConfig

GameConfig contains settings for games. Right now, there are only four available settings: clock rate, window size, window title, and frame rate. But of course, there's nothing stopping you from subclassing GameConfig to add in your own settings. Each setting is actually just a property of GameConfig.

To add your own settings, just create new properties and setters, done through Python decorators. It is important that you do them using decorators and not through making class attributes public. You will know why later but, for now, suffice it to say that you must call self.notify_subscribers() at the end of every setter.

After deciding your game's settings in GameConfig, you can then subclass...

GameScreen

The constructor for GameScreen takes in a single argument for the screen dimensions. Screen dimensions is expressed as an iterable (list, tuple, etc.) which has at least two elements: the width and the height, in that order. By default, in case your iterable has more than two elements, GameScreen will expect them to be the first two elements.

GameScreen is responsible for loading and drawing all the elements of your screen. Instantiate all PyGame Surfaces in setup. Then, override draw_screen to draw all that needs to be drawn. At the end, the most you should have with GameScreen are some animations. No user control.

Because to add user control you must subclass...

GameLoopEvents

The constructor for GameLoopEvents takes in a GameConfig object and a GameScreen object. There are several methods in GameLoopEvents which you must note.

First, you need to define the condition for which your main game loop should keep on going in method loop_invariant. This method simply returns a boolean on whether your loop should proceed or not. In the current code at the repo head (and not in Milestone 1; see more in the discussion about event handling below), GameLoopEvents uses loop_invariant to manage the pygame.QUIT event (i.e., the event triggered by clicking on a window's "close" button), so, unless you want to handle pygame.QUIT yourself, extending classes are advised to call on their parent's loop_invariant method and and the result with their own invariant condition.

Next, you need to mind the objects which you need in your loop. Surely, it won't do to instantiate them on every iteration of the loop. For that you have the loop_setup method. This method is called after the PyGame display has been invoked and before the loop (of course).

Finally, you can define what happens in the loop by overriding loop_event.

When is my GameScreen drawn in all this?

The GameScreen is drawn by the loop_event method of GameLoopEvents (the main one, not just any GameLoopEvents class). Therefore, when extending any GameLoopEvents class, it is important to call the parent's method so that it eventually cascades to drawing the screen.

Event handling

To handle events, override attach_event_handlers and call add_event_handler from there. add_event_handler expects two arguments. The first one is the PyGame Event object to be handled. The form of the second argument depends on the event. If it is pygame.KEYDOWN (key press event), you need to pass a dictionary that maps the constants GameLoopEvents.KEYCODE to the key code and GameLoopEvents.HANDLER to the handler function. For all other PyGame Events, the second argument is just expected to be a handler function.

Handler functions should expect one argument for the event.

pygame.QUIT

Note: Text below applies to the current code in the repo head and not for Milestone 1.

As noted above, the pygame.QUIT event is handled automatically. However, there's nothing preventing you from overriding this behavior. If, for some reason, you need to quit the program other than from pygame.QUIT, the stop_loop function has been provided. stop_loop takes one argument and is the default function invoked for pygame.QUIT. That said, stop_loop assumes that the argument passed is a PyGame event object but it does not use it anyway so you can pass anything you like.

(Actually, stop_loop, as the name implies, stops the game loop and does not actually stop the program/close the window. However, as per PyGame Objects default behavior, pygame.quit() is invoked right after the loop.)

As with everything in Python, I assume that we're all adults here and that no one overrides stop_loop. But if you really need to do so, make it so that it will have no need for it's sole parameter. Otherwise, things may break for you.


API Listing

This section lists the methods available in each class and discusses them in a more organized and formal manner compared to above.

Game Screen

Properties

screen_size (get only)

Functions

__init__(self, screen_dimensions)
Creates an instance of GameScreen. DO NOT instantiate images/surfaces here. Put instantiation code in setup() method.

screen_dimensions is an iterable with at least two elements. GameScreen expects that the first element of screen_dimensions is the width while the second one is the height.

setup(self)
Instantiate images/surfaces here. This method is invoked before the game loop starts.

draw_screen(self, window)
Here is where you put the code that will draw the screen. By itself, this should only be concerned as to how the screen should look like for this iteration of the game loop and not on how the screen came to be this way; encapsulate that elsewhere, in a Model, prefferrably.