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