Skip to content

Tutorial 14: Add your first game mode

This is part of our Getting Started guide.

The guide starts here.

It's been awhile since this tutorial has been updated. If you find anything that is no longer relevant, please let us know, or better yet, edit or update it yourself!

By this point in the tutorial you should have a "playable" game, though it's pretty boring because there's no scoring, no modes, and the display just shows PLAYER X BALL X the whole time.

So in this step the real fun will begin as we configure our first game mode! So far all of the configuration you've been doing has been machine-wide configuration which was stored in the /config folder in your game's machine folder.

All of the configuration options you added to the config.yaml applied machine-wide.

In this step, we're going to add a /modes folder to your machine folder. Then we'll add a subfolder for each game mode, and in each we'll create a YAML config file that controls what happens in that specific mode.

What's cool about MPF's modes system is that all of the configuration you do for a mode is only active when that mode is active. In fact from here on out, almost everything you configure will be at the mode-level rather than the machine-wide level. As we go deeper into the tutorial and the How To guides, you'll start to get a feel for what types of things should be in the machine-wide configuration versus the types of things that should be in mode-specific configurations. Pretty much all the hardware (coils, switches, lights, leds, ball devices, platform, DMD, etc.) are configured as machine-wide settings, and then game logic-type things (scoring, shots, sound effects, animations, light shows, etc.) are configured as mode-specific settings.

MPF can have as many modes running at once as you want. In fact you'll probably use this to your advantage, breaking up your game into lots of little modes to make the programming easier. (Many of these modes will not be "in your face" modes that the player is aware of. Things like skill shot, combo timers, super jet counters, etc., will all be configured as modes even though the player wouldn't think of them as modes.)

1. Read the documentation about modes

The first step to setting up a game mode is to understand how game modes work in MPF. So read that documentation now to get an overview, and then come back here for the step-by-step walk-through of doing your first mode.

2. Set up the folders & files for your "base" mode

The first mode we're going to create is a mode called "base." (Don't call it "game" because MPF has a built in mode called "game" that you don't want to overwrite.) This "base" mode will be running at all times while a game is in play and can be thought of as the "default" game mode. We'll set up default shots, scoring, configure the display to show the score, etc. Everything in the base game mode will be available if no other higher-priority modes are running. To create the base game mode:

  1. Create a folder called modes in your machine's folder.
  2. Create a subfolder for your modes folder called base. (You will ultimately create one subfolder for each mode you have, and the name of the folder controls the name of the mode.)
  3. Inside your base folder, create a folder called config. (This folder will hold your mode-specific config files.)
  4. Inside your config folder, create a file called base.yaml. (This is the default config file for your base mode. We use the naming scheme <mode_name>.yaml instead of config.yaml for these to make it easier to keep track of which files are which if you open a bunch of them at once in your editor.)

At this point your machine's folder & file structure should look like this:

image

3. Add your base game mode's settings to its config file

The settings that control a mode are configured in the mode's own configuration file. We do this because it allows modes to be completely self-contained. In other words, as long as you have a mode's folder and all its content, then you have everything you need for that mode.

So let's configure some settings for the base mode in the base mode's config file. To do this, open your new mode's base.yaml in your code editor. Add the config_version, then create a top-level configuration section called mode:. On the next line, indent four spaces and add the entry start_events: ball_starting. On the following line, also indent four spaces and type priority: 100. Your base.yaml file should now look like this:

##! mode: my_mode
#config_version=5
mode:
  start_events: ball_started
  priority: 100

There are lots more settings besides start_events and priority which you can set for a mode. See mode: for details.

The two settings we added here should be pretty obvious. The start_events: ball_starting means that this mode will automatically start when the MPF event ball_starting is posted. (In other words, this mode will start whenever a ball starts.) You can also enter a list of stop_events to control how the mode ends, though if you don't enter one here then the mode will automatically stop when the ball ends, so you don't have to specify a stop event now.

The priority: 100 means that everything this mode does will have a base priority of 100. We'll create future modes at higher priorities so they can take over the display, control lights, filter and block scoring, etc. (You read the documentation about modes, right?)

Also, when you create your own modes, keep them between 100 and 1,000,000. MPF has some built-in modes above and below those values that should stay at the top and bottom of the priority stack.

4. Add your mode to your machine-wide config file

Now that you have a mode set up, you need to go back to your machine-wide configuration file to add this new mode to the list of modes that your game will use. At first you might think this is a bit confusing. After all, you just created a folder and a config file for your new mode, so why do you have to specify that mode in another location too?

The reason is we don't want to automatically include a mode in a game just because that mode has a folder in the modes folder. (After all, what if you're testing something out, or if you have multiple versions of a mode you're playing with? It would be dangerous if MPF just automatically loaded every mode it found.)

So instead we built MPF so that you have to add all the modes you want to be available in a game to a list in the machine-wide config file. To do this, go back to your machine-wide config.yaml file (in <your_machine>/config/config.yaml) and add a top-level section called modes:. (Like all the sections in your config file, you can put this section anywhere you want in your file. Maybe up towards the top so it's easy to find later?) Then on the next line, type two spaces, then a dash, then another space, then type base. So now that section of your config.yaml should look like this:

modes:
  - base

Note that it's very important that you put dashes in front of each mode in this list? Why? Because with dashes, MPF will be able to combine settings together in this list from different config files.

For modes that's important, because MPF has several built-in modes it uses for its own things. (For example, "attract" and "game" are both modes, and we'll be creating future ones that you might want to use too for tilt, volume control, game statistics, high score entry, credits, etc.)

5. Run your game to verify your new mode works

Be sure to save the changes to base.yaml and config.yaml, and then run your game again. For this test, you do not need to use verbose logging since mode information is reported in the basic level of logging. Once MPF is running, start a game and you should see something like on the console and/or the log file when you run mpf both -t:

INFO : Mode.attract : Mode Starting. Priority: 10
INFO : SwitchController : <<<<< switch: s_start, State:1 >>>>>
INFO : SwitchController : <<<<< switch: s_start, State:0 >>>>>
INFO : Mode.game : Mode Starting. Priority: 20
INFO : Mode.game : Player added successfully. Total players: 1
INFO : Mode.base : Mode Starting. Priority: 100
INFO : SwitchController : <<<<< switch: s_trough_1, State:0 >>>>>
INFO : SwitchController : <<<<< switch: s_shooter_lane, State:1 >>>>>
INFO : SwitchController : <<<<< switch: s_shooter_lane, State:0 >>>>>

6. Make your base mode do something useful

We already mentioned that there are lots of different things you could add to your base mode. For now, let's configure the display so that it shows the player's score, as well as which player is up and what ball it is, like this:

image

To do this, go back to your base mode's config file (<your_machine>/modes/base/config/base.yaml) and add a section called slide_player:. Then add the following subsections so your complete base.yaml looks like this:

##! mode: base
#config_version=5
mode:
  start_events: ball_starting
  priority: 100

slide_player:
  mode_base_started:
    widgets:
      - type: text
        text: (score)
        number_grouping: true
        min_digits: 2
        font_size: 100
      - type: text
        text: PLAYER (number)
        y: 10
        x: 10
        font_size: 50
        anchor_x: left
        anchor_y: bottom
      - type: text
        text: BALL (ball)
        y: 10
        x: right-10
        anchor_x: right
        anchor_y: bottom
        font_size: 50
##! test
#! start_game
#! assert_mode_running base
#! assert_text_on_top_slide "00"
#! assert_text_on_top_slide "PLAYER 1"
#! assert_text_on_top_slide "BALL 1"

We briefly touched on the slide_player: functionality earlier in this tutorial and how you can configure it to show certain slides when various MPF events happen.

Every time a mode starts in MPF, an event called mode_(name)_started is posted. So in this case, we set our slide player entry to play when it sees the event mode_base_started which means it will play that slide as soon as the base mode starts. (And since you configured your base mode to start based on the ball_starting event, this means this slide will be created and shown whenever a new ball is started.)

You may be wondering why we don't set that slide to play on the ball_starting event? The key to remember with game modes is that all the settings in your mode-specific config file are only active when the mode itself is active. In the case of our base mode, the ball_starting event is what actually causes the mode to start. When ball_starting is posted, the base mode starts and loads its configuration. At that point that ball_starting event has already happened, so if you set a slide to play within that mode then it will never play because it doesn't start watching for that event until after it happened. (Hopefully that makes sense?)

Anyway, if you look at the slide_player: settings, you'll see that the slide that is shown when the event mode_base_started is posted contains three text widgets. One that shows the score, one that shows the player and one that shows the current ball number. Note that the text: entries for those have have some words in parentheses.

Words in parenthesis signs are variables that are replaced in real time when they're updated. In this case these are "player variables" because they are values that belong to the current player. More on using dynamic text (that is, text that automatically updated itself as underlying values change), is here.

Also note that there are some additional positioning settings, like x:, y:, anchor_x:, and anchor_y:. You can read about these in our How to position widgets on slides guide.

Finally, note that the text widget showing the score has settings for number_grouping: and min_digits:. You can read about what those do in the documentation for the text display widget.

7. Remove the old slide_player: ball_started entry

Now that you have this cool score display from your new base mode, you can go into your machine-wide config.yaml and remove the slide_player: entry for ball_started:. So now the slide_player: in your machine-wide config.yaml should just look like this:

#! slides:
#!   welcome_slide:
#!     widgets:
#!       - type: text
#!         text: PINBALL!
#!         font_size: 50
#!         color: red
#!       - type: rectangle
#!         width: 240
#!         height: 60
#!   attract_started:
#!     widgets:
#!       - text: ATTRACT MODE
#!         type: text
slide_player:
  init_done: welcome_slide
  mode_attract_started: attract_started
##! test
#! advance_time_and_run .1

What if it didn't work?

  • Make sure you actually start a game. Remember that this new base mode is only active when a ball starts from a game that's in progress, so you won't see the mode until a game starts. (If you're not able to start a game, check the troubleshooting tips in the previous step.)
  • If you get some kind of crash or error, specifically any errors that mention anything about "config" or "path," double-check that you put all the files in the proper locations back in Step 2. (A common mistake is to put base.yaml in the /modes/base folder rather than the /modes/base/config folder.)

Check out the complete config.yaml file so far

If you want to see a complete config.yaml file up to this point, it's in the mpf-examples/tutorial/step_14 folder.

You can run this file directly by switching to that folder and then running the following command:

C:\mpf-examples\tutorial_step_14>mpf both

Something missing or wrong? You can fix it!

This website is edited by people like you! Is something wrong or missing? Is something out of date, or can you explain it better?

Please help us! You can fix it yourself and be an official "open source" contributor!

It's easy! See our Beginner's guide to editing the docs.

Page navigation via the keyboard: < >

You can navigate this site via the keyboard. There are two modes:

General navigation, when search is not focused:

  • F , S , / : open search dialog
  • P , , : go to previous page
  • N , . : go to next page

While using the search function:

  • Down , Up : select next / previous result
  • Esc , Tab : close search
  • Enter : go to highlighted page in the results