Tag Archives: getting started

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 (self.game.modes). 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/SkillshotMode.py 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 T2Game.py 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:

Examples:
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

Timers

Any method can be automatically called after a specified amount of time passes (i.e., delay) using the pyprocgame provided method procgame.game.Mode.delay(). 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 self.game.modes.remove(self), 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 self.game.lamps 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/pinproc.so 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 2.8.12.2 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:

#add_executable(pinproctest
# examples/pinproctest/pinproctest.cpp
# examples/pinproctest/drivers.cpp
# examples/pinproctest/dmd.cpp
# examples/pinproctest/switches.cpp
# examples/pinproctest/alphanumeric.cpp
#)
target_link_libraries(#pinproctest

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” pygame-1.9.2pre-py2.7-macosx10.7.mpkg.zip
    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 setup.py 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
      linux:https://github.com/BVLC/caffe/wiki/OpenCV-3.1-Installation-Guide-on-Ubuntu-16.04

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

procgame

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.

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-master\SampleGame –assets for that project are located in a file in that folder called
UNZIP ME.zip; 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 T2Game.py 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.
–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:

where:
* 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: http://docs.ansible.com/ansible/YAMLSyntax.html

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: self.game.lamps.mult2x.enable()

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 (PDB16, 8×8, etc), you can use the example pdb.yaml to get you started. Also, forum user JoeShabadu has written an amazing getting started guide that includes a post getting your machine yaml right for custom machines. Honestly, the whole thread is a good read if you’re starting from scratch on a custom machine.

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 ‘first_flips.py‘, 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 ‘first_flips.py’ and running it with Python or the better way, running it from the command prompt via python first_flips.py 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

Lampshows

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.

Sounds

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

Fonts

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 T2Game.py

In this entry we will dissect the example given game class, T2Game.py, 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 T2Game.py –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.

Imports

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 __init__.py 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 __init__.py; 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.

Logging

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 T2Game.py, and it passes the class into the run_proc_game() function (defined in SkeletonGame) to create an instance of your game and run it.