Voila is a simple scene engine for pygame. It provides a very basic functionality of managing scene transitions and game exit via straightforward mechanism called fetchcalls.
Although written in 2024 for internal use, voila is made public in 2026 to perhaps help novice pygame users or experienced programmers tired of rewriting the same code over and over again.
In no way it tries to appear as an example of perfect code, but rather a simple template that can save time and hopefully even support a small-to-middle sized project.
Voila was tested on Python 3.11.4 with Pygame 2.5.2.
Voila relies on 3 main classes:
Enginemanages scene-transitions and game exits.Scenedefines a common api forEngine. All scenes must inheritSceneclass.Fetchcalldefines a message format that scenes send toEnginewhenever they want something done.
Engine(title: str, resolution: tuple[int, int], fps: int, scenes: tuple[Scene], default_scene_id: str)
title- title of the pygame window.resolution- resolution of the window.fps- desired fps count. If actual fps is greater, engine will attempt to slow game down by a difference between desired and actual inter-frame time.scenes- an array of all scenes that can potentially be entered in the game.default_scene_id- id of the scene that is desired to be entering (e.g. main menu).
Engine is implemented in a singleton pattern, meaning that calling Engine() in multiple
places inside one project will return the same instance. This allows for easier access in
a project with multiple files.
Scene(id: str) - engine distinguishes scenes by ids.
A custom scene can be made to function by redefining this 4 methods:
__init__(self)- if (re)defined, this must callsuper().__init__(id: str)handle_event(self, event)- allows scene to capture key-presses, mouse clicks, etc. This method gets called by engine individually for each event.update(self, dt)- allows scene to 'think'. Delta time is passed in seconds.draw(self, screen)- allows scene to draw on a screen. By default, engine does not clear screen surface (i.e. there is noscreen.fill('black')in game loop), but only flips display after the scene returns draw.
Other important method is set_fetchcall(fetchcall: Fetchcall). For example, if
the scene wants to quit the game upon left mouse button click, the code should resemble this:
def handle_event(self, event):
...
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
self.set_fetchcall(Fetchcall(Fetchcall.QUIT))
...More on fetchcalls below.
Fetchcall(code: int, *args) - code defines the type of fetchcall.
Fetchcall is essentially a request that any scene can make to ask the engine to do something with the game. It is intended to be expandable, so that any reasonable game-wide action scene would like to be done can be implemented by inhereting the fetchcall class.
The two predefined fetchcalls are QUIT and CHANGE_SCENE:
QUITfetchcall has code 0 and requires no arguments.CHANGE_SCENEfetchcall has code 1 and requires single argument: id of the desired scene.
Fetchcall class defines fetchcall codes, so instantiating a QUIT fetchcall might look like Fetchcall(Fetchcall.QUIT),
and instantiating a CHANGE_SCENE fetchcall might look like Fetchcall(Fetchcall.CHANGE_SCENE, 'another_scene_id').
Fetchcall arguments should be passed as regular method arguments, not enclosed into an array
(i.e. Fetchcall(code, arg1, arg2, arg3, ...) and NOT Fetchcall(code, (arg1, arg2, arg3, ...))),
as python will enclose them into an array automatically.
IMPORTANT when Scene.set_fetchcall() is called, the Scene instance stores provided fetchcall in
the internal variable _fetchcall, so it can be retrieved by engine when it is time to do so. This implies
that a given scene can only store 1 fetchcall at a time. For this reason, Scene has an internal preference
system: fetchcalls with lower codes have higher priority. For example, if you ask scene to execute
QUIT fetchcall and then to execute CHANGE_SCENE fetchcall, the latter one will simply be ignored. This is precisely
why QUIT has code 0 and CHANGE_SCENE 1.
Full example can be found in demo.py.