On the programming side, I mentioned I had no experience with Unity. That was the first hurdle to overcome. I still don't know a lot, but I know enough to rig some things up. The Unity site has a ton of tutorials. I have been through several, and can recommend this one: https://learn.unity.com/project/ruby-s-2d-rpg - I have always been less of a visual person when it comes to programming, and Unity tries to simplify some complex ideas into a set of menus or attributes. It does a great job, but I sometimes find myself confused when trying to accomplish seemingly simple tasks.
Each 'scene' in Unity has three different main methods (and, of course, you can add your own). One very handy, but easy to overuse, method is update(). If you want something to execute over and over again, it's usually best to stick it in this method. It ensures that it happens every frame. The performance of your Unity application can be greatly influenced by how much stuff your update() method contains.
There is also awake() which allows you to set up variables and other items when the script is instantiated. These are attributes, typically, and can be called from within update(). Similarly, there is a start() method, which happens only when the object's first frame is run.
Between the three (and mostly in update) you add your methods that handle various items. For example, my attract mode is split into several distinct backgrounds. I could create separate scenes for them (and that's probably the more Unity way to handle it), but instead, I wrote a method called from update that checks to see if a sound is finished playing (the Sierra startup sound), then swaps the background and enables a mode to choose to start a new game or continue.
From there, that mode is destroyed and another is added to allow you to choose your character class. At that point, it transitions to the game proper.
Pretty simple, but it took me an embarrassing number of questions to the extremely patient folks at Multimorphic (thanks Thomas!). Multimorphic is very serious about supporting developers for their platform, and it shows.
One of the really cool features in the SDK is the separation of display from hardware. The abstraction happens through a small API that allows you to transfer information from the screen to the hardware and vice-versa. It's extremely flexible. So, for the modes I mention above, you change the item selected by pressing the flipper buttons. I have to catch that press and change something on the screen as you do.
Another great feature in the SDK is the ability to write custom lightshows with fading and crossfade between colors. This is all built into the SDK in such a way that you can typically do what you want with just a couple lines of code.
As many homebrewers (and likely professional pinball programmers) will tell you, light shows are extremely tedious to program. When you add in RGB capabilities (every lamp on the Lexy playfield in the P3 is RGB), it gets even more tedious. This very nice ability in the SDK is most welcome.
The SDK works with a version of Unity that is available on Windows and the Mac. This covers a huge chunk of developers, but I'm a Linux guy. I do almost everything programming-related in Linux or Linux on a Chromebook, so this was a bit of a transition as well. That said, I'm getting used to using vscode and Unity in Windows. I bought a refurbished laptop with a reasonable solid state drive and enough memory to run Windows and these programs for about two hundred dollars. When I have a lot of time to work in a row, I plug into a second monitor and full keyboard and mouse. But with the laptop, I can always work on my lap if needed. It handles Unity just fine.
The SDK simulates the hardware and the playfield display. This hardware simulation works in a very similar way to pyprocgame or skeletongame. As such, the only barrier to entry is a Windows or Mac that can run Unity. It's extremely convenient as you bind certain keys to certain switches. There is a virtual ball that you can drag over the playfield monitor and hit those virtual targets. Between those two functions you can pretty much test everything. I'm not clear on how the backbox monitor is handled yet, but that'll come.