All posts by mocean

5b. Multiball – commonly asked questions

Multiball is largely managed by SkeletonGame and by the trough mode, which is automatically added to every game.

If you have an auto-plunger, your Mode can eject additional balls into play using the trough’s method:

Without an auto-plunge, use:

When multiple balls are active in play, a new SkeletonGame event is possible: evt_single_ball_play. This event lets your mode code know that all of the extra balls you tried to launch have been launched, but the player has drained all of them but one. Again, this event will not fire if ball launches are still pending, so you don’t need to worry about that. Typically this event marks the end of multiball. You probably want your “multiball mode” know that the player is down to a single ball, so you should define a handler for this event. This is done as:

If you wish to determine how many balls are anticipated to be in play, the trough exposes:

which returns the number of balls that will be eventually “live”, counted as the number of balls currently on the playfield plus the number of pending ejects

And for the number of live balls (or at least balls that are out on the playfield):

More info to be added ASAP

5a. Activating modes based on shots

How do I add modes to play based on other shots and not just ball/game start.
(a/k/a when to use AdvancedMode.Manual).

1) Make sure the mode_type is AdvancedMode.Manual as shown below:

2) Be sure to remember the name of the variable/reference that
holds the mode as defined in

In the example above, this is self.my_mode so the Game instance
has a field called my_mode; we can refer to it in Mode code via:

3) In Mode code, you can refer to the
list of currently active modes (which lives in the game) as:

So, to activate/start your new mode ( MyCustomMode) based on a specific
switch or event in a different mode, in that switch/event handler you would add:

3) To conclude that mode from:
3a) a different mode:

3b) Within MyCustomMode itself:

Using the GUI Tool to make RGB Lamp shows

Preparing to use the GUI Tool (for RGB LampShows, etc)

To start, you need an image of the playfield and the resolution matters because that’s how big the window will be when you use the tool later. At the time of writing resizing the window isn’t supported, which is lame, I know. The playfield image is better if it’s easy to somewhat ignore. I like to wash these images out (make them grayscale) so I can clearly see switches and lamps on top of the PF image. It is also crucial that the image is as “normal” (not distorted and directly overhead) as possible. A scan would be best, but then you lack ramp placement.

Below are three example images that work well:

The first is just lines, taken from JD’s manual page.  Very clear, very easy to see, but a bit hard to relate to the artwork.

The next example is of the Buffy playfield; line drawing super imposed on a washed out version of the playfield art, making it a bit easier to orient the user of the tool:

Last is an example of washed out playfield art under line drawings, complete with extra space on the right hand side with text based labels as placeholders for placing the coin door switches within the tool.

The command line example below supposes the filename is playfield.png.

Run the tool

You run the OSC tool using an command line arguments so it can find your machine yaml (t2.yaml in this case) and the playfield image (playfield.png) as such:
python -y t2.yaml -i playfield.png

Once you’ve run the program this way once, in the GUI’s menus, save a layout file, so you can keep your layout file (which includes lamp and switch placement).

Laying out switches and lamps:

In the GUI menus select “Layout Mode” which pops up two windows. One contains a button for each switch the other a button for each lamp. To place a lamp (or switch), left-click the lamp button, then click the playfield to place the lamp. The button stays an alternate color so you know which ones you’ve already placed.

(Notice in the screenshot above, the playfield image has not been washed out and as a result the interface is very hard to read).

For any switch or lamp you don’t want to place (and don’t want to see), the GUI provides an option to hide specific switches/lamps in the menus.

You’ll want to align your lamps as accurately to the inserts (or GI) locations as possible. When you make an RGB show, the position of the lamps (as you’ve placed them) will be used to determine the colors. Proper alignment will be key.

Creating an RGB light show

RGB Lamp show animation works by importing a bunch of images into the tool. Each image represents one time step in the RGB show. The tool then overlays each image over the playfield, and detects the pixel color that overlaps each lamp. Ideally, the images are the same size as the playfield image, so it’s easy to color the lamps.

The RGB lamp show tool is in the menu under Lampshows->Create an RGB Show (experimental) and the resulting pop-up is a prompt to open a sequence of images. Once you’ve opened the sequence you can rearrange the individual frames, add/remove frames, and save the sequence.

See the examples below to understand the contents of a generated file.

Recommended “manual” workflow:

Take a screenshot of the tool, with the lamp locations visible. Save the screenshot as an image, and use it as a base-layer in a paint program. Create more layers and “paint on top of” the playfield image. Save the layers as individual images. Each layer/image/frame is one moment in time/step in the animation.

Better workflow:

Even better is to use an animation tool. I use synfig which is a good, free 2D animation suite. Take your playfield screenshot as the background and animate different colored shapes moving over the playfield. Export the individual frames of the animation (as pngs). Bring the frames into the RGB maker so you can generate an RgbShow.


The RgbShow file format is supported as a valid lampshow format in attract.yaml, provided they are defined in the asset_list.yaml under an RGBShows: section, similar to the LampShows section, as so:

They can be manually played by the rgbshow_player object that exists in the game. For example:

And technically they can be loaded by that object as well (e.g., if you prefer not to use the asset manager) as such:

What’s in a .RGBShow file?

The RgbShow file format contain a dictionary at the top that defines mappings of human readable ASCII characters to specific hexadecimal color values (as hex: rrggbb). The body of the lampshow contains the names of the lamps and a sequence of colors — each column entry is that lamp’s color per time unit (the color is the defined in the dictionary above).

An example is provided below to show how the files are internally structured. Notice that lines that start with # are comments. Lines that start with ! come in two forms: the first are directives and can be overridden when played via play_show(), the second are the mappings of characters to colors. Note that the specific mapping syntax used => vs. ~> adjusts the color cycling behavior as indicated in the comments of the example. The mappings map from a single ASCII character to the specific 24bit color for the lamp in hex.

The body of the show ignores leading whitespace and defines what a named lamp/led should be doing over the course of the show playback.

  • Caveats as of 2/5/2018 on the dev branch*

Gradients are okay but if you use more than 64 unique colors, this version of the tool gets unhappy –I may patch you a full color version in the future, but the RgbShow output is pretty gross/unreadable if we store full color entries for each step in the show.

The SkeletonGame Event System

A major goal of SkeletonGame is to keep you from having to wrestle with implementing the logic that controls the typical pinball game state stuff (e.g., is a ball starting? stopping? drained? tilting?). The idea is that the majority of your code can live in “modes” and your modes will be notified about these game-play events firing. To be notified of these events, your mode should provide a method with the same name as the event, and your mode needs to be active (unless otherwise noted in the table). Your mode will be notified of the event by the framework calling your method. Modes with higher priority will receive the event first. Returning a number (of seconds) will slow the event propagation so that your mode can do whatever it needs to without interruption from a lower event. If an argument is specified, your method can receive that argument and use it to gain additional information about the specific event.

The following table summarizes the current events supported in SkeletonGame (dev branch) as of 3/18/2017:

event name fired when… return value delays…
evt_balls_missing the trough reports insufficient balls, blocking the start of a game no next function, so return value is irrelevant
evt_balls_found a previous search for balls (that had blocked game start) has been resolved nothing to delay
evt_game_starting the user has pressed start from attract mode return value delays the actual start of the game
evt_player_added$ called every time a player is added to the game. Will be called at least once (after evt_game_starting); argument is the newly created player no delay
evt_ball_starting called when a ball is about to start for a player (prior to the ball being placed in the shooter-lane, flipers being enabled, etc) return value delays the actual start of the ball
evt_ball_saved The moment a ball has been saved by the ball saver no delay
evt_shoot_again$ a ball has drained but an extra ball is available; NOTE: evt_ball_starting WILL fire after this as a ball will be starting after this delays the start of the ball
evt_ball_ending the final “in-play” ball has drained; args=(shoot_again,last_ball) to indicate if the player has any extra balls, and/or if this is the player’s last ball. the end ball process, which will initiate a bonus tally and eventually start the next ball
evt_tilt_ball_ending ALTERNATIVE to evt_ball_ending when the game has been tilted. evt_ball_ending will NOT fire. This event indicates all balls are back. Good place to admonish the player (note that evt_tilt will have fired before this, if not also evt_tilt_warning) either end of this ball or slam_tilt complete (game reset)
evt_single_ball_play called when the number of balls in play decreases to 1. Indicates the game is no longer in a multiball mode No return value
evt_tilt_warning tilt switch has been tripped; arg is number of times the player has been warned nothing
evt_game_ending the game has ended. All balls are done. Will also fire following a slam-tilt the game ending sequence follows; fire high score entry, then game ended
evt_initial_entry if the player has been awarded a high score of some sort; this event will fire before each individual high score entry prompt. Arg is the category record of the specific award. This event may potentially multiple times after each entry has been entered. blocks the actual entry of initials
evt_game_ended after high score entry is complete (will fire even if high score entry does not occur) this delays the call to reset() that puts the machine in a clean state before the start of the next game
evt_tilt if the player has tilted the machine. the arg is a boolean indicating if the machine was slam tilted (True) or not (False) n/a
evt_volume_up the volume has been changed. Use this event to show something custom to the player. The arg is the system volume as an integer (0..10). n/a
evt_volume_down the volume has been changed. Use this event to show something custom to the player. The arg is the system volume as an integer (0..10). n/a

$ –> means even inactive modes will receive this event.

Step 5: Making a Mode

Making a new mode:

A mode defines the specific reaction your game should have to various events that occur. If you want the player to receive 1000 points every time a specific shot is made, you will define that code within a mode.

The Mode is the basic building block of coding your own game; Modes can be as simple as defining a response to a single switch in your machine or they can handle every switch in your machine as the complexities of the sequences in which those switches might be hit. Modes might show something specific on the display or play background music while the mode is active, or they might be “invisible” as far as the player is concerned.

Modes in PyProcGame may:

  • Respond to switch events
  • Register/respond to timers
  • Enable/disable/schedule lamps
  • Enable/disable/schedule coils
  • Play sounds
  • Show animation/text on the display

When a mode is active it means it is present in the game’s ModeQueue ( The ModeQueue is a priority-sorted list of the Modes in the game. Inactive modes do not receive notification of switch (or other event) notifications.

In SkeletonGame, each Mode is defined by the game programmer as a subclass of “AdvancedMode” –let’s explore the creation of a mode that will serve as a skillshot.

Defining a new Mode

To create a new SkillshotMode class, we create a file called my_modes/ with the following contents:

This file doesn’t define much functionality for the mode, but provides several key portions of the definition. At line 1 above, we see the name of the new class (SkillshotMode) and its parent class (AdvancedMode –from which our new class inheriets a lot of functionality).

Line 3 above defines the __init__() function –the constructor method that is called whenever this class is created. This function must be included in your class definition, and line 4 includes the call to super() which gives the AdvancedMode class information about how your class should be treated.

The priority is the numeric priority of this mode relative to others. Being higher priority means your mode will receive switch events before other events do. The type indicates when the mode will be activated.

mode_type = which means…
AdvancedMode.Ball The mode is activated when a ball starts and deactivated when the ball ends. In a typical 3-ball game, that means your mode is activated/started and deactivated/stopped 3-times per player.
AdvancedMode.Game The mode is activated when a player starts a game and is deactivated when the (last) player’s game ends. In a typical 3-ball game, that means your mode is activated/started exactly once per game and deactivated/stopped exactly once per game, regardless of the number of players who are added into that game.
AdvancedMode.System The mode is activated when the game code is is initially launched and deactivated when the game code is quit/shutting down. The mode will remain active across multiple games, across multiple players.
AdvancedMode.Manual The mode is not auto-added or auto-removed. The programmer will take responsibility for activating the mode and deactivating the mode whenever it is appropriate to do so. NOTE: If a mode doesn’t specify a mode_type in the call to super(), this is the default/assumed type.

Because the mode is created with a type of Ball, the mode will be added/activated when each new balls starts. We will manually deactivate the mode early, once the skillshot phase of the ball is over.

Adding the mode to the Game class

We need to modify the file to import the new mode. We will modify the modes import line as:

and futher on in the __init__ method of the T2Game we need to add an initializer for the SkillshotMode (right after the other mode initializers). This line will be added:

If we were to test the game code, start a game, and watch the console output, we would see the mode queue will contain our new SkillshotMode. Unfortunately, the mode’s lack of interactivity with the player would make it otherwise impossible to verify its existence.

Enhancing the SkillshotMode functionality

Modes receive events as long as they are active. Specific function names within the Mode definition will automatically be called when these events occur. The following methods can be provided:

Switch events

A mode that defines methods matching a speciifc naming convention indicates it wants the framework to call this method to “handle” the switch event (i.e., a switch handler method). When a Mode is created, its methods are scanned for any methods that match the naming pattern: sw_<switch name>_<switch state>(self,sw) where <switch name> is a valid named switch and <switch state> is either active or inactive.

For example, the method:

would be invoked by the framework when the switch corresponding with label gripTrigger is activated (note that the binding of the identifier gripTrigger and the specific machine switch location is defined in the machine.yaml).

Similarly, a method named sw_startButton_inactive() would be called when the startButton switch changes to an inactive state.

An optional state duration can be specified, in the case that you wish to respond to an event only after the switch has been in that state for a certain amount of time. Adding a for_<time period> suffix to the normal switch handler naming convention enables this behavior:

sw_switchName_active_for_500ms(self, sw) | called once switchName is active for 500 milliseconds
sw_switchName_inactive_for_3s() | called once switchName is inactive for 3 seconds
sw_switchName_inactive_for_20ms() | called once switchName is inactive for 20 milliseconds

Mode life-cycle events

There are two standard method signatures which will be automatically invoked when a mode is activated (added to game.modes) and a mode is deactivated (removed from game.modes), respectively.

Because these method names are tied to activation and deactivation, you should combine this information with the mode_type (or your knowledge of when you manually add/remove this mode) to know when these methods will be called. A different approach would be to use SkeletonGame event handlers, as described below.

Additionally, PyProcGame provides a method signature that is called every “tick” for an active mode, called mode_tick(). A tick is a single pass through run_loop() as it completes one cycle of reading events from the P-ROC and processing them (by informing modes and running their responses). A game may have a tick rate anywhere from 300Hz to 7000Hz so the tick method would potentially be called 300 to 7000 times per second; in order to keep tick rates high, any mode that needs a mode_tick handler should have that code be extremely brief in order to keep the run loop running quickly. If the tick rate drops too significantly, the run_loop() will not tickle the watchdog circuit in the P-ROC hardware, and your machine will go dark. Don’t call sleep() in code.

SkeletonGame events

These are regular gameplay events that the SkeletonGame system tracks. Your mode will be informed of the game event occuring so you can run code to respond to the event. The following page details all the built-in events supported by SkeletonGame:

The SkeletonGame Event System

If your code defines the methods described above, the framework will call the method when that event occurs (provided your mode is active when the event occurs). Modes can also request to postpone the event propagation (i.e., the calling of the next event handler) by returning a number of seconds that the mode needs to ‘finish up’ (e.g., play a special animation or sound) or can delay and stop further propegation by returning a tuple of the delay in seconds and the second entry is True


Any method can be automatically called after a specified amount of time passes (i.e., delay) using the pyprocgame provided method For example, suppose there is a mode that should only be active for 10.5 seconds. A mode can be “deactivated” (removed from the game’s mode queue) with the line, so we define a new method to contain that line, and use a delay to call that method later.

Delay also takes two optional arguments, a parameter to be passed to the delayed method (param) and a name (name) which is useful for canceling the delay before it fires. If a delay is not explicitly named, a name will be assigned and will be provided as the return value from the call to delay().

The following is an example of canceling a delay before it fires, by storing the return value from delay(). This example would give the player a bonus for hitting target2 within 4 seconds of hitting target1

Lighting Lights, Flashing Flashers

While the above example illustrates some advanced logic using timers, it’s not very exciting for the player, who receives zero feedback while playing the game. Flashing lights are a huge part of the pinball experience.

Lights are part of the physical machine. From Mode code, the identifier leads to the dictionary of lamps in this machine. The specific lamp to be controlled is an identifier defined in your machine yaml file. A lamp can be enable()‘d (turned on), disable()‘d (turned off), or schedule()‘d to flash with a specific pattern. Examples of all three are given below, assuming the lamps named target1, target2, and startButton are defined in the PRLamps: section of the machine yaml, and that someButton is a switch defined in the PRSwitches: section of the machine yaml:

Displaying content on the HD Display

SkeletonGame provides additional display functionality on top of PyProcGame’s existing Layer metaphor.

A mode with something to show will set its self.layer to a specific instance of a Layer object (Note: Layer is actually the base-class, there are lots of specific sub-classes of Layer that provide additional functionality. The many Layer subclasses represent the various display types for content that might be shown on the DMD. Choose the right specific type of Layer based on what you want to display!)

Layers are like the Layer metaphor in Photoshop, etc. Layers are optional – if self.layer is not set, nothing is displayed by this Mode. The Z-Ordering of the Layers is dictated by the priority of the Modes highest toward the top of the display stack, lower priority mode’s appear below higher priority modes.

Because the Layer tools can be a little daunting at first, SkeletonGame provides a very powerful display helper method called displayText(), which will get text on the display without the programmer needing to worry about layers.

The complete usage of displayText will be documented in another entry, however the basic form is:

which would display “Message” in the default font on the HD display for the player to see. To provide a multiline message, use the python list syntax ([,]) and each list entry will appear on its own line.

To display an animation (or single frame, etc) on the display, provide a second argument to displayText, which matches a valid key in the animations section of the asset_list.yaml file. This example would display “Free” “Points!” on separate lines superimposed on a sequence of animated fire:

Step 1: (Alternate) Manual Installation for OSX/Linux

There is no one-click installer for OSX at this time. These steps have been tested to work on OSX 10.10; if you’re on Linux, I’m sure you can figure out how to adjust these steps accordingly.

  1. Head into System Preferences->Security & Privacy->Unlock and select: ‘Allow apps from: Anywhere’; When you’re done with this process you can set that back to its previous setting.
    Similarly, you need to disable “System Integrity Protection” in order to get past the unsafe use of relative path libftdi1.2.dynlib in /Library/Python/2.7/site-packages/ with restricted binary –Instructions to disable System Integrity Protection (which isn’t necessarily a great thing to do: apple SIP instructions except you are doing to use the disable argument instead of  enable.
  2. Install XCode from the Mac App Store (note: you need to launch it once after installing it, in order to complete the installation–it can then be closed)
  3. Download libusb-1.0.20
    Create a new folder in your user folder called proc and unzip this into that new folder.
    FYI: ~ is short for the current user’s home folder, we we call this target folder “~/proc” from here on out.

Open a terminal window and change to the ~/proc/libusb-1.0.20 folder and run:

(Note: on Linux, the preceding requires libudev, which is available for ubuntu in the apt packages libudev1 and libudev-dev)

  1. Download libusb-compat
    Unzip to /proc and change to that folder (~/proc/libusb-compat-0.1.5/)

  1. download cmake universal dmg
    install the cmake package by double clicking the pkg file
    Choose the default /usr/bin location and choose to install the command line tools (On ubuntu 16.04 sudo apt-get install cmake appears to install a usable cmake for our purposes)On OSX, newer installs of CMake include instructions to install the command line tools. This is done from terminal as:
  1. Download libftdi1-1.1

unzip into ~/proc folder
change to the newly created libftdi folder with cd

  1. download yaml-cpp 0.2.6
    unzip and copy to ~/proc
    change to that newly created yaml-cpp folder with cd

  1. download libpinproc via git clone, as described below (linux users can use sudo apt-get install git; OSX users can navigate to the github url and download the ZIP.

(take note that the cmake line ends with ..)

Change the include lines in the file proc/libpinproc/CMakeLists.txt to the following:

include_directories(${PINPROC_SOURCE_DIR}/include ${EXTRA_INC} /usr/local/include ${FTDI_INC} /usr/local/include/libftdi1 /usr/local/include/libusb-1.0)

Add the following line with the others below it:
set(lib_ftdi_usb usb-1.0 ftdi1)

I also comment out the build of pinproctest. It’s finicky to build and not strictly necessary. This changes lines in that file to reflect the following:

# examples/pinproctest/pinproctest.cpp
# examples/pinproctest/drivers.cpp
# examples/pinproctest/dmd.cpp
# examples/pinproctest/switches.cpp
# examples/pinproctest/alphanumeric.cpp

Save the file

  1. download pypinproc (dev branch) via git clone, as described below:

(Linux note: Ubuntu 16.04 has Python 2.7 installed by default; I needed to apt-get install python-dev in order for the following to work)

  1. download pyYAML 3.12
    unzip and copy that folder to ~/proc
    change to that new pyYaml folder (cd ~/proc/PyYAML-3.12)

  1. Download pygame. Pick the “Lion apple supplied python”
    Install the package(Linux: sudo apt-get install python-pygame)
  2. Download & install xquartz for mac (Linux users:skip)
  3. Install Python Pillow or Python Imaging Library (PIL):
13. For OSX (Pillow):

13. For Linux (PIL 1.1.7 or Pillow, instructions are for PIP):

Download PIL 1.1.7:(PIL) 1.1.7 source kit
unzip and copy to ~/proc
take a note of the folder name so you can cd to it

  1. PyProcGameHD-SkeletonGame (dev branch, for the latest)

This will install PyProcGame and SkeletonGame in ‘develop’ mode, such that when you import pyprocgame from any python script, it will use these files, from the current location (~/proc/skeletongame).

Install the PyProcGameHD SDL2 dependencies.

  1. Download and install SDL + PySDL2 0.9.5 for your platform:
    • PySDL2 0.9.5
      cd ~proc/PySDL2-0.9.5/
      sudo python install
    • PyOSC PyOSC 0.3.5b
    • Install the appropriate versions of the SDL2 and the SDL2_TTF libraries:
    • sdl2
    • sdl2_ttf
    • for many linux distros, SDL2 is installed via: sudo apt-get install libsdl2-2.0-0 libsdl2-image-2.0-0 libsdl2-ttf-2.0-0
    • Either set the PYSDL2_DLL_PATH (as per the instructions, here or add a PYSDL2_DLL_PATH in the game’s config.yaml.
    • Optional: For MP4 support install OpenCV and libffmpeg

to test, in python run:

The first three lines should return the prompt with no feedback. The last should print 6. This is how you know it worked.

This also updates the “procgame” command line tool, and the updated version works with the color file formats. From the command line running


Will show a list of available arguments/commands. For example, procgame dmdimage <file.dmd> <file.png> will convert a standard .dmd file into a single or series of png files.

Last but not least, if you want to use the GUI switch tester tool, you can get it from GitHub, here: OscSwitchMatrixGUI, which will require wxPython to be installed. –note, if you are in my class, I have posted a zip which includes the playfield image and the tool to Canvas, but you need to install wxPython before you can run it.

Many thanks to Brian Madden for his original work to record the PyProcGame install steps on OSX from his guide, here.

Step 1: Installation and Testing the Install (Windows)

Step 1: Install the environment.

Installing the PyProcGameHD/SkeletonGame environment requires installing a number of packages. The easiest way to get up and running is to use the Windows one-click installer. You can download the Windows All-in-One Installer here (Current as of 11/2020).

This will install P-ROC/P3-ROC Drivers, Python 2.7, SDL2, PySDL2, PyProcGame, PyProcGameHD, SkeletonGame, and so on.

I’m working on one-click installers for other platforms but they are far from done. Manual installation on OSX is described here.

Note: If you have previously run the installer, as of 6/6/2017, the new installer will install Python 2.7.13 and the dependencies required to run on Python 2.7; you should remove c:\Python26\ from your path before running the new installer.

Step 2: Run the sample game.

2a) Unpack the assets

Once the environment is installed you should confirm that everything is working. A sample game is included in:
C:\P-ROC\PyProcGameHD-SkeletonGame-dev\SampleGame –assets for that project are located in a file in that folder called
UNZIP; if you intend to run the sample you should unzip the folder.

2b) Run the sample game

Open a windows command-prompt (Win-R and type CMD.exe), switch to the folder that contains the game, and run it. To do that, after opening the command prompt, type the following:

These two lines will change the current working directory (i.e., folder), and then run the python file named in that folder.

If this works, you should see the progress bar pre-load the assets (images, animations and sounds), and then the game should start showing the “attract mode”

2c) Simulate play

To simulate game play:
Pressing ‘S’ will simulate pressing the Start Button on the cabinet and should start the game. The game should complain that balls are missing. Hold down the ‘1’ key to simulate closing the leftmost switch in the ball trough while pressing ‘S’ so the game will think all three balls are in the trough when the player is pressing start.
— release both ‘S’ and ‘1’
–press the ‘d’ key briefly to simulate the ball hitting the shooter lane switch
–press keys like ‘7’ ‘8’ ‘9’ to simulate hitting playfield switches
–Hold down the ‘1’ key to simulate closing the leftmost switch in the ball trough –the game will think all three balls are in the trough and end the ball–

If this worked, your environment is set and the simulator is working.

Step 2d) Optional: the graphical switch tester:

The graphical switch tester allows you to use an image of your playfield, layout the buttons and lamps on that image, and then test your machine by interacting with a GUI on top of that image.

If you have installed the GUI tool from the installer, a run_gui_SampleGame.bat file was created in c:\P-ROC\GUITool. Double-click that to run.

The contents of that file are, essentially:

* config/t2.yaml is the path to the machine configuration file
* playfield.jpg is a playfield image shrunk to fit the screen.
* t2.layout is a layout file saved from this tool (a first run will not include this -l t2.layout argument)

Step 2: Config and Machine YAML files

Understanding the config.yaml:

A PyProcGameHD/SkeletonGame ‘game’ will look for a file called ‘config.yaml’ when it launches to try to load some environment-wide and game speciifc settings. Your ‘config.yaml’ can exist in the same folder as your game code, if it isn’t found there the system will check for it in a folder called ‘.pyprocgame’ in your user folder. Chances are, if you are running your game from a different folder than the one your game is in, it will rely on that fail-safe location.

It defines the paths to the asset files your game will need (images, lampshows, sounds), SkeletonGame specific settings (which modes to enable or disable), DMD specific settings (resolution, fps), window settings (border, scale, dot-effect), whether to connect to the real P-ROC hardware or not (‘FakePinProc’), and finally a mapping of keyboard keys to switch numbers (for testing when the real P-ROC is not connected).

If you open up your config.yaml file, you will see a very specifically formatted file that defines key/value pairs. The PyProcGame/PyProcGameHD/SkeletonGame code looks for the values of specific keys to set defaults for your game. YAML is a whitespace sensitive format, so the presence or absence of spaces may cause the file to no longer load properly. There’s a good, high-level overview of YAML here:

The most important line to notice is the line that starts:
pinproc_class: –if this line is present and has a value of procgame.fakepinproc.FakePinPROC, then the simulator will be used and it will NOT connect to a real P-ROC/P3-ROC. If you place a # at the start of this line, it will be “commented out” so that the YAML processor ignores it. It is like deleting the line without actually deleting it. When you are ready to connect to a real P-ROC on a real pinball machine, you’ll want to comment out that line.

Understanding the machine yaml file:

Once you have your config.yaml the way you like it –that is, the display resolution has been set, the dot scale, dot effect, folders in which to find sounds and images and things– you need to define a machine configuration yaml file (or machine yaml file).

Your machine yaml is different from your config.yaml. A pinball machine is filled with switches, lamps, and coils, and we need a way to refer to them in the code. Identifying them based on their connection to the machine hardware (e.g., location in the lamp matrix, location in the switch matrix or the sw-16 board address and switch number) works, but it would be really cumbersome to write and to later debug. Who want’s to see things expressed in terms of S13, or C04?

This is the primary problem solved by the machine yaml file: you use it to define what components are attached to your machine and the yaml allows you to provide names (aliases) for each of the devices in your machine.

Below is an excerpt of the machine yaml file from the SampleGame provided with SkeletonGame. The file config\T2.yaml contains the entire configuration for this machine, which is a Williams Terminator 2. Looking at the excerpt you will see listings for coils, lamps, and switches. Each entry in the list (starting with a dash) is the friendly name for the device found at the corresponding number.

In SkeletonGame, it’s recommended that you store your machine config in the config folder.

Later, when you are writing code, you will be able to refer to the machine’s elements by these names. For example:

SkeletonGame adds some additional functionality to the machine yaml when compared to the PyProcGame format, specifically SkeletonGame adds support for the tags on switches/coils related to ball search. In addition, in order to make your life easier, some of the helper modes provided in SkeletonGame require certain switches to have specific names (eg, your ball trough switches must be names trough1 through troughN where N is the number of balls held in your trough, and trough1 is the leftmost switch —troughN is the switch closest to the shooter lane).

  1. config/machine.yaml

This is the machine definition file. It maps all the switches, lamps and coils in your machine to easier to use logical names. Some samples are available, including the T2.yaml example in the Sample Game

You must adhere to a few naming conventions:

  • your trough switches should be named trough1, trough2, .. troughN where N==num trough switches. OR your switches should be tagged as ‘trough’ as shown here tags: trough
  • your start button should be called startButton or tagged start_buton
  • your shooter lane switch should be called shooter
  • your (bob) tilt switch should be called tilt
  • your slam-tilt switch should be called slamTilt
  • ball search is handled differently! switches and coils should be tagged for ball search, as shown in the example.

defining your own machine yaml

If your game is a retheme/continued, based on an existing Williams or Stern boardset, someone may have already created a yaml file for you. You can check in shared/config for some existing yaml files. These files don’t conform to all of the naming requirements of the SkeletonGame naming convention, but starting from these files will likely make your job much easier. Also bear in mind that you might not like the names defined by someone else, and those names (except for those required by SkeletonGame) are totally at your discretion. If it seems like the name should be something else to you, go for it.

If your machine’s yaml doesn’t exist, you’ll need to grab a copy of the operator’s manual and generate entries for each of the lamps, switches, and coils. It can be helpful to use the machine yaml for another machine from the same manufacturer and vintage (and that machine’s manual) as a reference to get your machine’s yaml started.

For completely custom games running on the PinballControllers controller boards (a P3-ROC w/ PDB16, 8×8, etc), you can use the example pdb.yaml to get you started. On a P3-ROC, it is imperative that you set your machine type to pdb! Also, forum user JoeShabadu has written an amazing getting started guide that includes a post getting your machine yaml right for custom P3-ROC machines (Page 1). The whole thread documents his P3-ROC wiring and first steps programming his custom machine is a really good read if you’re starting from scratch on a P3-ROC custom machine. Joe’s P3-ROC wiring/getting started tutorial Page 2, Page3 , Page 4, Page 5, and Page 6).

sanity checking your machine yaml

This is optional, but a nice progress milestone for the impatient who want their game flipping now🙂

It can be a bit rough to try to get your game code up and running as the first thing you do, since you need to deal with your assets, etc. a good strategy is to use this code for a stripped down “first flips” game. It does nothing but load the machine yaml, initialize some things under the covers, and enable the flippers.

If you save the following into a file called ‘‘, copy your machine yaml into the same folder as this file (call it machine.yaml), and ensure that one of your lamps is actually called somelamp, then running this file (literally just double-clicking ‘’ and running it with Python or the better way, running it from the command prompt via python then your game should be up and flipping.

This is NOT a SkeletonGame game, and as such you don’t have any trough support, ball counting, or anything of the sort. It’s just a nice quick way to get your flippers active. If the game won’t run, it’s because your machine yaml is malformed.

If you are having problems with your yaml file, there are several good online yaml file validation sites (like YAMLlint) that can help you find spacing, or other syntax problems. These resources don’t know about the meaning of the specific tags of the machine yaml, so they can’t help you with those things, but for these basic problems (that can be very hard to see), these tools can be indispensable.

Step 3: Adding Assets (images, sound, etc)

Adding sound, video, fonts, etc. to your game

SkeletonGame provides an built-in asset manager that is driven by a simple YAML file format. On game launch, the asset manager will pre-load all the assets defined in config/asset_list.yaml. The asset_list.yaml, associates files (images, animations, sounds, music, fonts, and lampshows) with “friendly” names, and allows for custom settings for each of these items (e.g., change the default volume of a sound, change the x/y position of a sprite). Once these assets have been loaded, they can be used in code or in other locations by referring to their key (alias).

Looking at an example asset_list.yaml (this is the example that comes with SampleGame), we can disect the components expected to be present:

  1. Loader User Interface: The visual layout of the asset loading screen that will be shown while your assets are being pre-loaded (on game launch)
  2. LampShows: a mapping of lamp show file names and logical names (i.e., keys) by which they will be defined. Defining a lampshow in this file causes the lampshow to be parsed and validated during asset loading, so if there is in error in the lampshow you will find out immediately instead of when your code eventually goes to play the lampshow.
  3. Animations: a list of images and animations to be used in your game; minimally their file names and their logical names (i.e., keys). By including an entry the image or animation will be pre-loaded, ready for use in your game’s mode code. Supported formats include the ‘dmd’ format, many common single frame formats: jpg, gif, png (per pixel alpha supported), and some animation formats such as gif (for animated gifs) and single frame file sequences (e.g., a file range from my_image000.png through my_image029.png would be specified with the file name my_image0%3d.png and all 30 would be loaded as the frames of a single animation).
  4. HDFonts: Native system fonts (TTF) and aliases for specific sizes of those fonts
  5. Fonts: Bitmap fonts, based on the PyProcGame font format
  6. Audio, Music, Voice: Audio files named via key (wav or ogg format).

UserInterface — what the loading bar looks like

Settings in this section modify what the user sees when the game is pre-loading assets.

The splash_screen is an image file to be shown underneath the information displayed during pre-load. progress_bar defines the size and position of the progress_bar relative to the overall screen size. In the example above the full-width of the bar is 90% (width: 0.9) of the window, and 10% of the height of the window (height: 0.10). It is centered on the X-axis (0.5) and appears in the top 1/4 of the display. The border color of the bar is a bright magenta color (border: [255, 0, 255] –> means 255 of a possible 255 Red, 0 of a possible 255 Green, and 255 of a possible 255 Blue). The filled portion of the bar as loading progresses is shown as Red.

The text portion defines the color of the text used to indicate which file is being loaded, and the y_center defines the height at which the text will appear.

Those settings together produce the following loading interface for the user; while the text is clearly cut off, thankfully the user only sees this briefly. Note to self: add a font size option!!
preview of loading screen


lampshow files can be pre-loaded on asset load. The key is name by which you will refer to the lampshow files later when you attempt to play them, and the file is the filename, assumed to be located in the path ./assets/lampshows/

The above example loads two lampshow files, each of which has a key that is simply the name of the file without the .lampshow extension. You can find more information about the lampshow format, here

Animations (and Images)

Both single images and animations are defined in a section called Animations: The minimal information for an entry is typically a filename (file:) and an alias (key:). Code can then refer to the asset by the given alias.

file: can be a single .gif, .png, .jpg, or .dmd file, or can refer to a numeric range of files using the %d operator. Three simple examples are given below:

Notice on the third entry (robot) the file there is an additional parameter x_loc which will move the image over 50 ‘dots’ when it is displayed. (y_loc would move the image down, and negative values can be used to move objects in the opposite direction)

Animations as sequences of images:

For example, suppose there are 91 images called explosion1_0000.png through explosion1_0090.png and these images should be treated as consecutive frames of an animation. If all these files are placed in ./assets/dmd/explosion1_64x48/ (just to reduce clutter), specifying the file: 'explosion1_%04d.png' will load a file ./assets/dmd/explosion1_64x48/explosion1_0000.png as the first frame, ./assets/dmd/explosion1_64x48/explosion1_0001.png and so on until the first consecutive file number is not found. You can’t skip numbers in the middle, and you cannot skip the first (0) filename. The %04d means 4 digits, padded with zeros to the left (so 0000, 0001, …). %2d means numbers up to two digits, but without the zero padding, so 1, 2, 3, ..9, 10.

You can also use .mp4 files that contain animations. More documentation about their use is forthcoming.

frame_time: indicates how many times the frame will be shown before it advances to the next frame (note the default system framerate is 30 frames per second, and this can be changed in your config.yaml file). By default, the frame_time is 1.

repeatAnim: indicates whether or not the animation should loop, playing over and over again. If omitted, the default is False. (Notice that True and False are indicated with a leading capital letters, as in Python itself).

compositeOp: 'blacksrc' can be indicated to cause the framework to treat black (0,0,0) pixels as transparent. There is also a 'magentasrc' (255,0,255) and 'greensrc' (0,255,0). For PNG files, per-pixel alpha transparency and whole image alpha transparency is supported, so saving images as PNG files with per-pixel alpha (RGBA/32bpp) eliminates the need for using compositeOp.


The Audio: category allows the definition of music and sounds. Use .wav or .ogg (Ogg Vorbis) file formats. MP3 is not recommended at this time.

The following example should be straight forward.

  • Music files are expected to be found in ./assets/sound/music
  • Effects (sound effects) are expected in ./assets/sound/sfx
  • Voice (call-outs) are expected in ./assets/sound/voice


True Type and Open Type Fonts (ttf and otf) can be used in the framework. The list of the entries of “HD” fonts should occur under the Font category

The key defines an alias for the specific type face and the size of the font when rendered. The font may be specified as a filename or a system name, however using systemName will require a user to install a font onto his/her machine before use, which is far more intrusive. If using filename, the path is assumed to be ./assets/fonts/

Font “Styles”

Font styles may be defined within the font category as well, and should appear at the same depth as the HDFonts category

In the example above there are two styles defined: weird will fill the text rendered in a medium blue ([0,0,128] as R,G,B), has a line_width (aka border) of 1 and the line_color is a reddish purple ([255,0,128]).

SkeletonGame Requirements:

SkeletonGame requires a few conventions in your asset_list, that allow you to tweak/customize certain properties. You must be sure you define a few things:

  • A Font or HDFont named: tilt-font-big, tilt-font-small, settings-font-small, high_score_entry_inits, high_score_entry_msg high_score_entry_letters
  • TODO: Flesh out the complete list, for here!
  • something else that’s vital…

Step 4: Making a Game Class

You will need to make a Game class. Yours can look a lot like the one that comes with SampleGame called

In this entry we will dissect the example given game class,, in order to better understand what’s required in a SkeletonGame’s main class. Before we get too deep, it’s worth noting that a familiarity with Object Oriented concepts is very valuable here, as is some experience with Python. You can pick up everything you need from a few nights going through the Python tracks on Code Academy.

If you investigate the SampleGame folder you will see two files: config.yaml (discussed in a previous entry) and –the latter defines the “main” class of the Sample Game, and is the place to start in understanding how this game works and what is expected in your own game. Some good additional Python resources are this wikibook, and this overview of object orientation in Python.


The first few lines are imports; they include code written by others for use into your program. Some are included with python (e.g., logging) and others are part of the PyProcGame/PyProcGameHD/SkeletonGame framework (e.g., anything starting with procgame)

Nothing is wildly special in the above. In the next block, you’ll find the imports of python modules that define “Modes” –Modes are important, but let’s table that until we get through this file.

The above imports do a few things: the first imports my_modes which is a folder under SampleGame, and ultimately a package in Python lingo. In Python, the execution of this line causes Python to look a definition of a module of that name, and that search starts from the current folder, looking for a file of that name, or a package of that name. What python finds is a folder, so within that folder, Python is looking for a file called which will be executed when that package is imported. That file can include seperate import lines to define “All” the files in that sub-folder, but importing all the files in that folder is not automatic.

The next line imports two specific classes from my_modes –those examples will be the ones we dissect in the next entry.

The next two lines give examples of how to import a new Mode that you create if you do not wish to modify the; the general for there is:

from <folder>.<file> import <class_name>

Reading the Python docs about modules and packages should shed more light here, if you are so inclined.


The next line sets up the logging level and format.

Logging level INFO produces a considerable amount of output to the console, but actually not as verbose as is possible. The levels are listed here, and a good tutorial on using Logging (which is worlds better than just using print is availble here).

The curr_file_path line simply lets the SkeletonGame code know where in the system the current file is located for relative path loading.

The Game Class

Now we get to the meat of things. The Game class is the main game object. It “owns” all the attributes of the physical machine (.coils, .lamps, .switches) and can communicate with these components via the P-ROC interface. In addition the Game class owns the state of the active game, including (among other things) the “Player” (.current_player()), the list of players (.players), the ball number (.ball), and the Mode Queue (.modes). The Game class also is the home of the display controller (which we typically don’t access directly), and the sound controller (.sound),

Some understanding of Object Orientation goes a long way. Our game is a sub-class of SkeletonGame, which means it includes all the fields (i.e., variables) and methods (i.e., functions) of the SkeletonGame class. SkeletonGame is a subclass of BasicGame, which is in turn a sub-class of GameController.

In the code above, the __init__(self): method is defined, which is the T2Game class constructor –this is the function that gets called when a new instance of the T2Game class is created. That means, typically, this function is run once, when your game is executed. If you “peek ahead” to the bottom of the file, you will see where your game is constructed.

The rest of the constructor defines the switches that will start out as “automatically closed”, and invokes the __init__() method of the super-class (that is, calls the __init__ method defined in SkeletonGame); that code sets up a lot of stuff for us, and it’s nice that you don’t have to write that code. 🙂

The next few lines instantiate the Modes that manage the behavior of the game. When you add a new mode, you’ll need to add a line like the following:

If you’d like to add any global helpers, notice that the leftTargetLamps is defined just to be helpful, creating an array of the lamps corresponding to the left five targets, accessible through the game instance.

The last thing the game __init__() needs to do is call .reset(); the definition of reset immediately follows.

The .do_ball_search() method, shown below, is called by the framework when a ball search is required. Your methods should actually pulse coils in order to release stuck balls. This example is far from ideal since it doesn’t have any delay

Last, and certainly not least, at the bottom of the file:

That is what is actually run when you type python, and it passes the class into the run_proc_game() function (defined in SkeletonGame) to create an instance of your game and run it.