Multi-Game PC Setup with Dynamic Instructions Display


Note

This post is a work in progress, and is being published now to give an idea of the complexity of this setup for anyone interested in recreating it for their own purposes. Any part or all of this page is subject to change. Contact the e-mail address at the bottom of this page to send feedback.

War Gods in play, with instructions on a smaller monitor.

One of my favorite things is showing off unfamiliar video games to people, and one of the most efficient ways to do that is a mystery games tournament. I've played in, observed, and organized a fair number of mystery tournaments over the last decade and gathered a lot of intel on what to do and not to do when running one, and my highest priority is fast and efficient game changing to keep the participants engaged. This entails doing everything possible to minimize the burden on players, including configuring games in advance, providing controllers, and making sure the players know what the controls are so they don't accidentally do something like exit the game.

This post describes the current hardware setup I use to run events. It is a somewhat complex tutorial and makes many assumptions of the reader's level of technical know-how, as many common processes like setting up a Raspberry Pi are omitted or greatly simplified for readability. I created this setup to run mystery games, but I wrote this guide as generally as I could reasonbly make it for anyone interested in building a PC for use in a public location with multiple games that can be changed at any time. Games are played on a dedicated Windows PC—my previous gaming PC that was sitting in storage complete with old graphics card—and a Raspberry Pi 3 configured to run as digital signage. Multiple monitors on one PC, in my experience, is unpredictable in that there's no reliable way to force any given program to launch on a specific screen. Although introducing a separate device increases the complexity, I think it's an acceptable tradeoff to eliminate the risk that programs and the image viewer for instructions start on the wrong display.

NFC is used for launching games, specifically a deck of NFC cards and a common USB NFC reader[a]. I've written small custom Python scripts to handle reading NFC tags and executing associated batch files and cleanup tasks. This setup is completely self-contained and designed to be portable, with no internet access. The PC will communicate to the Raspberry Pi digital signage using a USB-to-TTL device connected to serial UART on the GPIO pins.

Aside

Since the games are kept a mystery from the players until they play, I use unmarked NFC cards and a card reader and have the players choose a card randomly. You are free to present the cards any way you choose. See the Zaparoo documentation for advice on this.

The PC is a kiosk optimized for visual presentation. It has only game controllers and an NFC card reader accessible to the players, with no keyboard or mouse or any other inputs available, and the desktop has no icons or taskbar visible. When a card is inserted into the reader, the PC will launch a batch file. The batch file will, in order, send a request to the Raspberry Pi to change the image it displays to one that corresponds to the game, change the wallpaper on the PC to show a loading message, and then launch the game.

Requirements

All shopping links below are non-affiliate. Please send an e-mail to the address at the bottom of this page if any of them are no longer valid.

  • PC with games installed and pre-configured
  • NFC reader and tags
  • (Recommended) An enclosure for the NFC reader to secure the tags and prevent accidental removal
  • Raspberry Pi (any model, preferably one with integrated HDMI and GPIO header pins)
  • Secondary monitor and requisite cables
  • USB to TTL device
    • I use the HiLetGo CP2102, which comes with female DuPont ribbon cable. You may also need a USB extension cable with this device, or you can purchase a cable with the CP2102 hardware built in.
  • Files in this GitHub repository

PC Setup, Part 1

The PC that will run the games in the public location will be referred to as the "kiosk PC" for the rest of this post. Ideally, it should run a version of Windows with access to the Group Policy Editor, to minimize or eliminate disruptions from Windows Update.

This section assumes you have set up and configured the games you plan on running.

Create a folder named bat on the desktop. Download nfcread.py, nfcread.toml, update.py, and wallpaper.py from the above repository and place them in the bat folder.

Required Software

Install these programs before continuing:

  • NirCmd - Go to the bottom of the page and download the appropriate version (almost certainly the 64-bit build). Unzip the download, open nircmd.exe and click on the button to Copy to Windows Directory.
  • The latest version of Python 3
  • An SFTP or SCP client - I prefer WinSCP
  • The Raspberry Pi Imager or some other means to flash Raspberry Pi OS to microSD (can be done on another networked PC)
  • The CP210x drivers for Windows (CP210x Windows Drivers, not the Universal Windows Driver or the Serial Enumerator)

When a batch file is launched, it opens a Command Prompt window, which I find unsightly. Presentation is a priority for this project, so I want there to be as little visual disruption as possible too. As a workaround, I use NirCmd to launch the batch file without showing a window. Keeping the display unobstructed also allows for a custom image to be shown immediately before the game starts, which will be covered later.

This setup will use an NFC card reader that will read a tag and launch a game that corresponds to the mapping of that tag's UID, and then close the game when the tag is removed from the reader. A custom Python script will drive the NFC reader and monitor for NFC tags. If a tag has an ID in its internal database, it will launch the batch file corresponding to that ID. When the tag is removed, another batch file will be launched that will close the game and do other cleanup, and then the script will wait for another tag.

For simplicity, I create a folder on the Desktop for batch files, named "bat", and add all .bat files to the whitelist. The batch files will create another batch file named "exit.bat" that will close the game when the tag is removed.

Creating the Batch Files

I place all batch files in a folder named "bat" on the desktop. A sample batch file is included in the GitHub repository for this post. Edit it to suit the game's requirements and save it to the bat folder. At minimum, you'll need to edit line 6 with the game's name, line 16 with the name of the executable of the game, and the end of the batch file with the commands to launch the game.

For some programs, such as certain emulators, you may need to change the working directory to the program's own directory with a cd command before launching the game.

The sample batch file will use NirCmd to move the mouse cursor to the corner, hiding it, and prevent the taskbar from appearing at all. If you need to show the taskbar, open the Run dialog with Win + R and run nircmd win show class Shell_TrayWnd.

Advice for Configuring Games

Wherever possible, configure games and emulators to accept background input.

MAME

Most of the games in my own setup are emulated games with command line arguments to load a save state. For example, MAME would be launched with the following at the bottom of the batch file:

cd C:\Games\MAME\
mame.exe sf2ce -state 1

and line 16 in the sample batch file would be changed to:

    echo taskkill /f /im mame.exe

Even after launching a game in MAME with a save state in the command line, any warnings about imperfect emulation will display and must be dismissed with a button input. If the save state is on a character select screen, pressing a button to close the message may pass that input to the game and accidentally select a character. To avoid this, if the game has such a warning, create the save state on the title screen instead and have the players press their Start buttons to begin.

Some games have very low audio volume, as they were expected to have their speakers connected to amplifiers. If the game's built-in volume controls in the service mode still cannot increase the audio to an acceptable level, the volume level can be further increased in MAME by opening Slider Controls and increasing the Master Volume.

Emulators With Configuration File Overrides in Command Line

Some emulators allow for different configurations to be used from the command line. I use this to override controller mappings for games that don't support in-game controller remapping. For console games, I recommend remapping controls such that the button to confirm selections is in a consistent place wherever possible, or indicate which button is used to confirm selections in your instructions pages for the digital sign.

Other

Some of my games are old PC games that either lack or have poor joystick support. For these, I use AntiMicroX to simulate keyboard input with controllers. AntiMicroX needs to be launched alongside the game with an autoloaded profile and closed along with the game. To launch AntiMicroX, add this line before the game:

start /min antimicrox.exe

and add to the taskkill command:

    echo taskkill /f /im game.exe
    echo taskkill /f /im antimicrox.exe

Programming the NFC Tags

nfcread.py reads only the ID of NFC tags. It will not perform any reading or writing of data within the tags themselves. Instead, it compares the tag's ID against a TOML file that contains mappings from the ID to a command. When the tag is scanned, the corresponding command is executed immediately, and a single exit command is started when the tag is removed, after a brief delay for safety. If a tag is scanned whose ID is not defined, it will be written to the file new_tags.txt once the program exits with Ctrl-C. By not reading or writing the contents of a NFC tag, the commands that correspond to the ID can be easily changed without needing to change anything about the tags themselves, and the contents of the tags can be used in another application.

Creating Loading Backgrounds (Optional)

On the desktop, create a folder named "wallpapers". In this folder, put a default background named "000" in an appropriate format (JPG or PNG). You can place any number of other wallpapers that will be randomly selected and replace the default background, acting as loading messages. Ensure they have names that will be sorted after "000" alphabetically.

As an example, my own setup contains an static image named 000.png, and the loading images are sequentially numbered and consist of text written over the same image, with messages such as "GET READY" and "GOOD LUCK" and "ONLY ONE WILL WIN".

This step is handled by wallpaper.py. Open it and edit line 22 to the path of the wallpapers folder, with double backslashes, if necessary.

Raspberry Pi Setup

This setup will use the standard Raspberry Pi OS with desktop environment. Install it with the Raspberry Pi Imager or your alternative imager of choice. The rest of this section assumes the default username pi, and Bookworm is the installed version of Raspberry Pi OS.

The Raspberry Pi will be connected to its own dedicated display. For setup, it will need an internet connection to download updates and other software, but once deployed, no internet access will be needed as long as it is in service for this task. The media player mpv will be used to show the media in the directory /home/pi/Pictures/idle while no game is loaded. When a game is loaded, a matching image file in the directory /home/pi/Pictures/games will replace it, until the game is unloaded. Depending on how the Raspberry Pi is mounted in your setup, updating the images may require remote access, either with SFTP over SSH or Samba access, which is outside the scope of this guide.

Set the Raspberry Pi OS installation up to your preferences. Additionally set up networking on your local network, enable SSH access, update the OS with apt update && apt upgrade, and enable autologin to the desktop of the default user. The steps to do this are outside the scope of this guide, as the exact process is subject to change over time as Raspberry Pi OS evolves.

After doing so, run sudo raspi-config, go to the Interface Options, then Serial Port. Answer to disable the serial login shell, then to enable the serial port hardware. This allows serial communication over UART with the serial console disabled, which is necessary for the following steps. Connect the GPIO pins 6, 8, and 10 of the Raspberry Pi (GND, TXD, and RXD) to the corresponding pins on the USB to TTL device (TXD to RXD, and RXD to TXD), but do not connect the device to the Windows PC yet.

Install mpv and create directories:

sudo apt install mpv
mkdir /home/pi/.config/mpv
mkdir /home/pi/Pictures/idle
mkdir /home/pi/Pictures/games

Create the file /home/pi/.config/mpv/mpv.conf with the following contents:

fs=yes
idle=yes
image-display-duration=30
input-ipc-server=/tmp/mpvsocket
loop-playlist=inf
osc=no
osd-level=0
shuffle

Change the image-display-duration value if desired, and remove the shuffle line if you don't want the idle assets to be played in random order.

Create the file /home/pi/.config/autostart/mpv.desktop with the following contents:

[Desktop Entry]
Name=mpv
Exec=mpv /home/pi/Pictures/idle/
Type=Application

Create the file /home/pi/.config/autostart/mpvserial.desktop with the following contents:

[Desktop Entry]
Name=mpvserial
Exec=/home/pi/mpvserial.py
Type=Application

Download the file mpvserial.py from the repository and place it in /home/pi/, and mark it as executable (chmod +x /home/pi/mpvserial.py).

This changes the Raspberry Pi into a rudimentary digital signage display, starting mpv on login with a playlist of media files in /home/pi/Pictures/idle/. Upload media (images and/or videos) to be cycled in an infinite loop to /home/pi/Pictures/idle/ and images to show upon game loading in /home/pi/Pictures/games/, ensuring that the file names for the game images exactly match what you wrote in the batch files on the PC, case sensitive. Once done, launch mpv with mpv /home/pi/Pictures/idle/ for the purpose of testing in the next step.

PC Setup, Part 2

With both the kiosk PC and the Raspberry Pi connected via serial, test the setup by scanning an NFC tag that has been configured to launch a game. Close all open windows, hide the desktop icons by right-clicking on the desktop and unchecking View > Show desktop icons, and scan an NFC tag with its mapping configured. The wallpaper should change, the game launch, and the Raspberry Pi now show the corresponding image for the game. Afterward, remove the tag and all of the above should be undone.

Technical Details

Communication with the Raspberry Pi consists of issuing serial messages to change the mpv playlist to a single item, the image file named for the game name in the batch file. This is done by update.py, which sends the requests through the USB to TTL device's COM port to the serial UART pins on the Raspberry Pi listening for those commands by running mpvserial.py in the background.

If possible on your version of Windows, disable notifications and automatic reboots. Utilities such as Winhance can automate this process.

Further Considerations

This section contains suggestions for further customization, including some ideas that may be more appealing for your own setup that I don't necessarily use myself.

The Python scripts that manage the Windows wallpaper and the mpv display are deliberately simplistic. It would be possible to, for example, change the wallpaper to a slideshow by editing the Registry or use mpv for Windows to show a video loop and show that instead of a static background, which would be necessary in a location like a game room where a long time may pass without anyone playing. Modifying the scripts to do this is left as an exercise to the reader.

To further expedite the process of installing and starting games, I also defeat many of Windows' security features and prevent it from accessing Windows Update with TinyWall and Windows Personalization Utility. Security is not a concern for me with this PC, as it acts as an embedded device with no personal information included, and the greatest risk is that the OS somehow becomes corrupted. The last thing I want is for any games to be interrupted with notifications or Windows Update notices, so the tradeoff in security is acceptable to me.

As a mystery tournament organizer, one of my highest priorities is to reduce player friction wherever possible. The controller I provide for both players is a custom-built arcade-style control panel, with a pair of GP2040-CE boards in XInput mode. As the majority of the games I run can be played on 8 or fewer buttons and digital 8-way movement, I connect these controllers first. I use a pair of 8Bitdo Ultimate Wired Controllers as controllers 3 and 4 for those games that cannot be played this way. The emulators and games I use can be configured to use any of these controllers as long as they are connected in the same order every time. In my experience, I have found that the following emulators will consistently map controller ports to the controller order you expect: * RetroArch * MAME/HBMAME * Dolphin * DuckStation * PCSX2 * RPCS3

I formerly used BizHawk for the majority of consoles it supported, but it seemingly polls controllers in random order, requiring me to remap controllers from scratch despite following a strict ritual of connecting specific devices to specific USB ports in a specific order. Because I use a large two-player arcade stick that cannot be easily swapped, this is an unacceptable program quirk, and so I am forced to use RetroArch, which can be configured to force controller device IDs to map to specific input ports. It also supports loading a save state automatically on game launch with a single "Auto" save state. If you should need multiple save states for different game modes, the batch file can be written to copy a different save state into the file path to the auto save state before loading.

One of my eventual long-term projects is to reconstruct this setup in a Versus City-style double cabinet arrangement, with digital signs mounted below the game monitors for the players' convenience. Both will use HDMI splitters.

Gold, silver, and black cards with an NFC sticker hidden inside.

I bought a set of 100 NTAG 215 cards to use with this project and they all work as expected. I then got the idea to use small NFC stickers that can be embedded in 3D printed cards. I've had to modify my NFC reader enclosure to accommodate the cards due to their thickness, as I've had to print the cards to roughly three times the thickness of a typical card to contain the stickers. These custom cards are printed in metallic colors, so I have designated them "boss cards" for the latter end of the bracket, containing the most choice selections (games that take more setup than usual).

I have found that the NFC reader I use is sensitive enough to NFC tags that it can scan tags through its 3D printed enclosure, meaning that any cards that come in contact with the reader without going into the slot can be scanned, potentially causing conflicts if two or more are in range. To mitigate this, I lined the inside of the enclosure with aluminum foil except for the top surface of the reader. This ensures only cards that are inserted into the slot are read.