• Register

The nuclear age brought more than incredible energy, it spawned an era of mad scientists with wicked ambitions. You, being the maddest of scientists, have developed a method for animating and controlling slime to create an army unlike any before. Attack of the Gelatinous Blob is a Real Time Strategy (RTS) game placing the player in the role of the Mad Scientist. Progress through levels, destroying cities while fighting off the army. Build support structures to defend your base and enhance your blobs; blobs that have the unique ability to morph their composition and skills depending on what they absorb throughout the map. With your horde of blobs and arsenal of doomsday weapons, you will conquer the defiant humans!

Post news Report RSS The Entity System

An Entity System (ES) is another way of managing all of your entities in a game, specifically how to organize their attributes (speed, damage, hit points, goopiness, etc...). Attack of the Gelatinous Blob uses an ES and in this post I will go into detail how an ES works and the benefits of using one.

Posted by on

What it Replaces

Commonly games will use a hierarchy of Actor classes, where each subclass adds new attributes to that entity/actor. Picture a top-level parent class called Actor, it has some basic attributes such as health, movementSpeed, and attackDamage. Then there is a subclass of actor called Human, where it adds to the attributes: jumpHeight, carriesWeapon, and carryWeight. Now lets say there is another subclass of Actor called Tank; it adds the attributes: armor, and carriesWeapon. Both the Human and Tank can carry weapons, and thus can both attack.

Next we create a child of Human called Blob. We want to subclass Human so we get the carriesWeapon and carryWeight attributes. But a Blob cannot jump! Ug, ok so we can have each class include a check of canJump(), no biggy. Next we add a 4th actor called ArmoredRobot. It has armor, carries a weapon, and can jump. So it could subclass Tank, but it can also carry weight and jump; should it subclass Human then? Either Human or Tank will work, but we have to copy attributes across to the other class. Not ideal as we now have two places where the jumpheight attribute lives.

The really messy part comes when you are accessing these attributes. Suddenly to see if something can jump we need to know if it is either a Human, or an ArmoredRobot(that subclasses Tank), and then get its jumpHeight. Imagine if there were more types of robots, some that couldn't jump or had some other attributes. Suddenly whenever we want to use these attributes we have to perform lots of checks and object casting and always have to be aware of the entire Actor class tree; when all we care about is if it can jump!

ES Overview

Entities Systems solve this problem elegantly by removing this object hierarchy.
An entity system is broken into 3 parts: The entity, the components, and the systems.

  • An entity is just an ID (AotGB uses integers). Each entity has a unique ID, similar to a primary key in a database table.
  • Components are the actual attributes that an entity can have: hit points, armor value...
  • Systems use the components, and only the components their care about, to do the actual work.

The easiest way to see this is a concrete example:
First we create an entity, it is given ID# 234. That's all it is! Just a number and absolutely nothing else.
Next we give it a component: a HealthComponent. The HealthComponent has two values: maxHP and currentHP.
Lets give it another component: a MovableComponent. It has two values as well: moveSpeed and turnSpeed.
Finally lets give it an AttackComponent. This has attackRange, attackDamage, and attackSpeed.


Okay, now that entity can move, has hit points, and can attack. Time to send it into the real world and cause trouble.

Systems

The parts of the game that interact with Components are called Systems. A game can have many systems; AotGB has around 30 of them.
One such system is the CombatSystem, the most fun of all the systems.
It runs once every frame, checks if an entity can attack, and if so it will perform that attack, deal damage, and check if the other entity died.

The first part in its update loop is to get all of the entities it cares about, specifically entities with a HealthComponent and an AttackComponent. It queries them like this

java code:
List<entityIDs> = es.getAllEntitiesWithComponents(AttackComponent.class, HealthComponent.class);

// β€˜es’ is the entity sys


With this simple query we get all the entities that we care about, and no extra baggage of other attributes, no class casting or instance checks. Super easy, clean, and in one line!
In reality the AttackComponent has a cooldown time, and a counter, so it can track if it is time to attack again. The CombatSystem looks at these values and will attack if the cooldown time has reached zero, like this:

java code:
if (playerAttackComponent.getCoolDownTime <= 0) {

// time to attack the enemy

float enemyHealth = enemyHealthComponent.getCurrentHP() -

playerAttackComponent.getAttackDamage()

if (enemyHealth <= 0) {

enemyHealthComponent.setAlive(false)

// play some sound

// give the player some points

}

}


Not all systems run every frame from the game's main update loop, as the combat system does. Some just run occasionally or are triggered to run. So what is the classification of a system? Anything that uses Components. Usually asking the ES for a bunch of entities with specific components it cares about.

Throw Out That OOP Mentality

The hardest part about ES is throwing Object Oriented Programming out of your head. At least for the entities and Components.
You do not want to subclass a component. For example with the AttackComponent you do not want a PunchComponent that subclasses it. If you do that, then you are back to checking in the systems "if this is a regular AttackComponent, or a punch this time?" You want to have a separate component, called PunchComponent, that does not subclass. You can then have a punch system that deals with it.
This can cause a little bit of duplication of code, but you can use some good design to make sure you aren't duplicating too much.
It takes practice, and I had to rewrite the ES twice because I didn't get OOP out of my head when making the first components.

The Back-End

Storing the components is fairly easy. I use a Hash map, but others use database tables. If you picture it stored in database tables then each Componet type (say, all AttackComponents) are stored in the AttackComponentTable:

Every entity with an AttackComponent has a row in the table. For example entity 98 has a damage of 7, a range of 2, speed of 0.5, and a cooldown time of 5.74
Performing the lookups is very fast because you can index any of the columns you wish, and you only have to query against only those entities with that particular component, not every single entity out there.

How I Have Found it so Far

  • ES makes your game very modular. If there is a bug with combat not working as intended, then it is restricted to only the CombatSystem class. This has saved my skin so many times and I rarely have a multi-hour long debugging session anymore; I always know where to look because of the modularity.
  • -Modability is super easy with components. A mod can just be a system that updates every frame, checks for a HealthComponent and a BlobComponent, and gives those blobs extra hit points every frame, thus regenerating them. None of the other systems have to care about this new system so the risk of breaking things is small. I've also set up AotGB so you can easily add and remove systems. If there is a level where there is no combat (just sneaking around and collecting items instead) I only have to remove or disable the combat system. No side effects; combat just doesn't happen. And all of this with just one line of code!
  • It does take some practice to get used to ES, especially to not think about OOP as you are building the components. It can also be a little too much for a tiny game; if you don't have an existing ES to plug in and use. I wouldn't recommend it to beginners.

So When Should I Use One?

  • If you have a nasty Actor hierarchy.
  • If your combat (for example) code is in many places.
  • if you are doing a lot of class casting or type checking: "is this a jumping orc or a swimming orc".
  • If you are doing networking. (I won't get into this here, but in another post)
  • If you are making an MMO. (I won't talk about this either here)

Conclusion

Well I hope you are a little inspired now to try making an ES or do some more reading. The benefits are many and there are very few downsides, so give it a shot!

And be sure to follow us on Twitter and Facebook for frequent updates and dev topics.

Further reading

T-machine.org
Paulgestwicki.blogspot.ca

Post comment Comments
Backspacer
Backspacer

interesting

Reply Good karma Bad karma+2 votes
LeiterJakab
LeiterJakab

Nice article!

Reply Good karma Bad karma+3 votes
Sploreg Author
Sploreg

Thanks =)

Reply Good karma+3 votes
Guest
Guest

Great article... now I have to rewrite my engine :-)

Reply Good karma Bad karma+1 vote
erlend_sh
erlend_sh

Some additional discussion and possibly helpful blurbs emerged on the jMonkeyEngine website:
Jmonkeyengine.org

Entity systems are practically becoming mainstream around those parts, so there's a lot of experience and second opinions to draw upon.

Reply Good karma Bad karma+3 votes
LeiterJakab
LeiterJakab

The links in the best practices section of the Jmonkey site are great too. Thanks for all that.
I too like this more than the inharitance style of OOP for these tasks. The UnrealScript classes of UDK are the best example hoe that can go wrong.

However I wonder why you say this isn't OO design. My understanding of design patterns is very limited at this point but isn't this basically the Decorator pattern?
I'm reading a book on DP and the writer strongly argues that relying primarily on (I don't know the correct English term) implementation inharitance is actually often against the goals of OOP (modularity and reuseability). So that one should inharitance to give classes the same interface in the first place and use the best pattern for implementing the functionality.

Reply Good karma Bad karma+3 votes
Sploreg Author
Sploreg

The Components aren't, and should not be, OO in design. The systems however absolutely should be.
The components are really just attributes, collections of primitive or maybe complex variables.
The author of your book is correct in saying that using only inheritance in OOP will make things difficult. And using an ES will help steer you in that safer direction.
I made sure to mention "no OOP" for components because all too many times I have seen (in my code and in other's) the occasional component that is subclassing another component. It's just a natural way of reducing duplicated code, but in ES it causes problems. Hopefully it will make people take a step back and think "what? really? I should take a careful look at what I am doing in here." I know it would have saved me time when I was learning about ES =)

A component should be really simple, and something that you can back by a database table or serialize easily over the network. Just be some getters() and setters() with no real operations in them; that's what the systems are for. And in a system you want to limit how many times you check "is this really an AttackComponent, or is it a subclass of it and do I need to handle that slightly differently?"

A decorator pattern needs a bit of work up-front by the class to know that it will be decorated. In ES the entity has no knowledge what any of its components are, it is just a happy little ID floating out there.

Reply Good karma+3 votes
LeiterJakab
LeiterJakab

Thanks! Really cleared up what I got wrong, I will read the rest of the links though. I think I'll use JME3 for a game.

Reply Good karma Bad karma+3 votes
Sploreg Author
Sploreg

JME3 is nice, although I am biased =)
The community is great and always fast to help with any issues that come up.
Glad you liked the article.

Reply Good karma+2 votes
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: