8 Comments

Entity Component System framework, redux

My Windows Phone game Entangled was the first game I shipped that used an Entity/Component/System framework. I used a simplified version of the framework I developed for my much more ambitious still-in-progress game, and it worked fairly well (See previous posts on this topic here and here).

Now that I’m re-skinning and updating the game for a port to iOS (and perhaps Android), I’ve had some more experience with the Entity/Component/System framework. I’ll talk about that in this post, and give some examples of how I attempt to solve some game development problems with it (something that is often missing from other articles).

Brief overview

I’ll just summarize my framework here in a few paragraphs.

The game world consists of Entities. An entity is a class wrapper around some identifiers.

Associated with each entity (via the identifier) is a set of Components that define the entity. Some examples of Components are:

  • Aspect: defines what the entity looks like (texture info, color, etc…)
  • Placement: defines the entity’s location and orientation
  • LaserInteractive: this is something specific to this particular game, and contains information about a core gameplay feature
  • Input: defines whether or not this entity fires click/drag events when interacted with by the mouse or touch
  • Entanglement: another game-specific component. It is used to link one entity’s position or orientation to another
  • FrameAnimation: indicates that the entity should use spritesheet animation, and specifies things like framerate.
  • SoundLoop: indicates a sound that is to be played (and volume, etc..).
  • Scripts: this is a bit of a special one, and describes which custom scripts are associated with the entity.

Note that Components only define data, no logic. They essentially describe “intent”.

Next are the Systems. This is where the logic lies. For instance, the RenderingSystem loops through all entities that have Aspect and Placement Components and prepares the necessary stuff to draw them. It doesn’t need to do anything special to determine which entities these are. It just states declaratively that it’s interested in entities with those two Components, and those entities’ ids are automatically added to a list in the System.

Other examples of systems are as follows:

FrameAnimationSystem: It tracks entities with both Aspect and FrameAnimation components, and adjusts the Aspect Components with the correct location in the spritesheet, based off the current time and the framerate specified in FrameAnimation Component.

 

Spritesheet animation frames

Spritesheet animation frames

 

LaserSystem: This is the most complex system, and manages the main gameplay logic that involves entities with LaserInteractive Components.

EntanglementSystem: This is another game-specific system. This looks at entities with the Entanglement Component, and ensures their positions and/or orientations are kept in sync with the entity’s entangled pair. It also draws the entanglement visuals (it is not only the RenderingSystem that is responsible for drawing).

 

These two objects are entangled. EntanglementSystem manages that, and is actually also responsible for drawing the visuals.

These two objects are entangled. EntanglementSystem manages that, and is actually also responsible for drawing the visuals.

 

Winning System: This monitors certain entities’  state to see if the current level has been won.

So you can see that most systems deal with one particular component, but also require a set of other components in order to do their job.

Some systems operate over a similar set of entities (for instance, the LaserSystem and WinningSystem), but contain completely unrelated logic. So while they could be part of the same system, “one class, one purpose” dictates they should not be.

The good things

The benefits of Entity/Component/System frameworks are well-known, and I won’t go into detail about them here.

But I will say it’s been really good for quickly prototyping gameplay ideas for this game, since it’s easy to assemble new types of entities together from existing Components.

ECS also really encourages writing code exactly once. The temptation to copy-paste code really isn’t there, because it just isn’t necessary. And that reduces the chance of bugs.

The hacks

It’s fair to look at examples where I’ve had to hack things into the framework and ask why.

There is the temptation to throw more data into existing Components. For instance, I eventually realized I needed to ensure that things were drawn in a certain z-order (I’m using alpha-blended sprites, so I’m not using a z-buffer). So I added a Layer property to the Aspect component. Is this the right place? Probably not, it doesn’t have anything to do with the Aspect. It may deserve a new Component altogether. But just for one additional property? (Actually, as I’m writing this, I realize that the Placement Component is probably the right place).

I also wanted to support dual textures and a custom rendering effect for some entities. I ended up hacking some stuff into the Aspect Component for this (if a certain bool is set, use the custom effect). I actually think this is sort of the right thing to do, but the part of the Aspect that defines the sprite needs to include this information. Currently it’s just a texture – I need another more complex abstraction there that the RenderingSystem understands.

Bricks need two textures.

Bricks need two textures.

Another example is that I needed some entities appear to vibrate back and forth, or slide in from behind other objects. However, I didn’t want to change the position of the entity from the point of view of most code (just from the point of view of the renderer). So I added an “AdditionalVisualOffset” and “AdditionVisualOrientation” to the Placement Component. Only the RenderingSystem looks at this.

That was definitely a shortcut/hack. A better solution would probably be to make the entity that has the visual Aspect be the child of the main entity. I support parent-child relationships between entities, so that one entity can be positioned relative to another. The child entity’s position could be adjusted to simulate vibration, without changing the position of its parent entity (which wouldn’t have Aspect). Unfortunately that would break an assumption I have that the main (parent) entity is the one that has the Aspect component. That assumption is needed for logic that sets the entity’s color (on Aspect) based on the type of laser light it emits (on LaserInteractive). I also have code that swaps the sprite used depending on information in LaserInteractive.

Unique logic (scripts)

The bulk of the game logic is contained in the Systems, but I often need more “one-off” logic. Some examples are:

  • The code that scrolls new levels into view gradually, and “introduces” a level by highlighting certain objects, and sliding them into view.
  • I have objects in the game that change the color of other objects that are placed inside them.
  • I have plants that start to grow when you are in a winning state.
  • The name of the level appears gradually, letter by letter, when the level is loaded.
  • Some objects in the game world make a sound as you drag them… the fast you drag, the louder the sound.
  • Animals pop up and offer you hints if you want.

These types of things are too “small and unique” to create new Systems for. So I have a concept of “Scripts” that can be attached to entities. I actually have a Scripts Component which is just a list of scripts that apply to that entity (scripts do get some special treatment though, so making them part of a Component may not be the best solution).

 

A script is used to slide these objects into view and then spawn another script that makes them "flash".

A script is used to slide these objects into view and then spawn another script that makes them “flash”.

 

One special thing about scripts is that there is only one instance of a script, even if it is attached to many entities. One of the original motivations for this was to reduce the heap complexity for my objects (I’m using C#, and garbage collection time increases the more allocated objects you have).

As a result, all state for scripts is attached to a generic property bag that is part of the Scripts Component. Although this limits the kind of information scripts can store, it does have the following benefits:

  • Since all scripts attached to one entity are updated together (in sequence), they are accessing the same region of memory in the property bag (better locality of reference).
  • I don’t need any custom serialization code for a script (i.e. for save games) – all the script state is serialized in the property bag.
  • It will allow me to easily use a separate runtime/interpreted scripting language for logic. I don’t do this for this game, but I do for the other larger game.

Getting/setting variables from a property bag makes the C# code in the scripts a little clunky, but this wouldn’t be an issue if I were using a separate scripting language as I mentioned in the last bullet (since I would then have syntactic sugar that makes interfacing with the underlying property bag look just like normal variables).

 

Clunky way to set property bag variables from C#.To identify variables I use a unique integer id, similar to that described in this post: https://mtnphil.wordpress.com/2012/01/06/identifying-entities-in-the-game-world/

Clunky way to set property bag variables from C#.
To identify variables I use a unique integer id, similar to that described in this post: https://mtnphil.wordpress.com/2012/01/06/identifying-entities-in-the-game-world/

 

I should also mention that the scripts just assume the bare minimum about the entity to which they are attached; so even though I’m calling them “unique code”, they can be applied to any applicable entity. For instance, the “faster you drag an object the louder it gets” script just requires that the entity have Placement and SoundLoop.

One thing I still need to support is the ability to pass parameters to a script when attaching it to an entity.

Messages

Of course, sometimes you need to communicate between systems, or between scripts and systems. I use messages for that. You can send a message “somewhere”, or to a particular entity.

Systems handle messages (they declare which ones they can handle), and Scripts can also handle them if they are sent to a specific entity (in that case, all Scripts attached to an entity get a chance). This is the one case where the entity framework has special knowledge about the Scripts component.

I don’t have any fixed list of message types; I just add them as I need them. They are basically used to glue together bits of code where there is no obvious way to do it. Probably for several of these cases I could refactor things to avoid having to use messages.

 

A script makes plants begin to grow when it detects that you are winning.

A script makes plants begin to grow when it detects that you are winning.

 

Some examples of message use:

  • PlaySound: A script or system can fire a message that says to play a sound. I could probably solve this in another more explicit way (e.g. having access to some audio manager or something), but currently from scripts there is no access to any particular game classes. So they just fire a message and hope someone handles it. In this case, the SoundSystem will handle it.
  • ObtainWinningState: This gets information about if the player is winning the current level (the WinningSystem answers this). I have a script that makes some plants grow, and it uses this to know when and how fast to grow the plants.
  • Click: This is sent by the InputSystem to the entity on which it detected a click. In order for anything interesting to happen, that entity will need to have a script attached to it that handles the click message.
  • RevealTitle: Here’s one that I realized was totally flawed and bogus as I was writing this. When the level is loaded, there should be an entity that represents the title text of the level. It’s initially hidden. A level initialization script runs for a varying amount of time, and then fires this message. It’s picked up by the title text entity, which has a script that begins to reveal the letters one-by-one. The thing is, I don’t have a way to broadcast a message to all entities in the world. So the level initialization script actually needs to know which entity to send it to (it enumerates all entities and searches for those with specific Components that identify it as the title text entity). It follows then, that it could just attach the “reveal letters” script to that entity at that time. This is one example of an area that is not well thought out by me, and this is just some glue to hold it together.
  • UnloadLevel: Another kind of useless one. The game keeps track of the entity that has the “initialize level” script attached to it. When its time to unload the level, it sends this message to that entity. That’s picked up by the “initialize level” script, which then frees all that level’s entities after a predetermined waiting period. I could just as easily have put the unload functionality in another script, and have the game just add that script to the entity. Instead, I used a message, because I needed to pass parameters (how many seconds to wait before unloading), and I have no way to pass parameters to a new script I’m attaching to an entity.

Other bits of functionality

I segregate groups of entities by their owner id (entities can be owned by other entities, or by a “top level group id”). Each level number has a unique “top level group id” associated with it, and all the entities for that level are created with that as their owner. This allows me to have multiple levels loaded at once. This lets me position levels at different spots in the world and pan between them (so two levels exist for a short period of time).

Panning from one level to another.

Panning from one level to another.

I have a way to enumerate all entities owned by a particular owner. So when it’s time for a loaded level to finally go away, I can say “enumerate all entities owned by 1234”, and then “free this list of entities”. Of course this involves scanning the entire entity list, and feels like a heavyweight operation.

Unfortunately a few of the systems care about this too. The WinningSystem is only interested in entities that are associated with the “current level”. And the LaserSystem needs to segregate entities by level so that they don’t interact with each other.

The LaserSystem solves this by the fact that I made a Constraint Component that defines an area that at entity operates within. A bit of a hack, but I suppose it’s reasonable.

The WinningSystem unavoidably (I think) needs to access some global state that indicates the current level entity owner.

Globals Bad

One further thing I can think of. As you travel from the main menu to the levels, I wanted to fade the levels in from a distance. The only reasonable way to do this is to render them to a render target first, and then draw that with varying degrees of opacity.

Of course, I also wanted some of the menu UI to be a part of the ECS framework. It just makes things easier.

But how would the RenderingSystem know that these entities are to be rendered to a different buffer than these other ones? In fact, the RenderingSystem is only part of the entire rendering functionality, and doesn’t manage render targets and such. It just issues draw commands.

The best solution I could think of was to have multiple simultaneous but completely separate entity managers. The level entities operate on their own complete separate “space” than the UI/menu entities.

Unfortunately I had a bunch of global state (or rather singletons) that prevented this. There was a single EntityManager and SystemManager. Global stuff always comes back to bite you. Fortunately, removing global variables is usually fairly straightforward to do, if tedious. So I now have the ability to run two (or more) completely separate entity systems.

Brain dump over.

Advertisements

8 comments on “Entity Component System framework, redux

  1. I’d integrate your “aspect” component into the sprite component. The sprite component should contain everything that is needed to draw the sprite: the texture/sheet to use, the current frame in the sheet, the color, the visual offset transformation, and the layer. Anyway, are components contained in entities or just referenced by entities and contained elsewhere? I mean, does the render system have to go through all entities and look for render components or is the list of render components stored somewhere?

    • There is no sprite component… that is the Aspect component. It has all you mention except for the transformation. I’m hesitant to put transformation in there, as an Entity can exist that is not a sprite, but does have a position.

      I’m preparing another post that goes into detail about how/where I store things (I might have it ready tomorrow). But to summarize for now, the components are stored separately in arrays. One array per component type. So if a system is just interested in one component type, it can just iterate through that array directly.

      Unfortunately most systems are interested in more than one type of component, so they can’t just iterate through one component list. They instead have to go by entity and request the needed components for each entity.

      • Ah, sorry, you are right, I could have sworn I heard you mention a sprite component… I guess my thoughts got mingled in my head with what I was reading. Note to self: don’t comment after 2 a.m!

  2. […] In this post I’ll go over how I’ve laid out the parts of my ECS framework, with some emphasis on issues related to the .Net framework. Check out some previous posts here and here. […]

  3. Blogs like this really help us budding game developers, thanks. I am still a little confused with ECS frameworks and messaging. For example, you have a Input Component, and a Sound Loop component. Conceptually, how does a message relate to the components data? I.e., what is the link between an Input message, and an input component?

    For input, I guess your Input Component is just kind of a marker component that says it is interested in player input? What fields would the input component have in it? What fields would the message have in it?

    For sound effects, I would have thought if you wanted a sound effect played, you would add a sound component to an entity, the sound system would pick that up, and then remove the component once it has been played?

    I am professional developer, but I am new to game development and I am just trying to figure out when and how to use messages as opposed to adding/removing/changing components.

    • I didn’t go into detail on my InputComponent, because it actually isn’t quite properly factored right now. But the gist of it is: it contains just some simple fields that indicate whether it is currently active and if it’s draggable. The InputSystem will scan through all active InputComponents and perform hit-testing on them (thus it also requires a Placement component), and then fire messages like “Click”, “DragStart”, “Drag”, “DragEnd”. Then custom scripts attached to the entities will pick up those messages and do something in response to them. For instance, there is a script that rotates an object by 90 degrees when there is a “Click” message.

    • In regards to sound effects, there are two types: (1) ones that need very specific control, such as looping for a certain time, or changing pitch or volume over time. And (2) one shot sound effects that are “fire and forget”.

      For (2), I just use a message, since we never need to store any state. It’s just “play the boom sound”.

      For (1), we need to store state (current volume, pitch, position) and be able to tell the sound effect to stop at some point. So we need a persistent thing, which is the component.

      You certainly could use a component for (2) if you wanted. And the component would have information in it that indicates this is a “fire and forget” sound, and the SoundSystem would remove the component after telling the audio engine to play the sound effect. That’s not necessarily worse or better than what I do now, just different.

      • Thanks for the insite. I am just having a bit of design indecision about what should be events, and what should be driven by just changing component data/adding components. There were just some grey areas and this had cleared it up for me somewhat. Cheers.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Just another WordPress site

Just another WordPress.com site

The Space Quest Historian

Adventure game blogs, Let's Plays, live streams, and more

Harebrained Schemes

Developer's blog for IceFall Games

kosmonaut's blog

3d GFX and more

Halogenica

Turn up the rez!

bitsquid: development blog

Developer's blog for IceFall Games

Game Development by Sean

Developer's blog for IceFall Games

Lost Garden

Developer's blog for IceFall Games

Memories

Developer's blog for IceFall Games

Casey's Blog

Developer's blog for IceFall Games

Blog

Developer's blog for IceFall Games

Rendering Evolution

Developer's blog for IceFall Games

Simon schreibt.

Developer's blog for IceFall Games

Dev & Techno-phage

Do Computers Dream of Electric Developper?

- Woolfe -

Developer's blog for IceFall Games

Fabio Ferrara

Game Developer

Clone of Duty: Stonehenge

First Person Shooter coming soon to the XBOX 360

Low Tide Productions

Games and other artsy stuff...

BadCorporateLogo

Just another WordPress.com site

Sipty's Writing

Take a look inside the mind of a game developer.

%d bloggers like this: