Component-based Entities and how they communicate

A forum to store posts deemed exceptionally wise and useful
bull
Posts: 36
Joined: Wed Sep 12, 2007 8:49 am
Location: s1Ng4p0R3

Post by bull »

Nice article.
I'm doing something similar for my project. I'll refer to your code sometimes.
I have some suggestions:

-Currently, all of your signals are "global" (through the action manager singleton) . IMO it's inefficient. You should consider making some of them "local". For example: the death of a normal monster only drop some items and raise exp. It is handled by only the monster entity, it should be local, not many objects will bother about it. Your signal handler will be full of switch case or if else to handle only relevant signal. However, the death of a boss can trigger a lot of thing: change of level, advance of story(which involve npc movement, script, text dialogues...). A lot of game components will get involved in this signal so it should be global. You can make your ActionManager non-singleton and let each entity have one of it(local action manager). The GameManager can have one instance to be a global action manager. If you think is somewhat memory inefficient, try implementing a simple scoping system like emitMessage(receiver,data) and emitGlobalMessage(data) .

-Try not to use too many singleton. Believe me, it's not good practice.
My name is bull, for we are many ...
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Post by sudi »

hey trefall is ur game still in development?
would be interesting to hear how the component system helps in veloping the actual game. I'm still woring on my framework so no finished game yet on my side.
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
Trefall
Posts: 45
Joined: Tue Dec 05, 2006 8:49 pm

Post by Trefall »

Bull:
Thanks for your input!

I've been dwelling a lot on the way inter-entity components communicate with each other through the ActionManager myself, and I will probably add a more instanciated message system that the Entity and the GameManager can instanciate, so that Entity-local messages are not handled through a lookup of all entityIDs in the game every time. So yes, we might very well switch to a non-singleton message system, where the global instance runs through the GameManager.

I've been thinking about reducing the number of singletons in this project for quite some time, and I might just move all current Managers (my singleton classes) over to the GameManager and just have a single singleton as global accesspoint for anything that needs to be globally accessible. The only problem I have with this, is that it would create dependencies for all managers whereever the GameManager is included, but then again, the manager classes are not improved or coded on all that often anyway...

Sudi:
Hey! Yeah, the game is still in development, though, as yourself, we are running towards a framework milestone right now and focusing our efforts on that. We finished a very small demo a month ago, but that was quite hacked together due to missing pieces in the framework. We're real close though to reach that framework milestone now, and following that goal, we'll create a demo again, to test the capabilities of the messaging and the structure overall.

After our first demo, it became evident that we needed to do some improvements. Like you described for your project, we really needed to code our entity management more securely. So I've went with a much more robust factory approach for entities now.

Basically, we have an EntityType that you define through the EntityManager with an EntityTypeID string. Then you can add components to this EntityType. Then, when you want an Entity of this EntityType in your game, you just instanciate this entityType, or copies it, which is also happening through a secure call to EntityManager using the EntityTypeID as parameter. The EntityManager only expose const references to the user, while all pointers and new operations are handled behind closed doors.

What we will do, is to define all EntityTypes via XML. We have all this in place now, there are just a couple improvements that to the code that needs to take place before we're finished with this part of the framework.

Another thing we decided on, was that we really wanted to be able to define all our components in LUA. I've integrated LUA into our framework and done some tests, but there went some work into defining how we'd go about the C++ relationship to the Component base class, and any component we define on top of that in LUA. Thankfully, we're using LUA and C++ at my workplace, where we've integrated it in a very similar way to our needs on this framework project, so I've found out how I want to do this now.

On the topic of Components and their functionality, we also found that a lot of functionality would fit very well into Finite State Machines. So we're also working in a flexible FSM system into the mix, where any Component can take in any number of FSMs to chunck up logic and seperate it logically, which really works well for a lot of our functionality.

For our GameStates, we'll do something similar to what we're doing with Components, that is, have the base class of the GameState, that the GameManager can use to switch between game levels and menues, and then build GameStates on top of that base class in LUA. The description of menues and levels might need some more structure in addition to scripts though... so maybe each GameState will need an XML document as well, at least for the parts of the menues and levels that are predefined (we plan to use a lot of procedural generated content, in inspiration from Dwarf Fortress).

Then I have to add a simple audio node and component to the framework as well, but I've worked quite extensively with OpenAL at my workplace, so this should be a quick implementation.

When these couple of things are sorted, we'll build our second demo on the framework with the aim to not touch a single line of C++ code. So the goal is to build the demo entierly in LUA and XML.

Since you're still working on your framework, have you come across any hurdles that have lead to this long framework development? Last I heard, you had already components exposed for LUA development and had a pretty safe entity system, what are you working on now?
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Post by sudi »

Good to hear that u still work on the game...anyways i guess u got me wrong with the scripting. I didn't implement LUA its a selfmade scriptengine made by someone else. its actually a c-style language. and instead of the hardcoded function of the component these functions are called. its not the best approc and i will probably exchange this with some other scriptlanguage...actually tried to integrate python already but didn't really work on it.
Now i'm as far as you i can define entitytypes via xml files and simply load them by calling createEntity(NAME).....
another thing i added are attributes to the entity discriptions which can be set in the leveleditor and are loaded into the components. unfortunatly this isnot really perfect bc right now the attributes are simply past to all components attached to the entity which is not the best way of doing it. Oh and the leveleditor right now is IrrEdit with a plugin which loads the entity discribtion files and lets u set the attributes.
And actually right now everything i wanted to have in the framework is in it. A working component based entitysystem, network , physic, and a really simple srcipt engine to maybe trigger game events.
All the stuff i'm still missing will be discovered when i programm the game.

btw we are both working on the same kind of game...which is actually pretty funny
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
Trefall
Posts: 45
Joined: Tue Dec 05, 2006 8:49 pm

Post by Trefall »

If you are interested in C style scripting, I've heard quite a lot of praise about Squirrel, though I really like LUA. It's very, very simple to use, and widely used as well. If you use something like ToLua++ on top of it, it's almost too easy to expose C++ stuff to LUA.

What exactly are those attributes you mentioned? Are they unique per entity instance or per entityType? I only ask, because in my mind, defining variables for the components via xml should be enough. Which leads me to the next question. Do you define variables for your components in the xml files when defining entityTypes, or do you set up the variables for components in a different way?

It's cool that you integrated a plugin with IrrEdit though! We'll be creating some simpler tools in the feature. Mainly one tool for defining the entityType xmls, with a 3d view of the mesh that you define, etc. Just to speed up the process of creating and tweaking entityTypes, and to make it accessible for artists and designers in the future.

Then we'll probably also need a shoehorned tool for setting up the parameters for some randomly generated world. Right now, we only generate a pretty simple dungeon, but in the future I hope we'll be able to expand this to terrain rendering and whatnot.

I have played with the thought of combining predefined and randomly generated level content, and I have a 2d map editor (basically just reading characters from a file where each character means something, like a wall tile, a floor tile, etc, which could easily be combined with some random generated parts as well. This simple map layout is enough for our purposes right now though.

I'm glad to hear you've reached your milestone on the framework. From the sound of it, you've come a little further than we have. I haven't yet implemented any networking, and won't either for some time. Neither is our physics integration complete yet (no static bodies are supported yet, which is needed for level geometry).

I also expect to enter into a new iteration of framework programming after the demo2 milestone has been reached. I mean, it's only natural that through using the framework you find bugs and weaknesses, and maybe get ideas for improvement. But I think that's the fun of it as well, constantly improving. At least as long as the development is divided into iterations where you get to do framework improvements for some time, then switch to build a demo/game on the framework, then switch to the framework to add new features / improve, then build a new demo testing new features and bug testing, etc. This model just keeps me motivated all the time, works great for me. Then a larger team can be assembled at a later point, when we feel the framework is good enough to take on a full fledged game.

I added you to my ICQ btw, so maybe I'll catch you online there some time. I find it interesting that we've been working on such similar frameworks and can share information like this.!
Trefall
Posts: 45
Joined: Tue Dec 05, 2006 8:49 pm

Post by Trefall »

Just a couple of followup thoughts on this topic which I wrote in reply to a PM I received:

1) It's wise to reduce the use of singletons to a minimum. I wouldn't use more than one singleton in my projects no more really, the GameManager, and then rather do protected constructors on the other Managers and befriend GameManager to allow only him to create instances of those managers. Normaly you'd want one instance of each in the GameManager, that you could then access anywhere in code via the GameManager.

2) Going with a complete component abstraction is very difficult. When going that route, I spent more time figuring out how to isolate functionality than I spent implementing it. But that is because I went for a complete abstraction of functionality. I'd rather use a deep hierarchy approach for common functionality that you will see a lot of entities use, and then do components on the functionality that will have a more pluggable/modular use. There is little point putting the renderer in a component, because every entity will use it. The same goes for physics, sound, etc. Things that should be abstracted into components are typically game mechanic specific features, like the health of a character, finite state machines, magical abilities, player input controllers, ai controllers, etc.

3) Going with singslot for eventhandling is cool, since you get away from the many to many relationship requirements, but it's very difficult to debug. Generally I learned that if I had to pass a lot of events in a component, that component hadn't been designed well enough, or the logic had been wrongly seperated. Later I found that if I just divided the components correctly, it was actually a lot easier to add smaller dependencies between components, like a finite state machine looking at the entity's health component to make decisions. Of course, such dependencies should be checked for NULL to survive and go a different route if the health component doesn't exist, or, at least, tell the developer that "hey, you forgot to give me health!" Razz So, being able to go call owner_entity->getComponentRef(ComponentType("Health")), or some such method, in a different component, is a lot easier to work with than doing events. I mean, if a component really needs, say, the health component to perform it's logic, then there's no reason why that component should work if you didn't attach a health component to the entity.

4) Keep the foundation of each component type in C++, but do the actual logic in scripting. I kind of tried the two extremes in this respect, but ended up finding out that going the middle route would have served me best. If I had only made the data variables and function declarations in C++, but then called corresponding lua functions to handle the logic of those functions, it would've been a lot easier to expand and tweak functionality while having a secure, easy to debug, c++ backbone. I love the advantages script integration gives me, but it's important to have a clear definition of what goes into scripts and what goes into hardcode. I've ended up that scripts that are pure functional work out really well when bound with a C++ backbone, because I can then change logic and reload scripts at runtime to tweak functionality without breaking anything.

5) Make a real distinction between a component and a property. I haven't really solved this yet, but how do you set properties on an entity that will automatically be given to the correct components, if they belong to a component? In my first approach, I tried entity table modification in lua via the xml settings. What would've been easier, was if components defined their properties, but gave them to their ownerentity with a reference to themself maybe. So that the MaxHealth property would become available to the entity, but truly point to the Health component. This type of functionality might not be immediately obvious if you'll be working on the xml level, but if you're going to make a plugin for the system into the irrEditor or make some other kind of editor, you really want to look at properties at the entity level. So, in an editor, you could first define which components an entity is made up of, and with each component you add to the entity, the property list for that entity would get more properties. Like I said earlier, node specific stuff, like the position/scale, etc would be properties available to all entities. So only the component specific properties should be modular like that.

6) Factories are cool. If you can store entity templates based off of xml or irr definitions (from "creating" or "designing" a new entity, and then instanciate those, that would be a lot easier to use in development code as well as a lot more efficient than reading xml definitions each time you want to put another object into your scene.

7) If you feel that you spend too much time figuring out what to put where, then chances are you have divided things wrong already. Chances are that you have a ton of relationships between components and it's come to the point where it's impossible to manage. The component system really shines the most when you have a clean isolation, or close to clean isolation between components, but this can be difficult. It's also error-prone if you have relationships, but components are still freely plugable. It's therefor adviced to create some kind of relationship map, and then use that in your editor to explain to the user that if he adds component B, he also need component A and C, because they rely on each other. It might be that you end up with certain "Base" components in your approach, if so, try to identify these early and make sure your relationship map pays more attention to those. So that if component B and C depends on component A, only A will have to be added if component B or C is added. So, if you want a safe-to-use component system in your game, you really have to create a tool that utilize it with some forced, guided safety routines.

There are also a lot of interesting posts on this topic over at GameDev.Net's Game Programming forum.
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Post by sudi »

actually i didn't run into these problems with components depending on each other.
An Example:
I have the following components:

Code: Select all

Graphic : this handels a scenenode
-Bound Variables:
                         string Mesh;
                         vector3d Position;
                         vector3d Rotation;
                         vector3d Scale;
-Bound Function:
                         setPosition(vector3d pos);
                         changePosition(vector3d pos);
                         setRotation(vector3d rot);
                         changeRotation(vector3d rot);
                         setMesh(string Mesh);

Physik : this handels a physics body
-Bound Variables:
                         string Mesh;
                         int ShapeType; //box, ball, triangle, .......
                         float Mass;
                         vector3d Position;
                         vector3d Rotation;
                         vector3d Speed;
                         vector3d UserForce;
-Bound Functions:
                         setMesh(string Mesh);
                         setShapeType(int type);
                         setMass(float Mass);
                         setPosition(vector3d pos);
                         setRotation(vector3d rot);
                         setSpeed(vector3d speed);
                         setUserForce(vector3d force);
ok actually i wanted to make a full example including player, npc and a simple prefab but this takes way to much time....anyway this still works.
ok following pseudocode:

Code: Select all

CompEntity box = Manager->createEntity(); //this creates a empty components collection
Manager->setComponent(box, Manager->createComponent("Graphic"));
Manager->setComponent(box, Manager->createComponent("Physik"));

//ok now we have initialized entity with a empty graphics and physics component
//lets give them some shape
box->FireEvent("setMass", 60.f);
box->FireEvent("setShape", BOX_SHAPE);
box->FireEvent("setMesh", "models/box.3ds");
box->FireEvent("setPosition", vector3df(0, 100,0));
//the rest is done by the components itself. we have a box falling down from position (0,100,0)
Now u could add as many components u want and everything would work right. U could also simply take out the physics component u would have a still box at position (0,100,0) or u could leave out the graphics component maybe for server use and still have the same code. Oh btw i have these Bound variables bc in my file loaing algo i do the following:

Code: Select all

CompEntity CManager::createEntityFromFile(string filename)
{
       CompEntity entity = createEntity();
       ....
       ....
       ....
       IComponent* comp = createComponent(reader->getNodeName());
       for(int i=0;i<reader->getNodeVarCount();i++)       
              comp->setValue(reader->getNodeVarName(i), reader->getNodeVar(i));
       setComponent(entity, comp);
       ....
       ....
       ....
       return entity;
}
This is pseudo code!!!
Doing that i don't have to call the FireEvent for the initial values.
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
Adversus
Posts: 128
Joined: Sun Oct 05, 2008 10:58 pm
Contact:

For those that can't get sigslot to compile

Post by Adversus »

It's probably that you are using GCC 4.0.x.

If so change the lines below (notice the additional typename) where you need to:

typedef typename std::set<_signal_base<mt_policy> *> sender_set;
typedef typename sender_set::const_iterator const_iterator;

and

typename connections_list::const_iterator it = s.m_connected_slots.begin();
typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
Pie21
Posts: 13
Joined: Mon Oct 06, 2008 11:21 am
Location: Melbourne, Australia
Contact:

Post by Pie21 »

Bit of a bump, but GPG6 and this thread have inspired me to give the Component system a crack. Over the past couple of days, I'm almost at the point where I've got all my creation stuff in place (I'm following more of a GPG approach with the ComponentTemplates and EntityTemplates, with a manager to create predefined Entites from their templates, as put together here by someone in another thread that I lost, with some modification - however I changed it a bit to kinda be in line with the stuff in the HOWTO).

However I'm getting an odd run-time error that I can't nut out. Basically I've set it up as follows. Naturally there's more to it, but this is what's relevant to the problem I think.

Code: Select all

class ComponentTemplate
{
	virtual char* getName() const = 0;
	virtual void makeComponent() = 0;
}

class HealthTemplate : public ComponentTemplate
{
	char* getName() const {return _name;}
	Component* makeComponent() {return new HealthComponent(this);}	// Constructor that takes the template for initialisation
}

class Component
{
	Entity* _owner;
}

class HealthComponent : public Component
{
}

class Entity
{
	addComponent(char* name, Component* comp)
	{
		_components[name] = comp;
	}

	std::map<char*, Component*> _components;
}

class EntityTemplateMgr
{
	Entity* createEntity(char* name)
	{
		newEnt* = new Entity;
		newEnt->addComponent(_storedTemplates[name]->makeComponent());
		return newEnt;
	}

	std::map<char*, Component*> _storedTemplates;
}
What happens when I run is that my GameManager sets up an EntityTemplate with a HealthTemplate, then tries to create an entity from this single-component EntityTemplate. It almost works, but as it gets to the EntityTemplateMgr->createEntity() function, it tries to execute this:

Code: Select all

Entity* newEnt = EntityManager::getInstance()->createEntity(entName);

	std::vector<ComponentTemplate*> components = _entTemplates[i]->getComponents();

	// Grab the vector of ComponentTemplates in the EntityTemplate
	Component* comp = components[0];
	// Try and call their methods
	// makeComponent() is the one I want to call, but getName() doesn't work either
	comp->getName();
	Component* madeComp = comp->makeComponent(newEnt->getID());
	newEnt->addComponent(madeComp);
The problem is trying to call the methods of the ComponentTemplate stored in the EntityTemplate from the EntityTemplateMgr. Basically I'm trying to the EntityTemplateMgr::Instance()-><vector of EntityTemplates>["Player"]->makeComponent(), if that makes sense.

The error says "0xC0000005: Access violation.", since the memory addresses of the METHODS I'm trying to call are like 0xabababab or whatever. I'm thinking it's a problem either with the inheritance of the methods (the HealthTemplate inherits from the base ComponentTemplate class, and is treated like one in the above case, although casting it doesn't help), or that the ComponentTemplates aren't being initialised properly when I add them with addCompTemplate in GameManager.

Oh and another odd thing is that IntelliSense suggests that comp (my pointer to a ComponentTemplate instance) has a getID() method, even though I removed every trace of it. When you 'Go to definition', it just jumps in between getName and makeComponent... since comp is the thing that's being buggy, it may be relevant.

If the problem's not immediately obvious to you guys, I've uploaded the whole solution HERE (copy Irrlicht.dll to extract directory - it's very much WIP). The only things of relevance are in the gocs filter/folder and GameManager.cpp. I'd really appreciate any thoughts, even just general ones about what causes valid objects with seemingly uninitialised methods, because I've been enjoying the component framework so far. Thanks!
Trefall
Posts: 45
Joined: Tue Dec 05, 2006 8:49 pm

Post by Trefall »

Sudi wrote:actually i didn't run into these problems with components depending on each other.
Ah, I see you solved component interdependencies by duplicating the data, so that each component own all the data it needs in a way. Yeah, I guess that's one way to do it, if you can afford spending the extra memory it will consume and the extra CPU to update multiple variables on event updates.
Having components being self-contained like you've done might be worth that extra use of memory and cpu though, it's really cool that you're still going with this approach!
Maybe the ultimate solution would be to somehow seperate the logic from the data storage, so that multiple logic components can access the same data storage components. As long as they're running in synced order, you wouldn't have to worry about locking the data access or nothing, but it shouldn't be too hard to support that, either, for multithreaded components.
Pie21 wrote:Bit of a bump, but GPG6 and this thread have inspired me to give the Component system a crack.
Glad to hear it! GPG5 had some excellent articles on the component system as well.

A couple of comments on your code:
ComponentTemplate holds a pure virtual makeComponent method of type void, while in the HealthTemplate, you have a makeComponent method of type Component*. This makes no sense and isn't how virtuals should be used. ComponentTemplate should hold a virtual Component* makeComponent obviously. Also, I'd put a virtual in front of all abstract functions you overload from superclasses, just so that you easily recognize which functions are overloading superclass functions, and which functions don't.

The true power of components comes in datadriver development, so you really want to be able to define entity-templates through XML or some script language. The task of the EntityTemplateFactory/Manager then becomes to create an EntityTemplate of an XML file it is fed through some addEntityTemplate(string filename) function. I don't really see the benefit of having a component-template.

You want entity-templates, so that you don't have to parse a text-file ala XML to generate a new instance of an entity type every time you need to (since you need to add the given collection of components), but it makes no sense to do something similar for components, thus I fail to see the need for component-templates.
A component factory/manager would be cool though, so that you can just use a string id in the XML, then from EntityTemplateFactory you just pass the string id to the ComponentFactory, that instanciate the correct type of component for you.

I also think that logic is slightly wrong when an EntityTemplate (or in your case the ComponentTemplate) has the ability to create an Entity (or in your case a component). It just doesn't fit in there imo. Check out Sudi's example, his managers (or Factories) takes care of everything that concerns the making of an entity or the instanciation of a component. He doesn't use templates, but an EntityTemplate is truly just an Entity with already added components that you can quickly do a copy of when you need a new instance of that entity type.

Code: Select all

Entity* ninjabox = EntityFactory::Instance()->copy("NinjaBox");
When using templates, that should be enough to get a new entity complete with components.

Code: Select all

Entity* EntityFactory::copy(std::string entityType)
{
    Entity* ent = EntityTemplateFactory::Instance()->copy(entityType);
    assert(ent);
    _entities.push_back(ent);
    return ent;
}
Above is a quick entity factory which really just keeps track of all entity instances and make sure that the user always get an entity in return. If the entity asked for doesn't exist, then the assertion will trigger and exit the application, because you expect all entity types you code against to already be registered with the entity template factory, thus if entity template factory returns null, it's an error/bug.


I'd check out the Game Programming section of the GameDev.net forum. There are some excellent discussions there on components and all the problems that might arise when trying to use them.
Trefall
Posts: 45
Joined: Tue Dec 05, 2006 8:49 pm

Post by Trefall »

A new approach to component-based entities using Properties was posted over at the Code Snippets forum:
http://irrlicht.sourceforge.net/phpBB2/ ... hp?t=35666
Post Reply