Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


We're also offering banner ads on our site from just $5! 1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Like
26Likes
Dislike

Entities-Parts I: Game Objects

By David Chen | Published Mar 19 2014 08:42 AM in Game Programming
Peer Reviewed by (Madhed, GuardianX, Dave Hunt)

framework entity pattern object component part

Background


I. Game Objects (current)
II. Interactions
III. Serialization

This article is the first in a series of articles about the practical application of the entity-component pattern to game programming. This article, in particular, describes what an entity and a component are, how they interact with eachother, and what the advantages of the entity-component pattern are over some traditional methods of creating gameobjects. Also, it will describe the Entities-Parts framework which is simply a collection of reusable classes and concepts for entity-component interaction.

Since I started implementing the entity-component model several years ago, it has evolved based off of experience using it in my games and studying various articles and other implementations. It has not been tested in large scale games such as RPGs, so by no means is it perfect. The implementation and the articles will keep evolving as my knowledge increases and I develop more games.

As part of the framework, I will also provide an implementation of the entity and component and an example rpg-battle system showing interactions between entities and components. The example will be built on in the next articles. The implementation is provided in Java and C++ w/boost.

It is generic enough that you can reuse your components in multiple games and decouple your game objects, i.e. entities, from high-level game logic and systems. I prefer to use the word 'part' instead of 'component' because it is shorter.

The Problem


Some approaches to creating game objects require you to cram all functionality into one GameObject class, or create several game object classes that have duplicate code. With the entity-component pattern, you can reuse code and make your game objects dynamic by thinking of them as a bunch of interconnected parts.

Deep Inheritance Hierarchy Approach: Let's say you have a Monster class. The class contains a few variables, such as those for health, damage, and position. If you want a new type of Monster that flies, you derive a FlyingMonster class from the Monster class. If you want a spellcasting Monster, you derive a SpellMonster class from the Monster class. The problem arises when you want a spellcasting flying monster. You now need to decide whether you want to derive SpellFlyingMonster from FlyingMonster or SpellMonster. Furthermore, you will need to copy and paste code from the FlyingMonster or SpellMonster class to provide the functionality that the SpellFlyingMonster is missing from its parent class.

Monolithic Class Approach: Another approach is creating a single class that represents and contains all the functionality of your game objects, i.e. the GameObject. While this solves the issue of having duplicate code that exists in deep inheritance hierarchies, this class can quickly get out of hand as it becomes responsible for flying, spells, health, position, etc. Maintaining it will be near impossible and gameobjects will be required to hold data for functionality they don't need. For example, a monster that only flies will still need to keep a collection of spells.

Solution - Entity-Component Approach: With the entity-component pattern, you think of a monster as an entity made up of several parts. You do not need separate classes for Monster, FlyingMonster, SpellMonster, and SpellFlyingMonster. For example, to create a FlyingMonster, you create an entity and add a health part, a damage part, and a flying part. If later on, you want it to cast spells, you add the spell part with one line of code. You can create dozens of monster types by mixing and matching parts.

Explaining the Concept


Note: The approach I present is fundamentally different to ECS (Entity Component Systems). In ECS, components only store the data, but do not provide the functionality. Instead, the functionality is provided by external systems. See http://en.wikipedia.org/wiki/Entity_component_system.

It is natural to categorize gameobjects into monsters, items, and walls. Notice that these objects have similar attributes and functionality. They all need to be drawn, hold a physical space in the world, and collide with the player in some way. The line between different categories of gameobjects tend to blur as the gameobjects become more complex. For example, adding movement and damaging spikes to a wall, basically turns it into an enemy, e.g. Thwomp in Mario.

In the entity-component pattern, game objects are described as entities that contain several attributes and functionality. The components/parts provide an abstraction for the attributes and functionality. Parts can be functional, e.g. control the behavior the entity, or just hold attributes that other systems and parts can reference, e.g. a health stat. The entity is responsible for managing parts as well as their lifetimes, i.e. initializing, updating, and cleaning up parts. There are many benefits of this approach: code reusability, addition/removal of attributes in a game object during runtime, and ease in generating new game object types in complex games such as MMORPGs.

Example: Roleplaying Game (RPG) Battle


It's best to illustrate how the entity-part framework works using the following example game/simulation. The example project is attached to this article. The example is in Java, but the C++ code for the Entity and Part classes is also attached.

The Main class contains the logic to initialize and run our game. It creates a monster and a helpless villager. It then uses a basic game loop and updates the entities. While running the application, the state of the game will be printed to the console by the monster entity such as the monster's health, villager's health, and monster's height as the result from flying.

As you can see, it is very easy to create new types of monsters once you write the code for the parts. For example, we can create a nonflying monster by removing the line of code that attaches the flying part. The MonsterControllerPart is the AI for the monster entity and the target passed into its constructor is the entity that will be attacked. We can make a friendly monster by passing in an enemy Entity into the MonsterControllerPart constructor instead of the helpless villager.

public class Main {

	// main entry to the game application
	public static void main(String[] args) throws InterruptedException
	{
		Entity villager = createVillager();
		Entity monster = createMonster(villager);
		
		// very basic game loop
		while (true) {
			villager.update(1);
			monster.update(1);
			Thread.sleep(1000);
		}
	}
	
	// factory method for creating a monster
	public static Entity createMonster(Entity target) {
		Entity monster = new Entity();
		monster.attach(new StatsPart(100, 2));
		// If we don't want our monster to fly, simply uncomment this line.
		monster.attach(new FlyingPart(20));
		// If we don't want our monster to cast spells, simply uncomment this line.
		monster.attach(new SpellsPart(5));
		monster.attach(new MonsterControllerPart(target));
		monster.initialize();
		return monster;
	}
	
	// factor method for creating an innocent villager
	public static Entity createVillager() {
		Entity villager = new Entity();
		villager.attach(new StatsPart(50, 0));
		villager.initialize();
		return villager;
	}
	
}

MonsterControllerPart code, which serves as the AI and behavior for the monster, includes attacking its target, saying stuff, and attempting to use spells. All of your parts must derive from the Part class. Optionally, parts such as the MonsterControllerPart can override the initialize, cleanup, and update methods to provide additional functionality. These methods are called when its parent entity gets respectively initialized, cleaned up, or updated. Notice that parts can access other parts of its parent entity, e.g., entity.get(StatsPart.class).

public class MonsterControllerPart extends Part {
	
	private Entity target;
	
	public MonsterControllerPart(Entity target) {
		this.target = target;
	}
	
	@Override
	public void initialize() {
		System.out.println("I am alive!");
	}
	
	@Override
	public void cleanup() {
		System.out.println("Nooo I am dead!");
	}
	
	@Override
	public void update(float delta) {
		StatsPart myStatsPart = entity.get(StatsPart.class);
		
		// if target has stats part, damage him
		if (target.has(StatsPart.class)) {
			StatsPart targetStatsPart = target.get(StatsPart.class);
			target.get(StatsPart.class).setHealth(targetStatsPart.getHealth() - myStatsPart.getDamage());
			System.out.println("Whomp!  Target's health is " + targetStatsPart.getHealth());
		}
		
		// if i have spells, heal myself using my spells
		if (entity.has(SpellsPart.class)) {
			entity.get(SpellsPart.class).castHeal();
			System.out.println("Healed myself!  Now my health is " + myStatsPart.getHealth());
		}
	}

}

General-purpose StatsPart keeps track of important RPG stats such as health and damage. This is used by both the Monster and the Villager entity.

public class StatsPart extends Part {
	
	private float health;
	private float damage;
	
	public StatsPart(float health, float damage) {
		this.health = health;
		this.damage = damage;
	}
	
	public float getHealth() {
		return health;
	}
	
	public void setHealth(float health) {
		this.health = health;
	}
	
	public float getDamage() {
		return damage;
	}
	
}

SpellsPart class gives the Monster a healing spell to cast.

public class SpellsPart extends Part {

	private float healRate;
	
	public SpellsPart(float healAmount) {
		this.healRate = healAmount;
	}
	
	public void castHeal() {
		StatsPart statsPart = entity.get(StatsPart.class);
		statsPart.setHealth(statsPart.getHealth() + healRate);
	}
	
}

FlyingPart code allows the Monster to fly to new heights.

public class FlyingPart extends Part {

	private float speed;
	// in more sophisticated games, the height could be used to tell if an entity can be attacked by a grounded opponent.
	private float height = 0;
	
	public FlyingPart(float speed) {
		this.speed = speed;
	}
	
	@Override
	public void update(float delta) {
		height += speed * delta;
		System.out.println("Goin up!  Current height is " + height);
	}
	
}

The Entity-Part Code


The following code blocks are for the Entity class and the Part class. These two classes are the base classes you need for the entity-part framework.

Entity class:
/**
 * Made up of parts that provide functionality and state for the entity.
 * There can only be one of each part type attached.
 * @author David Chen
 *
 */
public class Entity {
	
	private boolean isInitialized = false;
	private boolean isActive = false;
	private Map<Class<? extends Part>, Part> parts = new HashMap<Class<? extends Part>, Part>();
	private List<Part> partsToAdd = new ArrayList<Part>();
	private List<Class<? extends Part>> partsToRemove = new ArrayList<Class<? extends Part>>();
	
	/**
	 * @return If the entity will be updated.
	 */
	public boolean isActive() {
		return isActive;
	}
	
	/**
	 * Sets the entity to be active or inactive.
	 * @param isActive True to make the entity active.  False to make it inactive.
	 */
	public void setActive(boolean isActive) {
		this.isActive = isActive;
	}
	
	/**
	 * @param partClass The class of the part to check.
	 * @return If there is a part of type T attached to the entity.
	 */
	public <T extends Part> boolean has(Class<T> partClass) {
		return parts.containsKey(partClass);
	}
	
	/**
	 * @param partClass The class of the part to get.
	 * @return The part attached to the entity of type T.
	 * @throws IllegalArgumentException If there is no part of type T attached to the entity.
	 */
	@SuppressWarnings("unchecked")
	public <T extends Part> T get(Class<T> partClass) {
		if (!has(partClass)) {
			throw new IllegalArgumentException("Part of type " + partClass.getName() + " could not be found.");
		}
		return (T)parts.get(partClass);
	}
	
	/**
	 * Adds a part.
	 * @param part The part.
	 */
	public void attach(Part part) {
		if (has(part.getClass())) {
			throw new IllegalArgumentException("Part of type " + part.getClass().getName() + " already exists.");
		}
		
		parts.put(part.getClass(), part);
		part.setEntity(this);
		
		if (isInitialized) {
			part.initialize();
		}
	}
	
	/**
	 * If a part of the same type already exists, removes the existing part.  Adds the passed in part.
	 * @param part The part.
	 */
	public void replace(Part part) {
		if (has(part.getClass())) {
			detach(part.getClass());
		}
		
		if (isInitialized) {
			partsToAdd.add(part);
		}
		else {
			attach(part);
		}
	}
	
	/**
	 * Removes a part of type T if it exists.
	 * @param partClass The class of the part to remove.
	 */
	public <T extends Part> void detach(Class<T> partClass) {
		if (has(partClass) && !partsToRemove.contains(partClass)) {
			partsToRemove.add(partClass);
		}
	}
	
	/**
	 * Makes the entity active.  Initializes attached parts.
	 */
	public void initialize() {
		isInitialized = true;
		isActive = true;
		for (Part part : parts.values()) {
			part.initialize();
		}
	}
	
	/**
	 * Makes the entity inactive.  Cleans up attached parts.
	 */
	public void cleanup() {
		isActive = false;
		for (Part part : parts.values()) {
			part.cleanup();
		}
	}
	
	/**
	 * Updates attached parts.  Removes detached parts and adds newly attached parts.
	 * @param delta Time passed since the last update.
	 */
	public void update(float delta) {
		for (Part part : parts.values()) {
			if (part.isActive()) {
				part.update(delta);
			}
		}
		
		while (!partsToRemove.isEmpty()) {
			remove(partsToRemove.remove(0));
		}
		
		while (!partsToAdd.isEmpty()) {
			attach(partsToAdd.remove(0));
		}
	}
	
	private <T extends Part> void remove(Class<T> partClass) {
		if (!has(partClass)) {
			throw new IllegalArgumentException("Part of type " + partClass.getName() + " could not be found.");
		}
		parts.get(partClass).cleanup();
		parts.remove(partClass);
	}
	
}

Part class:
/**
 * Provides partial functionality and state for an entity.
 * @author David Chen
 *
 */
public abstract class Part {

	private boolean isActive = true;
	protected Entity entity;
	
	/**
	 * @return If the part will be updated.
	 */
	public final boolean isActive() {
		return isActive;
	}
	
	/**
	 * @return The entity the part is attached to.
	 */
	public final Entity getEntity() {
		return entity;
	}
	
	/**
	 * Sets the entity the part is attached to.
	 * @param entity The entity.
	 */
	public final void setEntity(Entity entity) {
		this.entity = entity;
	}
	
	/**
	 * Initialization logic.
	 */
	public void initialize() {
	}
	
	/**
	 * Cleanup logic.
	 */
	public void cleanup() {
	}
	
	/**
	 * Update logic.
	 * @param delta Time since last update.
	 */
	public void update(float delta) {
	}
	
}

Conclusion


This concludes the basics of the entity-component pattern and the implementation. Also, you got to see how to use the implementation's API in the example. As you can see, entities and components are powerful tools for creating game objects, but they can't stand on their own without higher level systems.

In the example project, notice that the villager's health went below zero but wasn't removed from the game. Also, the villager Entity had to be passed into the MonsterControllerPart constructor which would be an issue if the monster Entity had to be created first. These issues are outside the scope of this article but will be addressed by the next article, which describes how to implement high-level game logic to manage entity interactions with the help of these classes:

Event manager - Provides a centralized hub for event subscription and publication between systems, entities, and parts. For example, this will allow the villager to tell the entity manager that it has died.

Entity manager - Manages entities by adding newly created ones, removing dead entities, and updating them. For example, this will remove the dead villager from the gameworld.

AI System - Manages AI behavior of an Entity that is dependent on the presence of other entities. For example, this will provide the monster entity knowledge of available targets.

Battle System - Provides the rules of the game, e.g., calculating how much damage is dealt, knowing when an Entity is dead, etc.

The example will be expanded so that instead of simulating a 1 on 1 fight, a team of monsters will go up against a team of villagers. The entities will have varying classes (melee specialist, ranger, flying ranger, summoner, support mage, etc). The next article will deal with entity management and interactions: Interactions.

Article Update Log


5 Apr 2014: Shortened title.
22 Mar 2014: Added C++ w/boost version of code and fixed a memory leak bug in the C++ version.
22 Mar 2014: Changed title from Basics to Game Objects to be more inline with the content of the article
12 Mar 2014: Added C++ version of code
9 Mar 2014: Added additional code samples





About the Author(s)


I am working full time as a Software Engineer and am also pursuing my master's degree in Software Engineering. I have a bachelor's degree in Computer Science. Creating games is my hobby and I hope to make it my career one day.

License


GDOL (Gamedev.net Open License)




Comments

Looks really cool mate, looking forward to the c++ version :)

It is probably worth mentioning in C++ you can avoid the issue with multiple inheritance coupled with virtual inheritance.

Good article.

You seem to have gone the Unity3D route where components are 'owned' by entities. Maybe you could add a short section about or just a shoutout to the alternative implementation using a more relational database inspired model? http://en.wikipedia.org/wiki/Entity_component_system

It is probably worth mentioning in C++ you can avoid the issue with multiple inheritance coupled with virtual inheritance.

While true, I would stay away from multiple inheritance. Entity based frameworks provide a solid architecture through componentization without having to deal with this language specific feature. Multiple inheritance is highly discouraged in general.

Thanks for the feedback guys,

 

I have added the C++ version in the 'article resource'.  I took the C++ code from one of my games and modified it to remove any game specific code and uses of the boost library.  Let me know if there are any bugs with it since I haven't had a chance to test it.

 

Madhed - Yes, the entities own and manage components.  I'll add a couple sentences about the relational database inspired model in the Usage Notes.  I'm not completely sure what the alternate implementation is, but I assume that it's getting entities and parts by integer IDs instead of pointers.

@dave exactly. In the other model, entities are just an id, components contain only data, and 'systems'  contain the logic. It's another solution to the mentioned OOP inheritance dilemma. I like it from a conecptional point of view, but it can be harder to implement and you certainly need some kind of caching/indexing for the entity-component binding. Having the components stored as contiguous blobs opens some opportunities to improve performance though.

 

I only mentioned it because there is some confusion when talking about Entity Component Systems. I came in contact with your approach first and later learned there is another model with the same name.

This *appears* to be an initial part of a multi-part article, but nevertheless could really use a conclusion that demonstrates a more interesting, complete example. You sort of build up towards something but never quite get to anything interesting.

 From "Examining the Concept" section:

 

object-oriented programming requires you to cram all functionality into one GameObject class, or create several game object classes that have duplicate code

 

 

 

First, that "Examining.." section starts out with "OO isn't the concept." See below for suggestions to improve.

 

A concept is an abstract idea, not details of a specific implementation. It would greatly enhance your article if you can provide a good explanation for your concept of an entity-component model.

 

If you provide a good idea of your concept, then you can have a section titled "Examining the Concept." You have a lot of information detailing how the concept can be implemented, but it's out of the context of what you're implementing.

 

I would suggest you change the emphasis of the article from "OO is bad! Entity-component is good!" to something a bit more toned-down, perhaps along the line of "An entity-component approach to object construction provides the ability to.." That expands on the concept a bit better, IMHO.

 

As currently written, your article implies that the entity-component model can't stand on its own, but should be considered only as an alternative to object-oriented programming.

 

If you really want the emphasis of the article to be "entity-component versus OO," change the title and, after a more moderated introduction, you can down-play the emphasis on "Good vs. Evil" by briefly discussing a feature of the entity-component model, continuing with "This feature avoids possible complications when using an OO approach.."

 

 

make your game objects more dynamic

 

 

In the statement above, you use the word "more," still in the mode of comparison with OO. Why not just "make your game objects dynamic" and discuss ways to do that?

 

You may even want to scrap all the OO discussion, and provide a discussion at the end of the article comparing your model to other methods of providing attributes to game objects, and the particular advantages and limitations of your approach.

 

You've obviously put a lot of effort into the code, providing examples in a couple languages. Good job!

 

EDIT: One of the features I find useful using an entity-component model: the emphasis is on run-time vs. compile-time decisions. I think that's part of what you imply by using the word "dynamic," but it could be emphasized more - if you want.

Its a good article and describes an interesting architectural design.

A lot of entity/component system try to solve the problems that are created from improper use of inheritance. Take a look at this wiki for 'Composition over inheritance' (http://en.wikipedia.org/wiki/Composition_over_inheritance). It should give you a good idea of why many people prefer entity/component systems.

 

I would also recommend watching videos from Robert C. Martin. He explains a lot of good design concepts when it comes to code architecture (https://www.youtube.com/watch?v=WpkDN78P884).

 

Edit - I didn't notice it, but madhed already mentioned the relation of entity/components and the inheritance issue. Nonetheless, check out the links its good info.

 

A side note - There are component systems that are OOP and some procedural (data-driven). OOP competes with procedural. 

 

"Procedural code (code using data structures) makes it easy to add new functions without changing the existing data structures. OO code, on the other hand, makes it easy to add new classes without changing existing functions. " - Clean Code: A Handbook of Agile Software Craftsmanship

 

So if you do decide to use one over there other than It's a good idea to stick to that method and not mix the two. (http://en.wikipedia.org/wiki/BaseBean). To that point, I really like how you made all of your variables private :)

 

Good feedback...

 

This is a part of a series of articles.  This first article describes the implementation of the entity and component class I use which I tried to make as simple and flexible as possible, and serves as a practical introduction to anyone who is new to the entity-component pattern.

 

I also moved the sidenote about the more datadriven ECS approach in the "Example" section.

 

Josh -  I fleshed out the conclusion some more and made it more clear this is a part of an article series.  I agree the example is very basic, but I think it describes the base functionality of the entity and part class.  I will elaborate on the example in the next article when I talk about entity management and interaction, which would be too much to put in this article.

 

Buckeye - You're right.  The comparison shouldn't be in the "Explaining the Concept" section and should be more about the entity-component pattern.  I created a new section called "The Problem" which talks about traditional approaches to gameobject creation and renamed some things so it wouldn't seem like an attack on OO which I didn't intend it to be.  I added some things in "Explaining the Concept" that detail the entity-component pattern specifically.

Good rewrite! Toning it down a bit makes a lot of difference (to me, in any case). Will be back to give it another read-through.

 

Nice job overall.

Great job .When is the sequel coming out ? :)

Sanka - Glad you like the article smile.png.  I can't say for sure when it will be out because I'm busy with work and school, but I hope by the end of this month.  If not, I'll add more content to the example.

It's a great article, you presented the basic very well without getting caught up on tangents.  When do you plan on posting the next article?

SpiderJack - Thanks SpiderJack!  Like I said, I don't have a fixed timeframe for the next article because I'm carefully planning out what I want to talk about next.  I did make a lot of progress on the example code today so things are moving along smoothly.  I'm shooting to complete it by the end of the month.

It is really a great article. The Idea that components are owned by entities, awesome. For me completly new approach to use entities.

 

I'm looking forward for your next article :D

I have a small question. In class Entity you use a HashMap to store the Parts. If I create a Part responsible for drawing the Entity, as I do so that part is always executed last? In my view, I think this can generate some inconsistencies or bugs when, for example, a DrawingPart is "updated" before any PhysicsPart.

 

Anyway, I liked your explanation of the subject and the fact of having something concrete (codes, codes everywhere :D).

Sorry for the late response.  I haven't dealt with a situation like that where I would need parts to be updated in a certain order.  In that case, you can probably modify the Entity class to use SortedMap instead of HashMap.  Another solution is to add another method to your physics part called preUpdate(), which can be called before the main update stage.


Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS