Advertisement

Implementing a component-based entity system?

Started by September 01, 2013 03:13 AM
9 comments, last by stein102 11 years, 5 months ago

I've been trying to implement a component-based entity system for my game items. I'm not sure if I'm doing it correctly and if there's a better way. I'll post some code, can you guys help me improve my current design?

ItemCreator Class:


import java.io.IOException;
import java.util.ArrayList;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.XmlReader;
import com.badlogic.gdx.utils.XmlReader.Element;

public class ItemCreator {

	XmlReader reader;
	Element root;
	int i = 0;
	ArrayList<Item> items;
	
	public ItemCreator(){
		items = new ArrayList<Item>();
		reader = new XmlReader();
		try {
			root = reader.parse(Gdx.files.internal("items/Items.xml"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		Array<Element> items = root.getChildrenByName("item");
		
		//Loop through each "Item"
		for (Element child : items){
			int ID = Integer.parseInt(child.getAttribute("id"));
			String name = child.getAttribute("name");
			
			//Loop through each Item's components
			int numberOfChildren = child.getChildCount();
			Array<Element> components = new Array<Element>();
			for(int i = 0; i < numberOfChildren; i++){
				components.add(child.getChild(i));
			}
		createItem(components,ID,name);
		}
	
	}
	
	public void createItem(Array<Element> components, int ID, String name){
		Item item = new Item();
		item.setID(ID);
		item.setName(name);
		
		//add components
		for(Element component : components){
			if(component.getName().equals("description")){item.addComponent(new ItemDescription(component.getAttribute("text")));}
			if(component.getName().equals("wieldable")){item.addComponent(new ItemWieldable(component.getAttribute("slot")));}
			if(component.getName().equals("weapon")){item.addComponent(new ItemWeapon(component.getAttribute("damage")));}
		}
	
	
	
		//Add item to itemList
		items.add(item.ID,item);
	}
	
}

Item Class:


import java.util.ArrayList;

import com.badlogic.gdx.Gdx;

public class Item {

	ArrayList<ItemComponent> components;
	String name;
	int ID;
	
	public Item() {
		components = new ArrayList<ItemComponent>();
	}
	
	public void addComponent(ItemComponent component){
		components.add(component);
	}
	
	public void printItem(){
		Gdx.app.log("ID",String.valueOf(ID));
		Gdx.app.log("Name", name);
		
		for(ItemComponent c : components){
			Gdx.app.log("Component",c.toString());
		}
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getID() {
		return ID;
	}

	public void setID(int iD) {
		ID = iD;
	}
}

Thanks for any suggestions.

Java! I like Java so I'd like to chime in a bit :)
This depends on component design but normally components are likely to be more complex than just having a single attribute. So what I like to do is implement that for-each loop using a command pattern instead. The various commands encapsulate the logic to go from component definition to component instance are backed by component caches (which in turn are backed by factories) to help with reusing and/or instantiating and/or loading components (I have some components which are fully scripted in Lua so they need to be loaded from disk and compiled once, for instance). You can also use the command to initialize the component, for example by having it perform some one-time calculations or register event listeners with the parent entity or some global event bus.
Generally you should consider a number of things in your design (and there are no correct answers, each design has some tradeoffs):
1) Logic - where does the behavior logic for the components reside? Inside components? In other systems? How is logic updated? How do you deal with intermediate states during the update pass?
2) Communication - How do components or systems communicate with each other within the same entity? With other entities? Method calls vs vs registering handlers for specific events?
Using external systems are popular (search forum here for "outboard components" or some such), in such a system a component lives and is updated in a separate system and the entity is just an id. This should be good for performance since you are executing the same code many times in a row instead of vastly different code per entity, though I have no data on Java specifically in this regard. It's also more consistent since the update order is fixed. Personally I prefer a mixed approach, where some logic is stored in components owned by an entity and some is stored in lower level objects in other systems, updated separately.
Advertisement

when i was working on a generic "entities list", i did a little research on C-E systems. here's what i found out:

there are 3 reasons to go C-E:

1. non-coders defining new entity types. if you don't have this situation, you don't need it.

2. as an alternative to the "entity class hierarchy from hell". if you don't have this problem, you don't need it. if you do have this problem - C-E is not the only solution available.

3. as a last ditch optimization, once render can go no faster, and you must look to update for any additional speed gains. if you don't have this problem you don't need it. i only saw one case where it was done for this reason. the implementation is very specific, all data oriented design, keeping cache considerations at the forefront. its NOT the implementation you typically see for C-E. unless your existing code is obtuse as hell, performance returns will be marginal at best.

if you do go C-E, and want to do it in an OO way, your objects become lists of components: one list for each type, and a list of entities, or perhaps one list per entity type. implementing individual components and entities as stand alone objects is probably over-abstraction.

then what goes where becomes pretty straightforward. you have an object (a list) for each type of component, with get, set, and update methods. note that a different update method may be required for each "flight model" supported by the engine. then you have a higher level hunk of code that uses the get, set, and update api's of the component lists to get things done. the entity is just a bunch of component ID numbers.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

I did some research into how to best do an Entity Component System recently and imo putting a list of components into each entity is like being stuck halfway in the transition from the deep hierarchy to an ECS and having problems repeatedly finding out which components are inside each entity, although people have made it work.

Better is to go straight to a structure of arrays approach. Have some system generate a unique id which represents a new entity and remembers all entity ids currently in use.

Then have a system per component type which contains a table(array) of all components of the single corresponding component type. Each Component contains the id which represents the entity its associated to.

When some work is to do preferably have each system iterate over the whole (possibly sorted by id) component array it contains. That avoids slowing everything down by needlessly going through all Entity to ask each if they contain the component you are after at that moment even though probably most dont have it and having to do traverse an opaque calltree with one or more virtual calls per Entity.

There is some nice article series explaining ECS, some later parts of it are about how it connects with DBS in MMO, but if you blend out those parts I think it applies to all kinds of games: http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/

Kind of tiring to read online book, but with some interesting information inside: http://www.dataorienteddesign.com/dodmain/


Each Component contains the id which represents the entity its associated to.

i suspect the linkage should go the other way. instead of a component having an entity ID, entities should have component ID's.

in some cases, doubly linked might be required, entities have component ID's, and each component in turn also has an entity ID #.

but its been my experience that the linkage is (or can managed by) singly linked systems starting at the top with the entity, and linking down to lower and lower levels of subsystems:

entity -> model -> meshes, textures, materials

an entity consists of components, one of which is a model.

a model, in turn, also consists of components, such as meshes, textures, and materials.

many examples of this type of data hierarchy / relation can be found in games.

this is whats made me come to describe games as:

"modeling and simulation software that uses one or more relational database systems."

the ironic thing is that my worst dread in switching from aerospace to software was i'd spend my life as a database programmer. now i'm a game developer, and find that almost HALF of the type of application that i WRITE FOR A LIVING (IE: games), is relational databases!

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Well, in the links I put in its explained that you actually dont need to have any entity. The point is data thats needed by a system at one time is kept inside one type of component(and possibly one(or very few) other for data thats needed by more than one system) and then it just works on all components of the same type at one time and in most cases dont need to know to which entity each component belongs.

Then its not:

for all entity -> update() -> check all components for their type and do a tree of update() for all kinds of work (and trash the cache+stall often)

and not:

for all kinds of work to do -> repeatedly for the huge unsorted list of all entitys -> ask/check if it contains the right component and only then do something

but:

system1 -> for all components in the system(where you already know they are a single type) -> do the single kind of work

-> maybe same for next step or kind of work

system2 -> do same

...

That way if there is only few entity having some component type inside you only go through the short list and dont test the whole long list every time. Also it should help to not go through so many pointer indirections and type checks but just have a nice continuous array without irrelevant data interleaved inside or having to load bits and pieces from lists which are distributed all over the memory.

The id numbers are then (if the data is partitioned in a good way into component types) hopefully only needed for special cases like deleting an entity that could cause some (binary if they are sorted) searches or simultaneously going over 2 component arrays, where I plan on having them sorted by id and only checking them for skipping parts of the secondary component array thats shared with another system. But there is always the possibility for later adding some index, filtering or joining data if its repeatedly accessed in more than one way.

I'm not very far into implementing it, but I got convinced its a good thing and therefore I will do it that way.

Advertisement


Well, in the links I put in its explained that you actually dont need to have any entity. The point is data thats needed by a system at one time is kept inside one type of component(and possibly one(or very few) other for data thats needed by more than one system) and then it just works on all components of the same type at one time and in most cases dont need to know to which entity each component belongs.

yes, exactly. you don't iterate through the list of entities, you iterate though each list of components. as you say, components need know nothing about entities. that's why i said the link from component to entity was backwards. i suppose you might be able to do away with the entities list altogether and put entity IDs in the components, but then you'd have to do a search to find the component for an entity, instead of a lookup. i'd have to think about it. surely at some point in a game you have an entity and want to know one of its components. for that you need a component ID for lookup, or must search a component list. i just include the component ID automatically, but it may be unnecessary.

i can check it out in my architecture testbed. its designed to let you play around with different game architectures to discover patterns. it will quickly reveal if its needed, and under what circumstances.

that would be pretty cool if the entities list could go away. but something tells me it can't without switching from lookup to search.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Communication between parts of the same entity becomes more complex in this kind of system, and debugging is also harder when you have no entity to set a breakpoint for inspection on. It's in line with current data-driven design though and is good on the cache, since you can lay out all components of one type adjacent in memory and use the same code to call them.

For most projects people here at GameDev.net work on, it won't make a difference however, so it makes sense to go with whatever system one finds most intuitive (and there is always refactoring).

Do I want to make a list of "base items" in game where I define all my base items and just use that to reference all future in game items? Or should I just leave it all in my XML file and only look into it when I need to create a new item? Say I need to build a new "Short sword", which might have an item ID of 4. Would I just look that up in a List I have stored in the memory, or should I go back and read the XML file to find an item with a n ID of 4 and create a new entity with those properties?

You don't want to go to disk every time you need a new short sword. File I/O is one of the slowest operations you can do. Your game will eventually stutter if yo do this too often.

There are many options, most involve some variation of:

  1. cloning (but Java cloning is mostly broken, unfortunately),
  2. copy constructors (hard to get right without using reflection which is slow),
  3. copy methods (just make a new instance using the same state, then re-initialize all non-common state),
  4. or "template" classes which hold intermediate state (for instance, an EntityTemplate might have a list of component names to instantiate and a list of properties to use for instantiating them).

Keeping your class hierarchy as flat as possible will help a bit with this, and is generally a good idea in any case.

This topic is closed to new replies.

Advertisement