The Lucas Factor

Between the new game modes, Mac and Linux ports, and updated Steam release I did for You Have to Win the Game and the free DLC David did for Eldritch,  a common trait that’s emerged across Minor Key’s releases to date is substantial post-launch support. When I finished Super Win the Game, I had no intention of adding any new content; the game said what I wanted it to say, and that was that. Nevertheless, opportunities have arisen in the months since launch to bring me back to that game, from the anaglyph 3D mode for the IndieBox release to recent changes to better support fan translations. I haven’t been documenting that work on this blog because it’s not directly related to Gunmetal Arcadia and I wanted to stay on message.

This week, though, I’ve found myself with nothing to write about because I haven’t done any work on Gunmetal Arcadia. I’ve been focused on some new stuff for Super Win, and as that has impacted the development of Gunmetal Arcadia, I want to  discuss it. I don’t know what form this blog will take next week, or whether I’ll have returned to Gunmetal by that time, but I have another few days to figure that out.

I’m planning to showcase Super Win the Game at regional events this summer just like I did last year. This time, though, since the game is already complete and released, I’m in a place where I can be more flexible in how I choose to demo it. Last year, the demo was essentially just the current build of the game with saving and loading disabled. It would always start from the beginning, optionally with a tutorial, but without a clear indication of what the game would become, either in terms of mobility upgrades and platforming challenges or in terms of narrative.

In discussing ideas for a new demo with David, I first considered launching the demo build in a state where the player already has a few upgrades and starts at the entrance to one of the dungeons, as this would be a better representation of the gameplay experience. Then I thought about building a new demo level, possibly cobbled together from scenes from the actual game, that would be a small, finite space that could more immediately convey the experience of the full game.

A few days later, I woke up in the middle of the night and had the idea to promote Super Win at shows by turning this demo level into a competition. Beat the best time and win a free Steam key! And then that quickly turned into, hey, as long as I’m tracking the time that the player spends in a level, why not turn that into a real game feature? And that led me to write up a design document for a new speedrun mode, and for the first time in six months, I was excited about Super Win the Game again.

I prototyped the skeleton of a speedrun mode in a day, with options to choose from a number of courses, each of which may prescribe a number of upgrade abilities that may be granted or removed for the duration of the course. In this way, I can build levels with specific abilities in mind and not have to worry about when the player might visit an area or which upgrades they might or might not have and whether they could get stuck. This opens the door to a purer experience, less diluted by adventure/RPG trappings and more directly focused on the fast-paced platforming challenges that have been the core of the Win the Game titles.

So I guess that’s the official announcement. A new speedrun mode is coming to Super Win the Game! I don’t know when this content will be ready for release, but I’ll be figuring out that messaging in the near future. I’m obviously motivated to get back to working on Gunmetal as soon as possible, but I also want this content to be substantial enough that it’s not just a blip on the radar. It’s easy to imagine this would be a “major update” in Steam terms, and it could be timed alongside a sale, but I’ve already seen how Super Win performs on sale, and it’s not amazing. It’s enough to get by, maybe, but that’s it. (Thanks to the magical intersection of statistics and public-facing APIs, you can see how well it’s doing on SteamSpy.) So then I started thinking, how could I make this a bigger deal? And one of the things that came to mind would be to go back, address some of the most frequent complaints with the game, make it the best, most polished version I can, add some new content for everyone who’s played the game already, and then relaunch it at a new low price.

So, that’s an interesting thing. I feel like I’m well outside the window of a price drop looking like an upfront pricing failure, but of course I’ve talked in the past about how I think the original launch price probably was misguided. And no matter how many reviews and reviewers agree that Super Win is worth its asking price — and most have — I just can’t seem to shake the disconnect with the public perception that a pixel art platformer is an easy thing to make and should be cheap, if not free, by necessity. Hopefully this new price, whatever it may be, will better align with consumer expectations. But that’s only half the story. Lowering the price also makes sense in the context of Gunmetal Arcadia. After seeing how Super Win performs (and also observing numbers of other genre-similar titles on Steam Spy and getting a feel for where the ceilings lie), I’ve been feeling like Gunmetal needs to debut at a lower price than Super Win initially did if it’s going to be any sort of a success. But that’s a difficult and confusing thing to do in light of the fact that Gunmetal will be a larger game than Super Win in terms of scope and ambition, and pricing it lower risks creating the perception of less value, of this one being a step down from the last. My hope is that by preemptively dropping the price on Super Win, I can then launch Gunmetal at an appropriate price: higher than where Super Win is now because it is a bigger, better game, but lower than where Super Win originally was because I’m starting to think that’s just too much to ask in 2015 for what I do.

I don’t have a timeline or a specific list of features for this relaunch. I’m going to run with this idea for as long as it’s exciting and try to make it the best thing I can. I can say for sure that by popular demand, I’m adding a minimap. It’s already done. I finished it on Saturday. More may be coming depending on how I feel. But there are a few things this won’t be, and I want to establish that as early as possible. I’m not doing a hardcore mode. I’m not doing a cat mode. I’m not doing a remixed campaign. Those ideas worked in You Have to Win the Game, but they aren’t things that I consider to be core to the series, and I’m more interested in finding things that work for this game rather than retreading the same ground.

Likewise, there’s always the fear of running into “the Lucas factor” and wanting to fix up every little rough edge while I have my hands back in that codebase. I’ve made a few tweaks to the CRT sim between Super Win and Gunmetal, and it’s easy to imagine that I might want to port those changes back to Super Win. But the danger is, if I’m constantly sharing every relevant change among all associated games, then it will take me longer to finish new games, the new games won’t be as fresh and exciting as they could be, and it will be harder to pin down a definitive version of each game. It’s probably better to save the new stuff for the new game and allow each shipped title to remain representative of what I wanted to make at the time it was made. It feels a little silly to talk about my games in these terms when, for instance, I’m only a few months out from releasing Super Win, but at the same time, it really hammers home just how much progress I’ve made on Gunmetal in that time, that Super Win feels like such a distant memory.

To ramble a little bit longer, I was certain in early 2012 when I was closing in on the finish line of You Have to Win the Game that it would be the last indie game I ever made. Once I finished that one, I thought, I would be done, I would have made that one game that I’d be known for, and I wouldn’t have to kill myself crunching all the time like I’d been doing. And to be fair, nearly three years later, it’s easy to think that You Have to Win the Game will in fact be the game I’m known for, if I’m known for one at all. And that’s cool. I still think it’s a great game. I think it’s a more concise, more focused experience than its sequel was, and I think it hit a sweet spot where it was substantial enough that its low, low price of free felt generous and attractive.

I didn’t think I could ever feel good about Super Win the Game after its launch. I was burned out on working on it, talking about it, and trying unsuccessfully to promote it. It was anathema for a long time. It’s hard to say what changed, but I’m happy to be over that. It took half a year, but I’m finally proud of Super Win again and excited to be revisiting and updating it.

Player Awareness

Today’s update is going to be super short because I spent the majority of the week doing some post-launch work on Super Win the Game. But I’m making up for it with a ton of GIFs, so.

This week, I continued working on new enemy designs which have brought with them a handful of new AI features.

GunArc 2015-04-06 02-29-32-590

First up is some sort of bug thing. It spits acid? I’m still trying to figure out what the common theme among the enemies is going to be. I’m thinking some sort of insectoid alien species, but we’ll see.

GunArc 2015-04-06 02-38-19-943

Turrets also spit acid, at least for now. They will also turn to face the player, which is the first bit of player-aware AI code I’ve written. My expectation is that enemies will largely be able to function autonomously with no need to be aware of where the player is or what they’re doing, but there will be a few exceptions, and this is one of them.

GunArc 2015-04-06 02-38-43-284

Likewise, I’ve added player proximity tests to allow these hanging slime spawners to trigger when the player walks underneath them. I also used this functionality to create a spider enemy that behaves similarly, waiting until the player passes by to drop and attack. This required an extra bit of AI work to support a change in state from hanging motionless to dropping and moving around. To solve this, I add the ability to nest behavior sets within other behavior sets, effectively producing something akin to a standard behavior tree.

GunArc 2015-04-06 02-50-34-269

Finally, I’ve been reacting to feedback from the first playable test build. I’ve reduced the intensity of falling gravity, tweaked movement inertia a bit, removed player knockback when striking enemies with a melee weapon, and made bombs way less bouncy. I don’t yet know when the next public test build will be ready, but hopefully these changes will be a step in the right direction.

Insight

Welp, it’s been fun, but Bloodborne came out this week, which means Gunmetal Arcadia is paused indefinitely. (Not really but yeah but not really but yeah.)

For Super Win the Game, I created a dialog tree system for NPCs, signs, and various other pop-up dialog boxes. This system could display text and present the player with options which would branch the tree. It could also query the game state and branch depending on the results. Some of the more complex uses of this system were the jeweller and fortune teller NPCs, whose responses would change depending on how many gems the player were carrying, which items they had collected, and so on.

I’m reusing this system in Gunmetal Arcadia and updating it to hopefully increase its functionality while reducing some of the wordiness. In Super Win, each tree was authored once in the editor and never changed at runtime. For a known set of content, it was reasonable to make a query for the presence of each and every powerup in the game, but for Gunmetal, I’m trying to build with flexibility and scalability in mind. I don’t know how many items and upgrades I might have, so handwriting queries for each is out of the question. What I’m doing instead is making dialog trees able to be dynamically authored in code. The content I write in the editor can now contain markup indicating that the construction of the tree should be delegated to code, and additional markup can be passed to that code to specify exactly what is desired.

A snippet of a dialog tree in Super Win that queries against all known powerups.
A snippet of a dialog tree in Super Win that queries against all known powerups.
A dialog tree in Gunmetal Arcadia that delegates further construction of the tree to code.

In Super Win, dialog box options were typically limited to two or three choices (most often “yes” or “no”), but for Gunmetal, I anticipate using dialog box options for displaying lists of gear that the player may wish to buy or sell. As these lists may contain any number of items, I’ve added support for scrolling through a set of options that occupy a fixed number of lines.

GunArc 2015-03-27 01-52-23-878

After adding medusa heads last week, I continued working on new enemy designs this week, starting with two versions of enemy spawners. This prompted me to solve a problem that’s been lingering for a while. When I wrote about my entity serialization solution recently, I mentioned that a fix-up pass might be necessary to restore references between entities when loading serial data. This week, I finally tackled that problem as part of implementing correct enemy spawner behavior.

Spawners will generate new baddies up to a specified limit, but after the player kills one of these enemies, the spawner can produce another. To facilitate this, either the spawner must keep track of references to its offspring so it can determine when they’ve been destroyed, or else the offspring need to keep track of a reference to their spawner so they can notify it when they’re about to be destroyed, but in either case, a reference must be maintained in order for this system to function correctly.

GunArc 2015-03-27 23-58-17-251

As part of saving and restoring references between entities, I’ve had to reconcile two similar but distinct features of my engine: handles and GUIDs. Handles are assigned at runtime and are unique only for the duration of execution. These serve as a safe wrapper around pointers. When an object is deleted, its entry in the handle table is removed, which allows any remaining handles to that object to correctly fail a NULL test. GUIDs are unique values which identify objects that have been placed in the editor or spawned at runtime and which can be serialized. GUIDs persist beyond a single execution and are used by the serializer to associate data with a particular object. All entities and components have handles, but only entities that need to be serialized contain GUIDs.

Saving a reference to another entity involves simply saving out its GUID, as this is sufficient to locate the object if it exists. In the general case, this is trivial, but there are two problems we have to deal with:

  1. We want to save a reference to an entity that has been removed. We have a handle to this entity (as it did exist as one time), but we do not know its GUID, which is the thing we need to save.
  2. We want to load a reference to an entity that does not currently exist. We know its GUID, but as it does not exist, we cannot get a handle to it.

I went through a few iterations in solving this, and I’m pretty happy with the solution I’ve landed on. Any time a valid serializable entity is destroyed, a record is made of its handle and GUID. When that entity is rebuilt, we can look up its previous handle value from its GUID and reuse it such that any handles that pointed to the old instance of this entity will now point to the new one. This record also allows us to retrieve a GUID for a entity that no longer exists as long as we still have a handle to it, even though that handle no longer points to anything. This solves the first problem described above.

In some cases, this is also sufficient to solve the second problem. If an entity once existed but has been removed, we can look up its old handle from its GUID when we load a reference. That handle will not point to anything yet, but whenever the entity is rebuilt, it will assume its old handle value, and the handle will become valid.

The one remaining case is when we load a reference to an entity that has not yet existed during this execution. In this case, there will be no record of its old handle or GUID, so we need to reserve a handle value for it instead. Whenever the entity is constructed for the first time on this execution, it will assume that reserved handle value, and everything will work as usual. This solves our second problem case.

GunArc 2015-03-27 23-54-40-788

I’m still not quite at the point where the entire game state can be saved and restored exactly as it was, but this gets me one step closer to that goal and also validates some of my earlier assumptions about how development would go as I continue building new content.

Horizontal Slice

My last couple of blog entries started off with “it was a slow week” or “I didn’t get any real work done” or whatever, and that’s indicative of a loss of momentum I had been feeling as the sorts of tasks I was focused on were thinning. I’m reaching a point where a large majority of moment-to-moment gameplay systems exist in some form, and that left me wondering what to do next.

On Thursday, I plotted out a list of rough milestone definitions that will help me better understand what I should be working on and how it will help me reach a shipping state. Up until now, my pattern has typically been to prototype a single instance of each thing: one melee weapon, one enemy, one boss, and so on. This was done with the understanding that I would necessarily have to revisit these things in the future as I expanded the set of content.

I’m calling this my vertical slice. It’s not really a vertical slice on account of the fact that it’s missing some core elements of the high-level roguelike experience, but I don’t feel like those features would make sense to prototype yet in the absence of a wider array of content to pull from. Most of the immediate gameplay systems can be demonstrated in the current build, so…”vertical slice.”

The next step is to go horizontal and increase the breadth of content. For the foreseeable future, my tasks will consist of building new instances of the same sorts of things I’ve prototyped in the past: weapons, enemies, playable characters, and so on. My expectation is that as I produce more content, I will identify failings in my tools or places where I can better generalize code to support everything I want to do. I’m not done building features, and I don’t expect any milestone to completely displace the preceding tasks, but I do expect the workload to shift. I’ve already seen a shift away from editor, tools, and engine tasks to gameplay code over the course of the last few months. For this milestone, I anticipate spending more time in my editor and art tools producing content versus coding up new features.

At some point, these tasks will start to thin as well, and I’ll transition to the next milestone. I don’t have a good name for this one. Maybe alpha? We’ll see. This will be the point where I have a wealth of content and I can start assembling it into randomized levels. This is where I’ll start to see the roguelike nature emerge and where I’ll be able to get a real sense of the scope of the gameplay experience. As with the preceding milestones, I would expect to see some overlap in the nature of tasks here; it’s hard to imagine that implementing randomized levels wouldn’t bring new feature and content needs along with it, but those shouldn’t dominate my workload.

If that’s my alpha, then my beta will be to balance and tune the existing content, implement whatever lingering features haven’t found their way into the game yet, and try to find and enhance the long-term appeal. Some of the core promises of what Gunmetal Arcadia is probably won’t show up until this milestone. The concepts of faction conflicts and how each game session will shape the next have existed in my head since before I started keeping this blog, but I probably won’t be able to implement those in any real, meaningful way until the game is nearly complete. I can already tell that will be a stressful milestone, and one where it will be easy to want to cut corners. I can’t afford to do that.

Once I’m happy with the beta, I can think about release plans and whether it makes sense to put the game into Early Access at that point. I can imagine there would still be a fair amount of tuning and balancing to be done, especially as more players get their hands on the game. Regardless of how I release the game, the taskload for this milestone should consist strictly of bug fixes, polish, and balancing. The game should be feature- and content-complete at this point. Any features or content that have been cut can be evaluated for future updates, but my goal at this point should be to produce the highest-quality release candidate I can.

Post-launch, who can even say? In a perfect world, there would be enough interest and revenue to be able to continue supporting the game after launch with new updates and expansions. That will all depend on how well the game is received on its way to launch and beyond.

I haven’t attached dates to any of these milestones. I’m bad at estimating task durations, and too many specifics are still up in the air anyway. If I had to make a gut estimate, I’d say I’ll probably have wrapped up the horizontal slice by July when I do the expo rounds. Alpha feels like it’ll take me a few months, which would put beta near the end of this year, and then I could be ready for release early next year. But I don’t trust any of those estimates, and neither should you.


That turned out to be a whole blog post in itself, so I’ll just quickly go through some of what I worked on this week.

I wrapped up the boss prototype from last week, which involved refactoring my attachments system to better support attachments existing across multiple animation sequences. In theory, this puts me in a good place to attach armor or decorations to the player character, although I haven’t prototyped this yet, so I can’t say whether there would be any stumbling blocks there.

There are now three primary melee weapons in the game: a dagger, a short sword (the one from the previous test build), and a long sword. These all inflict the same amount of damage at the moment but vary in range and speed. They don’t necessarily all feel good to use yet, but I’ll be playing around with these values and more as I continue adding weapons to the game.

GunArc 2015-03-21 15-02-02-365

I added a “Medusa Head” sine-wave-traveling enemy to my test map. This required a couple of new features that I hadn’t yet supported; these things need to be spawned dynamically on a timer, whereas my existing placeholder enemy (the slime) was placed by hand. These also don’t collide with the environment and don’t need to be serialized when leaving a room and returning. Because they spawn indefinitely, I’ll probably also want to limit their loot drops, but I haven’t yet looked into that.

I’ve started refactoring items and upgrades into a more generalized “equipment” system. This will handle primary melee weapons, secondary throwing weapons, salable items like gems, stat upgrades, and so on. I’m in the process of figuring out how to dynamically author dialog boxes in order to list inventory contents from the status screen or when selling things to a shopkeeper.

Wall Chickens

It’s been a bit of a slow week getting back to normal development habits after GDC, but I did manage to start prototyping some features that I’ve been wanting for a while.

Continuing with the serialization work that I started a couple weeks ago, I implemented a method of preserving a record of the existence of entities that have been dynamically spawned at runtime. Previously, I was keeping track of the GUID and data definition file associated with any entity that were dynamically spawned in each room of the current map. This data is now written to the serial resource for each room of each map in order to preserve these records across map transitions and saving/loading. This is the final piece of the puzzle needed to facilitate complete saving and loading of any game state. In the future, once levels are randomly generated, some additional data will probably be required to ensure the level is correctly rebuilt when loading (e.g., a random seed or complete Mersenne twister state), but for the test maps I’m working with right now, this gets me pretty far.

I don’t know for sure whether I’ll want any instant death traps in Gunmetal Arcadia (it will depend, I suspect, on finding the right balance between challenge and frustration when tuning difficulty), but in case I do, I added a pair of features that should be familiar to Zelda II fans: bottomless pits and crumbling blocks. In Super Win, I used entities as crumbling platforms, but what I really wanted for this game was to be able to paint tiles on the map as I normally do and have some of those automatically crumble in response to the player stepping on them. Since I already tackled the issue of altering and rebuilding the world’s collision and renderable mesh while working on destructible terrain, it was a short leap to repurpose that code to support crumbling tiles.

GunArc 2015-03-10 18-38-59-229

The second half of that feature is the bottomless pit. In Super Win, I had a few cases where the player could jump off the top of the screen without automatically scrolling to the next room, when no room data were available in that direction. To support bottomless pits, I simply made this change also affect falling off the bottom of the screen, and then I added a new type of tile which does not block movement but which automatically kills the player when they cross it while traveling in a specified direction. Lining the edge of the screen with these tiles ensure that the player dies when falling off the screen.

I was able to repurpose this new tile type for another instant death trap which may or may not make the cut: Spelunky-esque walkable spikes. In the Win the Game series, spikes are implemented as solid blocks that cause damage regardless of which direction the player is traveling. Now I have the option to implement spikes which the player can safely walk through and which only cause damage if the player falls onto them from above (or whichever way they are oriented). I’ve been wanting to avoid leaning too heavily on features from the Win the Game series like spikes, but this feels like a good way to come up with some new and interesting scenarios rather than simply retreading the same ground.

As soon as I started implementing destructible terrain, I knew that I would want to hide loot inside walls. I threw together a quick prototype of what this might look like earlier this week. Right now, every tile has a 100% chance to drop loot, which is silly and obviously not what I’m going to ship, but it’s fun to see in action. There are still a number of unanswered questions here, e.g., will I want a global drop rate for random loot, or does it make more sense to place loot drops inside tiles by hand. (Or both?) In any case, this gets me one step closer to having wall chickens, so that’s cool.

GunArc 2015-03-11 00-50-35-828

The last couple of things I’ve done this week have been super quick prototypes just to make sure things will work as I expect. I threw together a test of what a dialog-driven NPC shopkeeper might look like based on the jeweller from Super Win. As it turned out, my dialog system had been broken since late last year when I made a change to decouple core game data from campaign-specific data, and this gave me a chance to find and fix that issue.

xan_shopkeep

I’m not sure yet what the complete shopping loop will look like; there are a number of dependencies that I haven’t solved yet (e.g., what does the player’s inventory look like?), but that’s becoming one of my top priorities. I’ve done a little bit of work on figuring out how upgrades to variables like jump height can be applied to the player based on collectable items, and I’m hoping to be able to generalize those patterns in a way that is conducive to building a wide variety of upgrade items very quickly.

The most recent feature I’ve been working on is one that’s been looming at the back of my mind for a while: bosses. Exactly how ambitious I get with bosses remains to be seen, but at the very least, they need to have weak points. I’ve prototyped a boss monster using two separate entities, one for the body and one for the head (the weak point). Even in this relatively simple case, trying to get these two entities to look and feel like one cohesive thing has introduced a number of complications, and I’m not yet sure whether this pattern will hold up in other cases. But for a first stab at it, I’m pretty happy with how this has turned out, and I feel more confident that bosses will be a feature I can ship with.

GunArc 2015-03-14 17-01-01-630

Public Test Build 0: Game Feel

I spent last week at GDC, so I didn’t get any development done on Gunmetal Arcadia, but as it turns out, I already had something a little different planned for this week’s update.

As I mentioned in a previous entry, I’ve been putting together a playable test build. You can download that here:

Windows: Xanadu.zip
Mac: Xanadu.dmg
Linux: Xanadu.tar.gz

This is a very early build with a minimal amount of placeholder test content. The purpose of this release is to collect feedback on game feel, specifically with regard to player character movement and combat. You can send me feedback by email to [email protected] or on Twitter to @PirateHearts.

public-test-0

This build contains a small test map with a handful of enemies who will respawn each time you enter the room. I recommend playing with a gamepad, but you can also use the keyboard, and the controls can be rebound in the options menu. The default Xbox 360 gamepad controls are:

Analog stick/d-pad: Walk, crouch, aim up/down
A: Jump (hold to jump higher)
X: Attack (can aim up or down while jumping)
B: Throw bomb (can aim up or down)
Y: Throw secondary weapon (after picking one up)

For Windows users, please note that this is the first time I have released a Windows build running my OpenGL+SDL implementation instead of my normal DirectX implementation. This was done to avoid having to build an installer for DirectX dependencies, and the game may not behave identically to the final version in some ways as a result.

The license agreement text included with this build is a placeholder for the shipping version and is not fully applicable to this release. This build may be shared, recorded, streamed, or otherwise distributed with no limitations.

Serialization

As I continued working on loot drops, I found it wasn’t feeling right to have items disappear after leaving a room. I don’t know for sure whether I’ll want items to persist indefinitely (in the current build, they have a ten-second lifetime before blinking out of existence), but I do feel like you should be able to walk away and return and still have a chance to collect something that dropped. In solving that problem, I’ve fallen down a rabbit hole of changes that may allow me to support complete save-and-restore-anywhere checkpointing functionality. To explain why that is, I’ll have to take a step back and talk about how things worked in Super Win.

I’ve described my entity-component system in high-level terms a few times already, but this time I’ll be going a little more in-depth with regards to the internal workings.

My level editor also serves as an entity construction tool. In Super Win, entity construction was done entirely by hand in XML markup; in Gunmetal, some aspects have been wrapped within a visual editor for ease. In both cases, however, the editor outputs definition files for each entity. These definition files consist of XML-like nested key-value pairs, written in a binary format for faster loading. As these files are generic in format, I use them for multiple purposes in my engine. When used to define an entity, each of the top-level tags define a component either in full or in part.

defviewer

At runtime, the game invokes the entity factory to produce an entity based on a provided definition file. The factory examines each tag, producing components as needed, attaching them to the output entity, and giving them a chance to use the input definition data to set up their own properties. At this point, the factory also queries the serializer to see whether it contains any additional data with which to define this entity’s components. If we’re just starting a new game and constructing an entity for the first time, it will not. We’ll come back to that in a bit.

The factory returns the fully constructed entity to the game, where it lives for a while and does whatever it’s supposed to do. (Essentially everything that can be seen or interacted with in the game is an entity, from the player character to enemies to loot drops to doors to signposts to particle effects and so on.) At some point, the entity will be destroyed. This may be because it’s something like a particle effect that has exceeded its lifetime, or because it’s an enemy who has been killed or a loot drop that has been collected, or maybe the player has walked into another room and this entity is being removed from gameplay. As part of entity destruction, each component has a chance to export data about its current state to a component delta structure. This data is passed to the serializer and stored until the next time the factory attempts to construct this entity, at which point the original definition data is supplemented with these deltas in order to restore the entity’s components to the state they were previously in.

In order to distinguish between similar entities which may share the same definition data, entities are assigned a GUID. These is done automatically when entities are placed in the level editor, and (new to Gunmetal) this can also be done at runtime for things that are dynamically spawned, such as loot drops. The serializer looks for the presence of GUID information when managing component deltas. If the entity has no GUID information, it is assumed to be a temporary entity like a particle effect that does not need to be serialized.

Not only does the serializer keep track of information about entities and components that have been created at runtime during the current session, it also handles disk access for saving and loading component deltas. This allows the previous state of entities to be restored on a future session. This was how saved games were handled in Super Win, and will likely be how they’ll be handled in Gunmetal as well.

So (minus one or two details I’m deliberately ignoring for the sake of brevity), these features as described should facilitate the complete saving and reconstructing of any game state. So why did I say it was a rabbit hole? Well…

~~~DRAMATIC PAUSE~~~

Despite the presence of this framework, only two components actually took advantage of this functionality in Super Win. One was the “delete component,” used to flag an entity as having been permanently removed from the game. This is how things like gems and powerups know to stay gone after the player collects them. The other was the “data component,” a component that allows any entity to store arbitrary key/value pairs. This was used for everything else that needed to be saved in Super Win besides existence. The player entity would store the name of the active campaign and map here, as well as the coordinates of the room where the game was last saved and the position of the checkpoint within it. NPCs would use arbitrary data to keep track of the few things they needed to know, such as quest completion status. Maybe one or two other things I’m forgetting.

And that was it. Literally nothing else about the game state was ever saved and restored, either when saving and reloading or just when walking between rooms. This is why enemies and hazards always reset to their original locations, crumbling platforms reappear immediately, and so on. In the original You Have to Win the Game, I actually found that behavior to be advantageous since several rooms were designed such that a mistake could put the game in an unwinnable state. Leaving and re-entering the room provided a convenient workaround to this problem. By the time I wrapped Super Win, it was becoming an annoyance, and as of this week, it’s become completely unacceptable for Gunmetal.

So now I have to go back through all my serializable component classes (sixty-seven at last count) and make sure they write out any relevant data that would be needed to restore them to their previous state when destroyed. For some components, this is trivial. The “spatial component” (more commonly a “transform component” but I’m bad at naming things) can serialize position, velocity, and acceleration data, and that gets me pretty far already. Where things get a little trickier is in dealing with references to other things. When the player is standing on a moving platform, a “basing component” on each is used to keep track of their relationship. These sorts of dynamics are more difficult to preserve. My current plan (which hasn’t been thoroughly tested yet) is to save out the GUID of the thing being referenced. On load, that thing may or may not yet exist, however, so a fix-up pass might be necessary for reinstating the reference once all the participants are present.

So far, this process has been going well. I have data being correctly serialized for position, velocity, and acceleration, for AI states, for animations, for current and maximum health values, and a handful of other things. That’s gotten me pretty far already. I expect that maintaining this system will be something that lasts throughout the entire development process (certainly if nothing else, it’s going to be something to take into consideration any time I add a new component class), but the initial impact has been fairly mild, as rabbit holes go.

Oh yeah. I wanted to draw a graph of how all these bits and pieces interact. It didn’t turn out very well, but uh.
entity_construction

Momentum

Super short update today, as I’m right in the middle of an exciting tools-related task, and I don’t want to lose momentum on it. Also this week has largely been spent on bug fixes, and those are difficult to write about in any sort of compelling manner except when they’re exceedingly complex to isolate and resolve.

I’ve been working on bringing a few features over from Super Win, including locked doors and pickups. Coins, bombs, ammo, keys, and health can now be collected in the field and are represented on the HUD. The next step will be to drop these from enemies and environmental objects (think Castlevania torches and sconces), and then after that, I’ll probably start implementing NPCs and shops. At some point, factions should start coming into the picture, which is exciting since the faction tug-of-war has been a core promise of Gunmetal since its inception.

loot

I made a small change to the audio synthesizer to reduce pops when notes are played by introducing a brief (1/200 sec.) fade-in and fade-out time on volume changes. This is most noticeable on headphones. A comparison is available here: https://soundcloud.com/j-kyle-pittman/sets/synth-comparison

In preparation for launching a public game feel test build sometime in the near future, I built Gunmetal for Mac and Linux a few days ago for the first time in months. Fortunately, nothing too major had changed or broken; I ran into a case where my GL implementation was failing to set blend modes correctly, but it was an easy fix.

Despite my comments last week on not worrying about scheduling, I’ve been curious where exactly I spend most of my development time — logging hours and estimating tasks have never been a part of my workflow, so by and large, I consider any schedule I could write to be inaccurate by an order of magnitude and wholly worthless. But if I can throw science at that problem, it gets way more interesting. I’ve been tracking my productivity as a function of SVN commits over time since launching Minor Key Games, but I’ve never had any sort of an accurate metric for how long an individual task might take. Starting today, I’m going to be spending some time developing tools to hopefully give me a clearer picture of my moment-to-moment development habits.

Roadmap

I’ve mostly spent this week tuning and fixing bugs in the features that I discussed last week, so rather than go into detail about what I’ve been working on, I thought I’d share some of my ideas for Gunmetal Arcadia‘s road to launch.

As I discussed in that game’s postmortem, Super Win was not the success I’d hoped it would be. A few months later, it looks like it might not be a complete failure either, but at best, it’s a slower burn than is sustainable. I theorized a bit in that article why that might be, and I’m taking steps on Gunmetal to make sure this game performs better. Keeping this devlog is the first of those; implicit in that is that I’m also not being so wary of showing the game in early or incomplete states. Between wanting to show representative “final” Super Win content very early (e.g., for the announcement and teaser trailer barely two months after conception) and not wanting to show too much of “the cool stuff” like the dream sequences and final level, I found myself in a state of constantly crunching to meet deadlines that did little to benefit the game’s visibility in the long run.

For Gunmetal, I’m doing an about-face on that attitude. My modus operandi this time has been to show everything, regardless of how broken and unpolished it is, hence all the placeholder art stolen from Zelda 2 and Faxanadu I’ve been putting on display in this devlog. (Happy news: as of today, I’ve replaced all “borrowed” art with original content. Still placeholder, but not so litigious.)

ga_placeholder

The next step is to start putting the game in players’ hands as early as possible. To that end, I’ll be uploading a short demo soon which will serve to illustrate the current state of core game mechanics. My intent with this demo is to collect feedback related to the feel of these core systems. Does the player move too fast or too slow? Does jumping feel too floaty or too heavy? Does combat feel solid and tactile or papery and insubstantial? Nailing the game feel is absolutely crucial, perhaps moreso than any other single aspect of the gameplay experience, and the longer I wait before gathering and reacting to this feedback, the harder it will be to make changes without inducing unwanted side effects on other parts of the game.

If this is successful, I’d like to continue making these sorts of iterative drops as more high-level systems start coming online. My expectation is that each of these builds would focus on some subsection of the game (e.g., randomized levels, item synergies, and so on), eventually culminating in a more complete, cohesive vertical slice experience.

Looking further down the road, once the game is fully playable and in a position to be polished and tuned, I could turn my attention to pre-release plans. I’ve been debating whether it makes sense to put Gunmetal into Early Access on Steam. My gut reaction to Early Access in the past has been an immediate NUH-UH, but recently I’ve been feeling like it might make sense for Gunmetal due to its roguelike aspects. It will be a while before I can fully commit to that plan; whether it makes sense will depend on a number of factors that won’t be fully visible for some time, and I would want to be careful about properly messaging what that version of the game was (e.g., a nearly complete game, not a partially functional tech demo) and what it wasn’t (e.g., a blank slate awaiting any and all input), but I think it could give me the opportunity to “soft launch” the game in a form that were technically complete and theoretically ready for release but with some extra buffer time built in to respond more aggressively to player feedback, bug reports, and suggestions.

In the meantime, I expect I will be showing Gunmetal Arcadia in some form at events this summer.  I took a playable demo of Super Win to several events last year, including RTX, SGC, and QuakeCon. I’m planning to attend some of these again, and possibly others as well. (I’ll announce dates as soon as anything is confirmed.) Whether I bring a playable build of Gunmetal depends largely on what state the game is in at the time. The worst case scenario is that it’s simply not ready to show and I just demo Super Win again (and maybe also Neon Struct), but at the very least, I should be able to bring a trailer and some promotional materials. I have the advantage this year of being able to fall back on that option, meaning I won’t have to crunch to complete a demo, an experience that I felt was severely harmful to Super Win. I spent the entire dev cycle on that game worrying about development time and crunching towards milestones only to land ahead of schedule and unsure of what to do next. This time I’m just not worrying about that, or really anything else. I know how to make games. I know I work fast. I’m not going to beat myself up over it.

Grindstone

I’m back! Well technically, I was back from my honeymoon last Monday, but I had already scheduled that post in advance, so this is the first new material I’ve written since getting married and traipsing around Europe.

I chose to spend this week knocking out a few tasks that had been looming overhead for a while, the last few things that need to happen before I feel like the complete set of immediate player actions exists in some form. The first of these was to alter the size of the player’s bounding box in response to crouching and standing. Historically, my entities have had one primary collision primitive that never changes in size or relative location, which is why holding the down button in Super Win just makes the character tilt his head down rather than crouching. For Gunmetal, I wanted to have a proper crouch that could be used to avoid enemy shots, and that meant tackling this issue.

crouchcoll

My editor now has a list of primitives which may be associated with animation sequences. At runtime, the game looks for changes to this setting whenever the current topmost animation sequence changes (as I mentioned in a previous blog, multiple prioritized sequences may be playing at once, with the highest priority sequence being the one seen on the screen) and toggles the appropriate collision primitive for that sequence.

crouchcoll2

There are still a few lingering issues left to solve here. For instance, I’m not doing any checks to see whether the new collision primitive is fully encompassed by the previous one; if it’s not, we run the risk of intersecting solid geometry and falling out of the world. (Consider the case of standing up after being crouched beneath a low ceiling. In this case, the full-size standing collision primitive would intersect the ceiling, and, in the absence of any proper handling, we could walk or jump through those blocks.) But in the general case, at least, this is working correctly, and I’ve been able to prove that crouching under projectiles will function as intended.

The next major feature I worked on this week is an extension of the palette swapping feature I implemented for sprites a couple months ago. I can now apply this same process to every 8×8 tile of each tileset in order to palettize environments. This is another feature that I contemplated for Super Win but rejected due to time constraints. For Gunmetal, this should be a huge win in terms of coaxing a variety of aesthetics from a smaller number of tilesets.

palettes

On the game side, this works a little differently than sprite recoloring. Each sprite is rendered in a separate draw call, so I can simply set each of the three palette colors as shader parameters prior to drawing. By contrast, each layer of the environment is drawn all at once in a single draw call, which means all the colors for each 8×8 tile must be specified somehow. My solution is to aggregate all the palettes into a texture atlas (shown below in PIX) and then index into this texture with a new texture coordinate channel added to the environment’s vertex buffer.

subpalatlas

In fact, with a little more work, this pattern could be generalized to encompass sprite rendering as well, such that I could draw all the on-screen sprites in a single draw call. This would depend on packing every sprites’ sheet into another texture atlas and maintaining a fixed-size dynamic vertex buffer whose vertices could be allocated to each sprite’s quad, in much the same way I handle  font rendering. Sprite rendering isn’t a bottleneck at the moment, however, so I probably won’t go down this road, but it’s certainly interesting to think about.

I did some minimal work on Saturday to faciliate vertical attacks while airborne, a la the downthrust and jump thrust attacks from Zelda 2 (or the pogo attack from Ducktales, or from Shovel Knight, or whatever). I’m not 100% sure this feature will make the cut — this depends largely on whether it feels necessary once I have more representative combat spaces blocked out — and it will need some further attention to make it feel good, but I wanted to hack it in there in some form, as it’s one of the last immediate player actions I’ve been considering.

vertatt

Finally, I’ve started implementing a HUD. I don’t know for sure yet what information I’ll want here, in part because I haven’t yet decided how some game systems will work (e.g., will secondary weapons consume ammunition?), but since I’ve been working on combat recently, it’s important to at least have health represented in some fashion. I’m toying with the idea of displaying enemy names and health on the HUD, which isn’t necessarily in keeping with the style of the classic games I’m imitating, but I think it could be nice for bosses at the very least. Theoretically, a minimap might be a better use of that space, although depending on the size and topography of the levels, a minimap might not be necessary for this game.

hud_mockup

I’m reaching an exciting point where the individual systems I’ve been working on are starting to coalesce into something that resembles a game. I suspect sometime within the next few weeks, I’ll be able to publish a small demo as a means of collecting feedback on the feel of core mechanics, the first step in a plan to involve players in the development of Gunmetal Arcadia all the way to launch. I’ll be talking more about this plan and a roadmap for the future next week, so stay tuned!