Categories
Uncategorized

Save Anywhere

This post is inspired by the current discourse about Pacific Drive‘s save system. It’s a good game and I’m not intending to pick on them. The technical challenges they describe are real, and we’ve all faced them. This is my solution from my solo games.

What Does It Mean to Save Anywhere

“Save anywhere” means the ability to save a game’s progress and restore it to the exact state, or as near as possible. A typical example is a game where a player is allowed to quicksave and quickload at will, perhaps to retry a difficult sequence over and over without consequences. A perfect example is retro console emulator save states, which store the complete memory layout and whatever else they need to load later, with everything exactly as it was, down to the emulated CPU timing. In non-emulated environments, saving the complete state of system memory is not feasible; we, as developers, need to make decisions about what information is important enough to write to disk.

What Do You Save

Everything. Every entity. Every component. Every property. Save everything.

There are various ways to do this—some engines or frameworks or languages provide high-level reflection and serialization, enabling a game developer to simply save the scene/world and get everything within it “for free”. If you’re not working in a space that already does that heavy lifting, this means iterating every entity in your scene and writing all their important properties to disk. This is The Hard Part if you don’t do it early in development; the goal is simple enough, but determining what is an important property is not obvious, and trying to figure out the set of important properties when the game is full of hundreds of entities with tens of thousands of properties is a testing and development nightmare. This is the main reason why games that do not plan for save-anywhere from the start cannot feasibly add it later.

What Do You Not Save

Except!

There are some things that don’t need to be saved. Static entities that are part of an authored level do not need to be saved (unless some part of their state is mutable); when the saved game is loaded, they will be instantiated as a part of that level. Their properties are effectively saved once in the game’s content files, never to be changed.

And other properties may not need to be saved if they can be automatically determined from context. AI behavior states should fall out naturally from all the other state in the game. It’s probably not necessary to remember that enemy X was doing an attack animation at player Y, if you’ve already saved that enemy X is aware of player Y. When the game is loaded, the AI behaviors will fall into place from that information, and enemy X will attack player Y.

When Do You Save

All the time. Constantly.

When to save is partially dependent on the game design, but there is no game design or genre where the player should not have a reliable backup save file in the event of a power outage or Alt+F4.

Save the game when the player clicks Save Game. Save the game when the player quits for any reason. Save the game at regular intervals. Make sure that the player’s progress is never lost.

I’m a big fan of the Gunpoint style of rolling autosaves (for certain types of games, see below). When you save the game at regular intervals, do it in a rotating buffer of save files, so the player can restore to various points in their past, whether they were managing their save files or not.

For most games (see below) don’t autosave the game during combat. Don’t autosave the game when the player is in any dangerous motion (perhaps in midair, or any other unusual move). Don’t autosave during a cutscene unless your game is all cutscenes, and then this probably isn’t the right blog for you.

When Do You Load

This is where the save/load system really depends on the game.

For most games, you should let the player load any save file at any time. Give them a menu of manual saves, autosaves, their most recent quicksave, cloudsaves, friends’ cloudsaves, chapter bookmarks, who knows, who cares? Let them jump to the place in the game they want to go to, at any time, for any reason.

But! Roguelikes are a special case, because you don’t want the player to abuse the save system as a way to cheat death. That doesn’t mean you shouldn’t save the game! Always save! In fact, for roguelikes, you should ignore the rules above about not saving during combat. Don’t let them savescum out of a tough situation! The player might encounter a crash, or need to quit suddenly to go deal with some real life. So always save! But for roguelikes, the player should not have any choices about what to load; it is always the most recent save, so they cannot cheat.

Save Anywhere in Rosa

Finally, the technical bit, specific to my engine.

The Rosa engine uses a pure entity-component framework, where every game entity is of a singular type, composed of virtual components which inherit from a base class. Every entity belongs to the scene. Every component belongs to an entity. Every saved property belongs to a component. So the save process goes:

Basically: iterate the entities in the scene, and iterate the components in each of those entities, and write the important properties of those components.

There is no automatic system here, no reflection of variables that get automatically serialized. When I add a member to a component class, I also add it to the ::Save function. This is The Hard Work.

But, this isn’t a lot of work if you build it from the start! I rarely touch these save/load functions, and when I do, it’s because I’m in the middle of writing a new component, or new functionality on an existing component, and I know what parts need to be serialized. It looks like a lot of work, but it is extremely low maintenance.

Loading is basically the same process in reverse, and with conditions to handle different save file versions:

This is why each component serializes a version number, and it is how I make sure that old save files will be compatible with newer code; if the component was serialized with an old version number, it will simply skip loading the related properties and use their default value. Again, this looks messy, but I don’t ever have to think about it! It’s not zero work, but it’s just following a mindless pattern, and it Just Works.

Why Should I Care?

You might think your game design doesn’t need save-anywhere, because it’s meant to be difficult or it’s a short game or whatever. I think those are bad reasons to not do save-anywhere (as I noted above, roguelikes probably need save-anywhere more than any other genre), but if nothing else, consider this: save-anywhere is a huge time saver in testing your game. Anyone who has ever had to replay a 5-minute sequence in a game to get back to the part where a bug can be reproduced should understand this. When you can save/load anywhere, you can reproduce (most) bugs almost instantly, which saves a ton of development time and frustration. Even if your game has no player-facing save system, the ability to restore the game state to repro a bug is immeasurable.

Do it for the players. Do it for QA. Save anywhere.

Categories
Uncategorized

On Credits, and Other Things

Hi, I’m David Lindsey Pittman, and I make games.

Growing up, I was embarrassed by my middle name: Lindsey. That was a girl’s name, and I was a boy. I didn’t want anyone to know that was my name.

In my early years working in games, a lot people called me Dave. For a while, I hated it, because that wasn’t my name! Then I accepted it; and I didn’t just accept it, I adopted it. For a hot minute, I was Dave, and Dave was me. Then I rejected it again.

Names are fuckin’ weird yo, and I’m cis so I’m sure I don’t know the half of it.

Sometime during the pandemic year (I mean 2020, but they’re all pandemic years since then, right?), I decided to start using my full name in the credits. “David Lindsey Pittman”. No one calls me that, but that’s who I am. There are a lot of David Pittmans in the world, but I’m pretty sure I’m the only David Lindsey Pittman.

That was the same year I got into a brief spat on Twitter with a stranger over some hot take I tweeted about guns in video games. I don’t remember exactly what I said, and I don’t remember exactly what he said, but I definitely remember that I replied that I could end his career with a tweet. Because it was bullshit machismo posturing on my part, and it would have blown over and been forgotten the next day, except that a shitty alt-right comicsgate site decided to make a story out of it; and 4 years later, that’s still in the top results if you Google my name. The internet doesn’t forget anything.

On Halloween 2022, driving home from a screening of The Rocky Horror Picture Show, I was stopped by Plano PD for speeding, and arrested on suspicion of DWI. Within a week, if you Googled my full name, you could find my mug shot and my home address. I don’t mind owning up to my fuck-ups (fucks-up? how do you pluralize fuck-up?), but it is extremely fucked up that personal information in police and court records can get exposed online by sharks who will ask you to pay them to take it down.

I’ve lived my life online for as long as there has been online. I was DavidLP013 on AOL. I was David “Kyp Durron” Pittman in Unreal mods. I was MillennialDavid on AIM, before “millennial” was a thing anyone said. Then I was dphrygian, and that one stuck.

Sometimes, it would be nice to be invisible. I probably wouldn’t have so much anxiety if I just shitposted from an anonymous account, instead of being “David from Twitter” as my coworkers affectionately(?) call me. But for better or worse, my life is an open book. I’m 40 years old today, I’ve made more games than mistakes, and I hope the next chapter is a happier one.

I’m David Lindsey Pittman. And I make games.

Categories
Uncategorized

Schloss der Wölfe

I made a game about hunting Werewolf Hitler and punching Nazi zombies in the face. It’s called Schloss der Wölfe (“Castle of the Wolves”) and it’s out now for Windows and Linux: https://dphrygian.itch.io/schloss-der-woelfe

Schloss der Wölfe (or “Wolf” for short, because I’m not going to keep typing ö) was conceived as a short personal game jam, in the vein of the 7DFPS jam but on my own schedule. I started work on it on September 15, and intended it to be done by September 25. It’s now almost Halloween and I’ve just launched it, so… what happened?

For starters, it was probably a poor idea to do a game jam during a real crunchy month at my job. I made very slow progress the first weekend and had no time or energy to work on it midweek, so by the second weekend, it was clear that I wouldn’t hit my deadline. But I still wanted to finish it, so I let it slide into October. It’s a good game for Halloween-month, after all. Then it slipped the mid-October timeframe, as I got sidetracked with various tech detours and let the scope of the project grow beyond the original plan. But I was determined to finish it by Halloween, and here it is!

Wolf is the third in a series of minimalist first-person games I’ve made over the past two years, after NEON STRUCT: Desperation Column and Li’l Taffer. These three games are similar in design to my commercial games of the 2010s (Eldritch, NEON STRUCT, and Slayer Shock)—it’s what I love to make and what my engine was largely built to do—and one of my goals for each was to test new features and improvements to my engine by forcing myself to ship something small instead of noodling on tech with no real target.

I had begun some initial work on Eldritch 2 earlier this year—and I hope to get back to it now that Wolf is done—but that project got held up for months as my life got busy with buying a home, summer parenting duty, and the aforementioned crunch, among many other things. So Wolf was also a good opportunity to clear my mind and shake off the rust after a few months away from regular work in my codebase. As such, I let myself explore avenues that I’d normally avoid during a game jam, such as:

  • Reorganizing my deferred renderer G-buffer to replace the reflectance channel with an implicit reflectance (determined by metallicity) and freeing up a channel for ambient occlusion maps.
  • Adding new features to the texture generator that I developed earlier this year for Eldritch 2, including support for creating AO maps.
  • Completely rewriting my procedural geometry generator functions, to simplify the process of adding new shapes.
  • Replacing the simple echo filter I’d been using since 2018 with an actual reverb filter (which involved a crash course in digital reverb design—fascinating topic that I never knew much about before).
  • Implementing console commands, finally.

So, yes, a lot of tech detours; but it accomplished that goal of shipping something new and not just leaning on what my engine could already do to make a game as fast as possible. I consider that a success!

Enough about tech, let’s talk about the game. This is the original pitch, as I wrote in my email thread of potential game jam ideas:

Something I’ve never done before is a zombie game, and I have a feeling zombies in my signature toy aesthetic could be funny AND tense. Some light survival mechanics in a mid-sized generated city (or castle?) and a simple L4D style goal… that’s compelling. Maybe make it a Wolfenstein-core Nazi zombie thing for added punch. Could also use the comics style of Fray [an abandoned game project of mine, not the Joss Whedon comic], and really lean into a classic Captain America vibe. Call it “Schloss der Wölfe” (Castle of the Wolves) or something. Codename could be “Wolf”.

Of course, I’m not the first person to discover that the only thing better than killing Nazis or killing zombies is killing Nazi zombies; but as I said, I’d never made a zombie game before, so it stuck. The original idea was more stealth- and survival-oriented, but as I began to explore the concept and came up with Werewolf Hitler (initially as a joke that mi-i-ight be a stretch goal at best, but eventually became the central premise of the game), the tone got sillier and the genre shifted toward action.

I nailed down the high-level shape of the map and the pacing beats pretty early, but the bulk of the level design didn’t happen until this past weekend. In between, I focused on the mechanics: splitting my hands state and weapons system apart to support dual-wielding (the left hand can only punch, but it is technically a weapon in the code), optimizing AIs for zombie hordes, adding a simple zombie toxin status effect, and a whole lot of bespoke scripting for the (ahem) big boss fight. Also, it’s truly surprising to me that I’d never previously shipped a game with explosive barrels. I’m happy to finally check that one off my gamedev bucket list.

I’ll probably share more as I unwind over the next few days and reflect on this project. It’s my favorite thing I’ve made in a while, but it’s been six weeks of intense work, and I’m tired.

Categories
Uncategorized

NEON STRUCT: Desperation Column gamedev postmortem

NEON STRUCT: Desperation Column is the third game in the NEON STRUCT series, and the second that I’ve released as a free game jam.

The reason I’ve been doing game jams again recently is that sometimes I need the extrinsic motivation of a theme, a competition, or a deadline. I like making games, but it’s easy to get lazy, and an extra push doesn’t hurt.

This is the first game jam I’ve done in a very long time that put gameplay first. I’ve been doing narrative-focused stuff for a while, and that’s been great too; but this is pure action gameplay, and I’m at home here.

I started from a codebase that already had a lot of the things I needed for this game. I think that’s fine for a game jam, it’s the same as starting with Unreal or Unity + your fav plugins, but I definitely didn’t make this from scratch in a week.

The 7dfps jam has generous rules (read: no rules), but I wanted to finish on time AND make something good. So I knew I’d have to be careful about scope, and baby I LIVE for being careful about scope.

My initial parameters were: 1 weapon, 1 enemy type, 1 environment. That grew to 2 enemy types, plus an NPC type, and 3 environments (sorta); but the ultra lo-fi style meant those additions didn’t explode scope.

You can search bodies and containers, but there is only 1 resource to find: ammo. You have limited ammo mostly because it gives you a reason to search containers. And it maybe creates tense moments when you run out.

There are no health pickups, no upgrades or unlocks, no skills or perks. No conversation trees, no shops, no character customization. I’ve done all those things before, I still have the code, and I chose to not do them here.

The result is a minimalist game that is now one of my favorite personal projects: a pure distillation of the action/FPS mechanics at the heart of all my work. I think it’s the closest thing to Eldritch I’ve made in a long time.

And it broke me out of a mental rut where I’d felt like doing this sort of thing was too hard, or would take too long. It wasn’t, and it didn’t. It was a sheer joy to make this game, and I hope that shows.

Categories
Uncategorized

They Called It Rapture

I quit my job in March.

I worked at 2K Marin for over five years, and I quit my job in March.

I didn’t make a lot of noise about it at the time, in part because I was still figuring out what I would do next, and in part because I didn’t want to draw any negative attention toward 2K Marin and the nascent XCOM project (now re-revealed as The Bureau: XCOM Declassified, which I’m thrilled to see is getting a much warmer reception this time around).

Making XCOM was a long and strange journey, and I hope to share some of those stories after the game is released; but today, I want to reflect on the early days at 2K Marin. Making BioShock 2 has been the highlight of my career so far, and I can’t imagine a more passionate and supportive team than the people I worked with on that project.

I had less than a year of professional experience under my belt when I applied for a job at 2K. I hadn’t yet shipped a game. I had a borderline stalkerish knowledge of the BioShock team. In retrospect, I don’t quite understand how I got the interview, much less the job, but I am eternally grateful to Alyssa Finley and Carlos Cuello for taking a chance on a huge BioShock/Irrational fanboy.

I was introduced to Jordan Thomas on my first day, but I didn’t actually know him by name. Only later, after coworker Johnnemann Nordhagen (now of The Fullbright Company) reminded me of Kieron Gillen’s fantastic PC Gamer article “Journey Into the Cradle,” did I realize that our creative director was the same man who designed Shalebridge Cradle and Fort Frolic.

I geeked out a lot in those days.

The early days of 2K Marin were a surreal experience, far removed from the norms of AAA game development. We were a tiny team, focused on bringing BioShock to the PS3 and full of heady ideas about a sequel. It felt more like a garage band than the newest branch of a multi-million dollar corporation. I was outside my programming comfort zone, maintaining Ruby scripts for a fragile build process, and I didn’t even care. The vibe was amazing.

We soon moved into a larger space and began growing the studio to develop BioShock 2. We hired a couple of fantastic AI programmers, Matthew Brown and Leon Hartwig, who I would later be fortunate to join on the AI team. I got to learn from an incredible design team: Jordan Thomas, Zak McClendon, JP LeBreton, Steve Gaynor, and Kent Hudson, to name a few.

I learned as much as I could. I gave as much as I could.

A tangent: I anchor the years of my life with particular events and use that to contextualize everything else. I guess everyone does this, but I feel like I’m particularly conscious of the process. I moved to Great Falls in 1992 (the year Super Mario Kart launched). Our apartment caught fire in 1998 (the year Unreal launched). I got married in 2007 (the year BioShock launched).

I don’t remember a lot of 2009.

I mean, I do. I remember drinking Anchor Steam and playing Persona 4 in a hot apartment. I remember jocular AI team meetings with Matthew Brown, Kent Hudson, Harvey Whitney, and PJ Leffelman. I remember staying late at work, gorged on bad Chinese food, trying to solve some elusive bug in the last level of the game. But I don’t have an anchor. When I try to contextualize events around that time, that entire year is simply the year I spent making BioShock 2.

And I’m okay with that.

Because that experience is everything I ever imagined game development could be. I loved the game I was making. I loved the people I was working with. I loved the work I was doing. What more could I ask for in a job?

I played BioShock 2 again this week. The last time I played it was the week it launched, in February 2010. Three years later, I still clearly remember the shape of the thing, but I’ve forgotten the details. Some parts are better than the version in my memory. Some are worse. I try to imagine what it would be like to have played this game as a fan, as someone who loved BioShock but wasn’t involved in the creative process for two years.

I can’t do it.

The game is inseparable from my memories of making it. I see the room where the player is first taught to use Electro Bolt to shock multiple splicers in a pool of water, and I immediately recall a bug that made splicers interrupt themselves halfway through a sentence. That room was my test case for that bug—it gave me an easy way to observe enemy AIs using dynamic VO barks without ever attacking the player. I am certain that if I revisit the game in twenty years, that bug will be the first thing I remember when I see that room.

I’m okay with this, too.

I loved BioShock. I loved it as a fan. I loved System Shock 2, as a fan. I wanted to work at Irrational because I was a fan of the games they had made. Years later, with older and wiser eyes, I can look back and be glad I never worked at Irrational. But I will never regret working at 2K Marin during 2008 and 2009. I think I appreciate BioShock 2 more as a developer than I ever could have as a fan. I will never have to question whether it was too similar to the first BioShock, or whether another visit to Rapture was really necessary. It doesn’t matter to me. I was a part of something special, the transient convergence of a hundred or so brilliant people, and that is more important than any video game could ever be.

(RIP Eden Daddy)