Categories
Eldritch 2

Anxiety

A common feature of Lovecraftian* games is a sanity meter: generally speaking, encountering bad things makes your sanity meter go down, and then worse things happen! I didn’t do this in Eldritch, in part because of the questionable nature of modeling “sanity” this way, which already chafed against my nascent understanding of mental health; but also in part because that game was made very quickly and I never had time to fully consider how I might incorporate it into the design.

Making a sequel a decade later, I’ve had more than enough time to think about how I would do it. I’ve learned a lot more about mental health, some first-hand. I’ve found places where a mental health model could fit into Eldritch‘s core loop. And I still think it can be a vital element of a Lovecraftian* game, but I want to do it right.

My current plan is to model the player’s mental state in Eldritch 2 as Anxiety instead of Sanity. Whereas sanity is typically treated as a depleting resource, going from a baseline 100% to zero, anxiety starts at zero and grows, potentially unbounded. These are obviously both still extremely simplified models of mental health, but putting mental health stress in terms of anxiety instead of sanity shifts the cause of any subsequent ill effects from the stressed character to their stressors.

* Lovecraft was a racist, homophobic, misogynistic Anglo supremacist; I’m making a game in his mythos but I disavow the man, and that’s a whole separate issue I’ll be tackling.

Categories
Eldritch 2

Dev’s Journal – 17 May 2024

The past week has been a grab bag of small improvements and bug fixes for Eldritch 2, along with a lot of design work and planning to structure my next milestone. First up, an assortment of things from my recent svn checkins:

  • Improved navmesh tools
  • Improved spawner selection tools
  • Finished dungeon gate scripts
  • Implemented an X-ray shader
  • Improved and fixed HUD markers
  • Improved and fixed minimap markers
  • Added a lowpass filter to music when game is paused
  • Reimplemented frob highlights to address some issues
  • Added legacy/southpaw controller configurations

But the bulk of my recent work has been in design planning and documentation. I finally tackled some important issues around player progression (which was essentially nonexistent in Eldritch) and inventory/builds. I’ve had some open questions about what I wanted from these systems for a while, but I was hesitant to make certain decisions too soon, when other parts of the game were still molten or amorphous.

But the pieces of Eldritch 2 are coalescing in my mind, and I’m becoming able to distinguish where I want to repeat what Eldritch did, and where I want to depart from it, with intentionality and a clarity of vision. And answering those questions has fortuitously opened up some avenues for narrative which I otherwise might not have considered.

Related: a handful of cuts (i.e., features from Eldritch which will not be returning in Eldritch 2) are worth mentioning now:

  • Ladders/ropes: Climbing is never great in first-person games, and Eldritch‘s implementation was half-baked since enemy characters couldn’t follow the player up ladders. I’m fine just cutting this, while using my other available tools to retain verticality in level design.
  • Destructible worlds: Eldritch 2 isn’t using a voxel engine, and as such, the inherent malleability of the world won’t be coming back. This cut hurts me a bit more—blasting a path to the exit with dynamite was a joy in the first game—but the tradeoffs in visual fidelity, workflow, and performance are worth it to me.
  • Character customization: This was a late addition to the first game that I mostly did because I could. It’s likely that Eldritch 2 will have a small amount of cosmetic options; but this time around, it’s important for narrative reasons that you are Elle, the returning protagonist from the first game’s key art.
Categories
Eldritch 2

Dev’s Journal – 10 May 2024

I’ve had a few months of irregular progress and infrequent updates here, partly due to other demands on my time (like shipping a game at my day job) and partly due to a brain fog that has only recently cleared up (it turns out taking my vitamins and supplements is a good idea, actually). But the past few weeks have seen better progress, and I feel like I’m hitting my stride on Eldritch 2 development again. I don’t have a specific topic for a deep dive this week, but here’s some handwavy stuff I’ve been up to:

Procedural Level Generation

  • Room themes a la Eldritch: bias toward complementary rooms
  • Prioritizing rooms that close portals, to bias against random sprawl
  • Level design structure: oriented dungeons downward
  • Level design structure: opened up staircase rooms
  • “Gem” procgeo shape
Meta brush with attached spawners being resized

Tools

  • Meta brushes, or resizable prefabs
  • Glue/unglue brushes
  • Worldgen stats improvements and bug fixes

Misc. Features

  • Dungeon entrance portal books
  • Dungeon gates
  • Passcode randomizer

Misc. Bug Fixes

  • Fixed not being able to walk up 0.5m steps when crouched
  • Fixed audio engine race condition causing bursts of noise
  • Added VO/bark priorities to fix pain barks being interrupted
Categories
Eldritch 2

The Style (of Music)

A couple of months ago, around the time I wrote about the texture generator in my Rosa engine, I was also thinking about how I would write and record music for Eldritch 2. The music in Eldritch was a fluke: I recorded some moody guitar chords and riffs on my phone, looped them in Audacity, and ended up with a soundtrack that people actually liked. I didn’t and still don’t understand exactly how that happened, and I had little confidence that I could repeat it a decade-plus later.

I’ve become fond of non-destructive generative workflows, or procedural assets which can be edited from the bottom up. That was part of what led me to texture generation, and I realized I wanted that for music too. It’s the freedom to experiment with a safety net, a way to lock in a good idea without being locked into a bad take.

I’d been interested in modular synthesis for a while. I’ll probably never have the budget or space to build a modular synth rig, but the generative aspects of modular synth music resonated with me the same way texture generation did. There are some good modular synth apps out there, but because I’m the kind of guy I am, I wrote my own.

It’s pretty obvious how to generate a sawtooth or square wave. Less obvious how to apply reverb or a lowpass filter to an audio waveform. What if I wanted to apply an audio filter based on the amplitude of a waveform? Well, I can use an envelope follower for that, guess I’d better learn how to write one of those!

It was a fun crash course in audio synthesis, and I came out of it with a demo soundtrack that recalls the Eldritch vibes but is also immediately editable if I decide a note is wrong or I want a different filter. And if I ever miss the organic nature of strumming actual strings on an actual guitar through an actual amp into an actual microphone, hey, I’ve still got guitars and amps.

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.