← Building Empire Builder: all parts

Series · 2 parts · 11 min read

Building Empire Builder

How Empire Builder: Digital came to be.

Part 1 · · 5 min read

Getting Empire Builder off the ground

Every year we came home from GenCon with a list. Games we'd played or watched over the weekend and promised ourselves we'd make time for. Empire Builder was always on it.

And every year, the list would slide right on by. Work, family, life. The list always loses.

"Surely there's a digital version"

In late 2022 we had what felt like an obvious thought: we play video games all the time, so why couldn't this be one of them? Surely someone had already made a digital Empire Builder.

A quick search turned up nothing. I shrugged and wrote the idea off.

My dev partner, Derek, did not. He went looking for whoever could make that game happen, half expecting to learn it was already in the works, and half wondering whether it was something we could do ourselves. There's a longer story there, but the short version is that he found Darwin's Toys and worked out an agreement that everyone involved felt good about.

Just like that, a couple of guys who couldn't find a free weekend to play a board game had signed up to build one.

A team of two

Derek and I were, and still are, the only developers and artists at Zombie Possum Games. I don't see that changing anytime soon. Then again, I'm sure plenty of successful studios started as one or two people who were certain they'd never need to grow.

Building a digital version of a game we already loved seemed like a gentle way to step into game development. An established game comes with a lot of built-in advantages, and the biggest one, we figured, was that we wouldn't have to design the rules.

They were already written down for us!

The rules are already written (aren't they?)

They were. Across multiple editions, released over decades, with contradictions and wildly varying levels of readability.

If I'm being honest, it was kind of a nightmare.

Empire Builder is played competitively at conventions around the world. There's an entire organization, the Train Gamers Association (TGA), built around it and games like it. The rulebook we worked from was 8 pages long. The errata document that came with it was 18 pages.

So "the rules" turned out to be a conversation between three groups: us, Darwin's Toys and the TGA. Add a healthy dose of hubris, the misplaced confidence of two developers who were sure they knew how the game worked, and you get a lot of collisions between what we built and what the rules actually said once the errata weighed in.

There was a lot of rework.

Starting before the ink was dry

I started building systems before the contract was even signed. I didn't need every hard-and-fast rule nailed down to know what the map looked like or how trains moved across it, so I dug in.

That's where the next hurdle showed up: the engine.

We chose Unity mostly because it was the only modern engine either of us had used. I'd been dabbling with it for over a decade. Derek had never touched it, but he's an accomplished C# developer, and by the time we got serious, Unity had good support for modern C#.

Knowing C# and knowing Unity turned out to be two different things. Derek came from years of building software for larger companies, and I think he'd admit that some of those habits and patterns don't translate directly to Unity, or to games in general. Today he's every bit as strong a Unity developer as I am, at least in everything this game touches. That didn't happen overnight.

The hard part, for me, was not assuming every disagreement meant he just didn't get it yet. There were plenty of grumpy late-night conversations that bled into grumpy early-morning ones.

These days we're more like a cantankerous old married couple. We mostly argue about money. That, and he refuses to put the toilet seat down.

Data first, because one map was never the plan

One principle we adopted very early: drive as much of the gameplay and map information through data as possible.

That came straight from our ambitions. We never wanted to make just the Europe map. We wanted (and still want!) to release all the major Empire Builder maps, and the only realistic way for two people to do that is to keep each map's specifics out of the code.

The catch is that the moment you have data, you need tools to manage it. From day one we were producing map data and map-specific artwork, and eventually sound, all of which needed tooling to maintain.

I'd argue our tooling could be better than it is. But in any project like this, internal tools are the first thing to slide to the back burner when there's a player-facing feature waiting to be built.

What came next

That's the beginning: a list we never got through, a search that came up empty, a partner who wouldn't let it go, and two developers who thought the hard part (the rules) was already done.

It wasn't. And the hardest problems were still ahead of us: building multiplayer for a game no off-the-shelf networking solution fit, watching our engine's licensing go sideways in the middle of development, and learning what it really means to build a game alongside another company. More on those soon.

Part 2 · · 6 min read

Multiplayer for a board game

Your first game should NOT be multiplayer.

This is a piece of advice I've seen written in a lot of places.

I can say with some authority that this is really great advice.

We didn't take it. Empire Builder is a game you play with friends. Up to six people around a table, building track across the same map and racing to the same goal. A single-player-only version was never really an option for our initial release, so multiplayer was a day-one problem.

Modern engines make networking look easy

In most modern engines there are native tools, or add-ons, that handle a lot of the boilerplate networking for you. Keeping objects in sync across game clients, updating positions in real time, that kind of thing, is a few button clicks away in Unity and Unreal.

Okay, I'm oversimplifying. But take it from me: getting two clients talking to each other is very easy in a modern game engine.

A board game isn't a platformer

The trouble is that those tools are built for a different kind of game. A digital board game doesn't have enemies charging at you or characters running and jumping. Streaming a constant flow of positions and rotations back and forth between clients doesn't make much sense when the game happens one turn, one decision, at a time.

What a digital board game does need is a whole different set of answers:

  • Which client it the source of truth? Which player's copy of the game holds the "real" state, and does the host's machine do the processing?
  • What happens when clients disagree? If two players' games end up seeing different things, who wins?
  • What happens when someone drops? A player losing their connection halfway through a two-hour game can't end the game for everyone.

The list goes on. For a game like ours, there was no plug-and-play solution. So we rolled our own.

We didn't write our own network stack

Don't get me wrong. We didn't write a network stack from scratch. That's for crazy people, or at least a specific kind of crazy person. We're a different kind of crazy.

We chose PlayFab, and it had a couple of nice synergies for us:

  • Direct support for Unity. That was nice.
  • Native support for Steam. We knew Steam would be our first platform, so this was big.
  • It's a Microsoft product, built on Azure. For some folks that's a non-starter, and that's fine. Derek and I built our careers on the Microsoft stack, so for us it felt like a win, and it opened up plenty of integration options down the road.

At the jump, PlayFab gave us a place to store player information and game (title) data, and to capture gameplay telemetry. We've have plans to leverage more PlayFab features, like game saves to persist games across multiple sessions.

A means, not a message

What PlayFab gave us was a means to communicate. It didn't tell us what to communicate, or how.

At that point, Derek was still figuring out how to install Unity, so that decision, right or wrong, fell to me. I started building a message-based communication model: every meaningful thing a player does in the game becomes a message that the other players' games receive and apply.

That model is still the foundation of what we have today. I was intent on building something that "just works," and I did. But along the way I made several messes that we had to clean up later.

Weight we didn't need to carry

Our biggest mess came from diving into implementation before we really understood how PlayFab worked.

PlayFab makes certain guarantees about how messages are delivered. Order of delivery, for example, is handled by the API and SDK, so as a developer you can depend on messages arriving in the order they were sent.

We didn't know that. So we built a lot of weight into our messaging model to handle problems the platform was already solving for us.

Gutting all that wasn't hard. But it was a meaningful amount of wasted work, on a two-person team where every hour counts.

Trust, but verify

The flip side surprised us just as much. Like so much vendor documentation, PlayFab's sometimes told us we didn't need to worry about things that, in the end, we definitely had to worry about.

So we were overbuilding in some places and underbuilding in others, and in both cases the documentation was only part of the story. The rest you learn by running the thing.

Where the abstraction runs out

This was also about the time we learned that the PlayFab SDK for Unity is an abstraction layer sitting between Unity and the real binaries underneath.

That's a big part of why PlayFab is so easy for new developers to adopt. It's also why you have a finite toolset to work with. Eventually we started bumping up against the guardrails, and the documentation on how to extend the SDK was murky, at least at the time.

Paying for help was worth it

On the plus side, we were paying PlayFab customers right out of the gate, and that came with a pretty satisfying support model. When we needed help, we typically got it in a timely manner.

For a tiny studio that doesn't have a networking specialist down the hall, that matters more than any feature on a comparison chart.

Do it, fail, redo it

Knowing what I know now, I'd absolutely approach it differently, mostly to avoid all those refactors. I'd spend a little more time up front learning what the platform already promises, and a little less time building around problems it had already solved.

But I only know what I know now because I did it, failed, and redid it. So if you're staring down a problem like this, don't let the fear of getting it wrong pull you into analysis paralysis. Build the thing. Clean it up later. You'll clean it up better than you would have planned it.

When I started my development career thirty years ago (Christ on a bike, I'm old!), my first website garbage. But, my second was better, and so on. In th same sense, my second game will be better than my first.

But don't miss the point, it will be better because of my first, not in spite of it.