Entity-Component System

Several years ago, after graduating from the Guildhall at SMU, I began writing my own game engine based on the rendering engine I had developed as part of the curriculum. This has been the basis for nearly every indie/hobby game I’ve released since, from Arc Aether Anomalies back in 2008 to Super Win the Game last year, and it’s something that I am constantly growing.

I’ve written about the motivation for rolling my own tech in the past, (notably in the “About” text in You Have to Win the Game), so I’m going to gloss over that today and instead focus on one of the biggest changes I’ve made to my engine over the last couple of years: the inclusion of an entity-component system.

Let’s jump back in time to May 2011. I had recently rolled off the  Duke Nukem Forever team and was looking forward to having some time to start working on a new side project. This became The Understory, an ambitious mix of first-person dungeon crawling, procedural level generation, and Metroidvania item progression. I worked on it for about four months before realizing that (1) the game was ridiculously overscoped, and (2) I had coded myself into a corner and would have to rewrite a lot of stuff if I were to make any more progress. I abandoned The Understory, though some of its ideas would come back in the untitled dungeon crawler I worked on in late 2013 (also abandoned) and now in Gunmetal Arcadia.

A month or two later, I decided to start up a new project, one that would be smaller in scope, and in which I could tackle some of the development problems I had encountered on The Understory. This would become You Have to Win the Game.

Right from the start, I knew I needed a better plan for authoring game entities. In The Understory, I had tried to write a classic OO-style entity hierarchy, with all my entities being derived from an abstract base class and virtual functions everywhere to differentiate things from each other. This failed spectacularly, as I ended up wedging in “bool IsPlayer()” sorts of functions all over the place. I needed a more flexible system, and the entity-component model felt like a good choice.

It took me a while to get a good feel for the scope of a component. Some of my early tests had physical properties (position, velocity, acceleration, etc.) each split out into their own components. In retrospect, this was obviously silly, as these properties are wholly dependent on each other and should all exist in the same component.

In You Have to Win, I had some minimal hooks to allow myself to initialize components from markup in a few very specific cases. This allowed me to define templates for things like bouncing hazards and bullet shooters. I could then instantiate these templates via another markup sheet that would define the entity’s position in a room. I had a rudimentary level editor built into the game with which I could draw maps, but entity placement had to be done through external text files and build scripts. Tuning rooms was a slow, tedious process.

When I started working on Super Win, it quickly became clear that the editor I had developed for YHtWtG would no longer suffice, and in building a new one in C#, I was also able to tackle the problem of placing entities directly within the editor. This also led me to find a more general solution for the problems of saving and loading entity data, culminating in a unified serialization path for all entities.

In Super Win, I began moving in a direction of having all components be entirely defined by XML markup. (In Gunmetal Arcadia, this is now the only way to define a component.) Eventually, the amount of markup text I was having to parse just to start up the game became a bottleneck. To address this, I wrote a new binary file format containing nested key/value pairs and a command line tool to convert XML or INI files to this format. This removed the bottleneck and allowed XML markup to continue to be a feasible option for defining components.

In Gunmetal Arcadia, I’ve begun abstracting away large parts of the XML markup behind a visual editor interface. I had already done a little bit of this in Super Win, specifying sprite sheets and entity positions automatically under the hood, but for this game, I’m trying to completely eliminate the large amounts of markup required to script NPCs and enemies. As I mentioned in last week’s post, one of my eventual goals is to have a visual interface for every component, such that I never have to think about the markup at all, but I’m not yet sure that’s a reasonable goal for Gunmetal.

One of the hurdles I’ve yet to completely overcome is the notion of scripting game logic with markup. I have a pair of related tags, <action> and <query>, which are recognized by a number of different bits of code and which allow me to, in essence, make function calls or branch on conditions in a script-like manner. This served its purpose well on Super Win, and I’ve reused it a few times already in Gunmetal, but the internal syntax for these tags is often inconsistent and lacks any sort of reflection to clarify their usage. In the future, I hope to standardize these in some way, at the very least by enforcing a consistent syntax, but also — if I’m really dreaming big — possibly by going so far as to provide a C-like scripting language that could either be translated into known tags or converted to bytecode and processed directly.

It’s clear that my entity-component system is not finished, but it’s getting better with every game and is becoming a fundamental, defining part of my game engine. I haven’t really talked about the actual code here, but as I’ve gotten accustomed to programming within this environment, I’ve made a number of improvements to facilitate safe, fast, concise access of components and their properties from wherever I need them. I can refer to components or entities by generic handles that abstract away the actual pointers and gracefully deal with problems like objects being deleted while something else is still trying to reference them. I can resolve references to entities or components of a specific type given either an entity or component point or a generic handle. This flexibility has been invaluable for writing the sort of code that entity-component systems naturally encourage, the sort of code that doesn’t care specifically what it’s looking at but knows how to deal with particular types of components which may or may not exist.

Combat and AI

This week, I finally started working on two related systems that have been lingering in the “stuff I really need to do” pile for way too long. The first is combat; although I started prototyping a melee weapon and testing its collision against the environment and other game entities last month, I hadn’t yet completed the loop by funneling these collision results to a health component and reacting to damage correctly. The second is AI; in order to start testing damage against the player, I needed a simple placeholder enemy who could move around and inflict damage on touch.

When I was working on an untitled, unreleased dungeon crawler prior to Super Win the Game, I had written a few components releated to the concepts of health, damage, knockback, etc. Most of these had been sitting unused for over a year and had never really reached a shippable state, so rather than revisit each of them, I chose to rewrite all of them into a single new component that would manage all of these elements at once. Ever since moving to an entity-component system (and perhaps the origins of that system will be my next blog), I’ve been trying to find the right scope for each component. I find that when I break things down into the smallest, most granular level, I end up with a lot of dependencies and communication between components, and that feels bad to me. I like it when components are able to function autonomously. There will always necessarily be some interaction among components, but in this case, nearly every piece of functionality in each of these components was dependent on another one, and it made more sense to unify them into a single component.

So now I have one component (still called a “health component”) which manages the entity’s current and maximum health values, receives damage from external sources, sets stunned and invulnerable states in response to damage, and tests whether incoming damage is valid based on entity allegiance, invulnerability, and so on.

The second feature I worked on this week for AI. I’ve been dragging my feet on this one for a while; along with UI and collision, AI is one of those systems that I’ve historically had difficulties implementing. When I was working on the Win the Game titles, I was able to avoid implementing AI by treating the enemies as “hazards” that would simply bounce back and forth between two walls forever, but for Gunmetal, I’m going to want a variety of enemy behaviors, and I will benefit from making it as easy as possible to author and maintain those behaviors.

For now, I’ve chosen to script AIs with markup in largely the same way I did in Super Win, but with some additional rules for choosing a random action from a weighted set.

ai_behavior_sets

This is a first pass. It’s highly unlikely this is the implementation that will ship in Gunmetal Arcadia, but it’s a start. Looking back at my previous post on animation tools, it’s easy to imagine how I could produce this markup from a visual interface. In fact, I’ve been debating whether to extend the editor’s entity construction tool to encompass all components such that I never have to author any markup by hand. That might be a little too ambitious for this project, but if I continue reusing these systems and this editor for future games, it could be worth the time investment.

Secondary Weapons

This week, I’ve been working on another facet of the core player actions in Gunmetal Arcadia: secondary weapons. These will be an alternative to the standard melee weapon and will encompass a variety of ranged attacks in the vein of Castlevania‘s knife, axe, cross, etc. Only one secondary weapon can be carried at a time, and each will have its own advantages.

In terms of implementation, I was able to leverage the attachment point system I wrote recently to spawn new entities at a certain time during an animation and at a specific location relative to the player sprite. Unlike the melee weapon attachment, however, the secondary weapon sprite is not fixed in place; once it has spawned, it is free to travel through the air and collide with whatever it wants.

One of my goals with secondary weapons is to facilitate a number of different play styles by modulating their behavior with other effects, in much the same way as items can synergize in The Binding of Isaac. So for instance, a basic throwing axe might be upgraded to pass through enemies after it hits them, bounce when it lands on the ground, and inflict fire damage. Or a throwing knife might split into two smaller knives that each home towards enemies. This technology doesn’t exist yet, but as secondary weapons start to come online, I will be building with modular effects in mind.

I haven’t yet determined whether these secondary weapons will be limited by hearts/mana/whatever arbitrary resource as they are in the Castlevania series. I imagine that will come down to testing and tuning, and in the absence of enemies, I’m not there yet.

In addtion to the secondary weapon, the player will always have access to a cache of bombs. Bombs can be placed on the ground or thrown from a distance and will inflict radius damage to both enemies and the player. More importantly, they can also break through certain surfaces.

Destructible terrain is something that I wanted to do in Super Win but didn’t make sense for a number of reasons. In that game, I constructed the visible mesh and collision mesh for each room once upon entry and never allowed them to change. Anything that needed to be dynamic was necessarily built from an entity object, including crumbling platforms and the “ghost blocks” that can be toggled on with the aid of a powerup. With the recent change to make collision dynamically generated only when it is needed, there was one less hurdle. I can now simply alter the underlying tile data, and the collision automatically updates to match. The visible mesh still needs to be updated, however. My expectation was that rebuilding the entire mesh would be too slow, but I figured I would start there and optimize where I could. As it turned out, rebuilding the entire visible mesh is fast enough that it does not produce a noticeable hitch even in a slower debug build. I have optimized it a bit by queueing up all changes to the terrain for a single frame and only rebuilding the mesh once per frame, but even without that optimization, I did not experience any loss of performance. Theoretically, larger rooms might take long enough to rebuild that it would be worth subdividing the visible mesh into smaller portions and only rebuilding the ones that have changed, but I can cross that bridge when I come to it.

Bottle Episode

Today marks my first devlog post of 2015 and roughly three months of development on Gunmetal Arcadia. Feels like a good time for a recap and a look at what’s next.

The earliest seed of the idea for Gunmetal Arcadia that I can find dates all the way back to May 2012, just weeks after I released You Have to Win the Game. I sent myself an email with the subject line “Short-form procedurally generated Zelda 2 dungeon crawler” and some notes on what this might entail. I didn’t pursue the idea at the time, but it stuck with me. It wasn’t the right choice for Super Win, but as I was demoing that game and pondering future projects, it came around again. This time, I described it as “Super Win with combat” and cited Battle of Olympus as another point of reference. I also wrote down “Ghosts of Arcadia” as a potential title. The “ghosts” part of this title was a nod to the idea that previous sessions would influence future sessions, possibly even with previous characters appearing as literal ghosts a la NetHack.

A few months passed. I continued to take notes on this concept and play more NES games for inspiration. Eventually, I decided that I had already exhausted the concepts of ghosts and dreams in Super Win, and I landed on Gunmetal Arcadia as a title that more accurately represented the game I envisioned.

I set up this blog at the very end of September, just before Super Win launched, and I created a repository for Gunmetal a few days later. I was in the process of moving to a new apartment, and I knew I wouldn’t be able to spend all my time working on a new game, but I wanted to hack out some features to immediately distinguish this game from its predecessor. I started with game feel, finding the appropriate weight and sense of movement for the player character.

It was around the middle of November that I had finally settled into the new place and hit the ground running on full-time Gunmetal dev. Some of my first goals were to revisit the toolset I had developed for Super Win and level it up for my Gunmetal needs. I expect this will be a continual thing throughout development, especially as I tackle procedural level generation at some point in the future.

The last month and a half have been a scattershot series of feature adds roughly centered on growing the set of actions available to the player on a moment-to-moment basis. I’ve discussed melee combat and ladder traversal in previous entries; this week, I also added destructible terrain and bombs. Bit by bit, the number of things you can do in my test level is growing, on top of whatever features already existed in Super Win (doors, dialog boxes, collectables, and so on).

There are some notable omissions in the current feature set. Although I have a melee weapon that collides with the environment, I don’t yet have enemies. I don’t have powerups. I don’t have any concept of health bars, damage, dying, or permadeath. I don’t have procedural level generation; that will likely be one of the last features to come online, as I want to be able to prove I can make a single handcrafted level fun before attempting to make random computer-generated levels fun. Three months in, there’s still a lot of things that need to happen before this starts to feel like a game. And that’s probably fine. I don’t have a specific timeline for this project, but my experience with previous games has been that the upfront feature implementation tends to move quickly and goes the furthest towards establishing a quality bar for the game. I don’t want to get into Super Win postmortem territory, but I probably could have spent more time building features for that game, and I want to make sure I give Gunmetal the time it needs.

One last note: I tried streaming some live development last week, and it was pretty fun, so I’ll probably give it another go at some point. I haven’t yet figured out exactly what day or days will work best for that, but you can follow me on Twitch if you’re interested in checking it out.