Jump to content

  • Log In with Google      Sign In   
  • Create Account


Outboard component-based entity system architecture


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
106 replies to this topic

#61 Lord_Evil   Members   -  Reputation: 680

Like
0Likes
Like

Posted 30 October 2007 - 08:14 AM

Quote:
Original post by Emmanuel Deloget
Question: how do you manage
1) data duplication (the bounding box can be of interest to various components; should all components have an embedded bounding box)?

Well, if some data is potentially used by multiple subsystems I'd create a corresponding component.

Quote:
Original post by Emmanuel Deloget
Question: how do you manage
2) components requirements (because I have this component, I shall also have this other one)?

I'm not sure if I fully understand that question but if multiple components are needed the subsystem would check for those components individually and either reject the entity or create a default component (a bounding box, for example, could be calculated from other spatial data).

Other components could be optional. As an example the visual subsystem could expect the mesh but could also make use of some shader component.


Edit: I'd not use the Observer mechanism to change the order of processing but to build lists of entities that have changed. Then when it's the subsystem's turn within the processing order it will remember what entities/components have changed and only process those.

[Edited by - Lord_Evil on October 30, 2007 2:14:08 PM]

Sponsor:

#62 wild_pointer   Members   -  Reputation: 230

Like
0Likes
Like

Posted 31 October 2007 - 12:56 PM

I've been thinking of moving to "outboard" component storage and find myself with a pretty much 1:1 ratio of subsystems to component types. I can see myself having many, many component types. How do you manage all these? Also, this seems to make it a lot more difficult to create new components, especially from a script.

On the otherhand I see problems with "inspecting" each entity for various components that require attention each frame. Are there any other solutions? I was thinking of adding a "onTick" global type event that components can hook into which would let them get updated/drawn/whatever each frame without requiring iteration through a list of entities. Any thoughts?

#63 TwoD   Members   -  Reputation: 122

Like
0Likes
Like

Posted 01 November 2007 - 04:30 AM

Quote:
Original post by wild_pointer
I've been thinking of moving to "outboard" component storage and find myself with a pretty much 1:1 ratio of subsystems to component types. I can see myself having many, many component types. How do you manage all these? Also, this seems to make it a lot more difficult to create new components, especially from a script.


I see this too in a way. I'm currently sketching out an engine design based on what's been said in this thread and other places, and there does seem to become a 1:1 (or near) relation between components and subsystems. Of course you could allow a subsystem to add more components to an Entity (or just associate them with the Entity and store them in the system itself), depending on which behavior it should have.

I guess this depends on how exactly you define your components. On the Engine-level (no game specific components here) I see few occasions where I would need more than one component at a time from each subsystem. It might be a different component depending on type of animation needed from the Animation subsystem and such, but I don't think one would need more than one component from that specific system.

On this level subsystems seem to become very strongly associated with their own components. So if you want to add a new behavior to your Entity, you might need to write a new subsystem to take care of that specific task. (Especially since components here seem more like data containers as I've understood it.) #1

I'm going for a subsystem interface, so I can add new subsystems at any time and know that the engine knows how to pass it an Entity if told to (from script perhaps).

This could also mean some game specific (above engine level to be clear) subsystems are implemented in script, utilizing script implemented components. The subsystem could of course also be dynamically linked if one so wishes.


In "Dungeon Siege" they had a Component Database to manage the components. Each component had a unique id (Siege Component ID) and each Component "template" also had one if I'm not mistaken. When they needed a new component they just asked the DB for it using the template id. Since they had a continous world, they also had to load and unload a whole bunch of components all the time. They solved the problem with maintaining unloded components state using "SCIDBits", 32 bits stored together with the SCID even after the component was unloaded. That way a Chest Component could remember if it had been opened before etc even right at the moment it was loaded back into the world.

EDIT:
#1) Hmm, what if one would stretch the component "pattern" further and include "plug-and-play behavioral components" in say the animation system? If I want a different type of animation behaviour (skeleton instead of frame based), but would like to keep the framework of the animations subsystem, I could make a new behavior component for the subsystem, and a data component for the Entity. I'm not sure if this would be practical at all, it just hit me it might be worth a thought or two... it's probably more work than replacing the whole animations subsystem with one that does all you want to tho.

[Edited by - TwoD on November 1, 2007 10:30:30 AM]

#64 supagu   Members   -  Reputation: 148

Like
0Likes
Like

Posted 20 November 2007 - 12:18 AM

i've been using a data-driven design for a while now which im not happy with heres the gist of it:


class Message
{
}

class Entity
{
bool HandleMessage(Message* msg);

array<Entity*> children;
Entity* parent;
}


think of the top most level as an entity, then all of its children and thier children are components.

the problem i had with this is that communication is a problem between entitys.

many times i would just assume my parent is the entity i needed communication with.

All communication must be done using HandleMessage. This is also another problem. Its ugly having hundreds of messages, but it does make for some cool scripting opportunities!

i've been thinking about this stuff alot, and my new system im about to prototype after reading other peoples thoughts and scouring the web for what others have done is this:


class Entity
{
InsertComponent(component)
RemoveComponent(component)

GetComponent(type)
array<Component*> components
}

class Component
{
InsertObserver(component)
RemoveObserver(component)
NotifySubjectChanged(component)
NofityObservers()

Entity* owner
}


the observer pattern looks like it can solve my communication problem, but this means components need to know about other components, which i think is fine so far here's an example i came up with:


class HealthComponent
{
void TakeDamage(damage)
{
health -= damage
NotifyObservers()
}

void Regenerate(amount)
{
health += amount
NotifyObservers()
}

float health
}

class ScoreComponent
{
void Init()
{
HealthComponent* hc = owner->GetComponent(HealthComponent::Type)
hc->InsertObserver()
}

void NotifySubjectChanged(subject)
{
switch (subject->GetType())
{
case HealthComponent::Type:
HealthComponent* hc = subject;
if (hc->health <= 0)
deaths++;
break
}
}

int deaths;
int kills;
int score;
}


creating entitys is easy to do:

Entity e;
e.InsertComponent(new HealthComponent);
e.InsertComponent(new ScoreComponent);
e.Init();


Components also act as interfaces, so i dont need the ugly messages, i can do this:


HealthComponent* hc = e.GetComponent(HealthComponent::Type);
if (hc)
hc->TakeDamage(10.f);


im just wondering how to do state machine type code with this system now, and how im going to do scripting. Might need a special function
HandleScriptCommand as part of a component

#65 HexDump   Members   -  Reputation: 209

Like
0Likes
Like

Posted 20 November 2007 - 03:30 AM

If anyone could post some guidelines about how to divide components and how to use this paradigm using a script system I would be really grateful.

And by the way I will make a really newbie question regarding this system.

Think of an entity that has a Component containing its position, orientation, etc... And a Physics component. The Physics component needs to know the position of the entity, how do you retrieve it? I have been read through the thread about sending messages, etc... wouldn´t be enough doing something like $this->Owner->Get Component("PositionOrientationComponent")->GetPosition()?. And isn´t this a lot of trouble to just get the position every time it is needed?.

Sorry for the newbie question...


Thanks in advance,
HexDump.

[Edited by - HexDump on November 20, 2007 10:30:31 AM]

#66 pancakedice   Members   -  Reputation: 159

Like
0Likes
Like

Posted 20 November 2007 - 04:45 AM

Quote:
Original post by HexDump
And by the way I will make a really newbie question regarding this system.

Think of an entity that has a Component containing its position, orientation, etc... And a Physics component. The Physics component needs to know the position of the entity, how do you retrieve it? I have been read through the thread about sending messages, etc... wouldn´t be enough doing something like $this->Owner->Get Component("PositionOrientationComponent")->GetPosition()?. And isn´t this a lot of trouble to just get the position every time it is needed?.


I believe Sneftel mentioned that the entity actually has spatial information, because many subsystems needs it. The outboard components knows the entity and can therefore get the positioning info directly from it. He could have made a SpatialComponent, but that will only make almost all of the subsystems depend on that instead.



#67 notbad   Members   -  Reputation: 122

Like
0Likes
Like

Posted 20 November 2007 - 06:23 AM

dingojohn, this was just an example, what I wanted to know if this is the way components get things from another components.

As I said in my last post if anyone could explain some guidelines about how to divide components and how to use this paradigm using a script system I would be really gratefull.

By the way, what are this subsystems you´re talking about? render system? input system? IA system? etc...?

Thanks in advance,
HexDUmp.

#68 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 20 November 2007 - 07:12 AM

Quote:
Original post by notbad
dingojohn, this was just an example, what I wanted to know if this is the way components get things from another components.

If a subsystem knows about another subsystem, then it can find out about the other subsystem's components. That lookup information is necessary anyway (for when entities are removed). So when a component is created which will depend on another component, the creation process can resolve that linkage. From there on out, the component can just ask the other component.

I can't really give you great guidelines on how to divide components, other than to say that this sort of an architecture leads to slightly fewer components than the standard onboard component system tends to. As for scripting, that really depends on where scripting fits into your game architecture.

Quote:
By the way, what are this subsystems you´re talking about? render system? input system? IA system? etc...?

The first and third are good matches. For the input system, since only one entity is controlled, the setup can be a lot more simple.

#69 HexDump   Members   -  Reputation: 209

Like
0Likes
Like

Posted 20 November 2007 - 07:38 AM

I´m using LUA, if this is of some information.

HexDump.

#70 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 20 November 2007 - 07:53 AM

Quote:
Original post by HexDump
I´m using LUA, if this is of some information.

A little bit, but not much. In terms of control flow, information flow, etc., how does scripting fit into your game architecture?

#71 HexDump   Members   -  Reputation: 209

Like
0Likes
Like

Posted 20 November 2007 - 09:28 AM

Well, I don´t really know what you mean, but I can tell you I´m using couritines for my entitnes (each thread is a entity). I Have some functions that define the behaviour of the entity, I mean, Events, like OnHit(), OnDie(), OnCreate(), etc...

From my entity system I create a lua entity (it is a C++ class) that calls the OnCreate() in lua and this sets everything the entity needs to init (mesh used, speed, etc...), after it comes back from OnCreate(), I finish the initialization in C++.

Sorry if I´m making silly questions, but I really afraid to move my design to component system and having lot of undercovered problems.

By the way, what are this events system used for? I read something about communication between components? I think there's a more direct way to do this, don´t you think?.

The other problem I see with component system is the granurality of it. I think we will end up with hundreds of components for just everthing.

Thanks in advance,
HexDump.

#72 HexDump   Members   -  Reputation: 209

Like
0Likes
Like

Posted 21 November 2007 - 02:14 AM

Well, I have been doing some drawings and thinking about my design :). Here is where I am so far, I hope could give his opinion about it:

1) I will go with a component system where entities will be a container for the components.
2) The components will be stored in a map/hash, with key "component name", instance.
3) When a component is added to an entity, it will publish to the entity callbacks
to handle the component, example:

====> $this->Owner->RegisterAction("UPDATE",UpdateCallback);
====> $this->Owner->RegisterAction("GOTO",GotoCallback);
====> ....

This registrations could be mantained in a map/hash of type <String,CallBack>
A call for example to get the Position of a entity (if we have decided to to put this in the entity itself because of its use in almost all entities) to a Frame componenet would be something like:

====> $this->Owner->DoAction("GETPOSITION");

and this will give us position of the entity.

This provides me with a mecahnism to not having hundreds of simple methods in entity that the only thing that do is delegating to the real method of the component.


4) In order to handle parameteres, returns, we could use a ParamList that will be passed, or returned by the Action.

What do you think about it?.

Thanks in advance,
HexDump.

#73 Thergothon   Members   -  Reputation: 160

Like
0Likes
Like

Posted 29 November 2007 - 02:39 PM

This is a great thread, I've learned a lot. Inverting the component system is so beautifully simple I can't believe more people haven't thought of it and been advocating it.

One thing though, I'm not sure this system lends itself well to threading across many, many cores. It would be easy enough to throw each subsystem into it's own thread and let them run asynchronously, but eventually I can't see you coming up with enough subsystems for all the cores you will have. And since objects are just data containers in this case, with no reference back to their components except loosely through listeners, it would be difficult to group spatially close objects and throw those at threads instead.

Any thoughts?

#74 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 29 November 2007 - 03:28 PM

Quote:
Original post by Thergothon
One thing though, I'm not sure this system lends itself well to threading across many, many cores. It would be easy enough to throw each subsystem into it's own thread and let them run asynchronously, but eventually I can't see you coming up with enough subsystems for all the cores you will have. And since objects are just data containers in this case, with no reference back to their components except loosely through listeners, it would be difficult to group spatially close objects and throw those at threads instead.


Actually, I think this system is probably more amenable to parallelization than the standard component design. If you can't come up with enough subsystems to give all your cores something to do, just split a subsystem update across multiple cores. That's probably a good idea regardless, since different subsystems will eat up different-sized CPU slices. (This is assuming your subsystem updates can be parallelized; if they can't, you're screwed regardless of which design you go with). The key here, I think, is a job-based as opposed to a thread-based model, which Sony's been trying to convince people to go with anyway.

#75 Thergothon   Members   -  Reputation: 160

Like
0Likes
Like

Posted 29 November 2007 - 09:56 PM

Makes sense, looks like I'm going to have to hit up google for some job-based v. thread-based articles.

Cheers.

#76 David Neubelt   Members   -  Reputation: 794

Like
0Likes
Like

Posted 01 December 2007 - 08:59 AM

Quote:
Original post by Sneftel
Actually, I think this system is probably more amenable to parallelization than the standard component design. If you can't come up with enough subsystems to give all your cores something to do, just split a subsystem update across multiple cores. That's probably a good idea regardless, since different subsystems will eat up different-sized CPU slices. (This is assuming your subsystem updates can be parallelized; if they can't, you're screwed regardless of which design you go with). The key here, I think, is a job-based as opposed to a thread-based model, which Sony's been trying to convince people to go with anyway.


Creating a good parallelization design doesn't mean that you design sub systems to live on a thread/cpu. I don't really feel the component model will ever lend itself well to working on multiple threads. If there are any links to the outside world even if its loosely coupled then, in my opinion, the design of the system is flawed for multiple cpus/threads. Parallelization works best when you work in a functional model. Take input, transform input, create output. You can then take a bunch of these functions queue them up and send them off to do work. This allows one subsystem to use multiple CPU's/threads. The real work being done by functions and the subsystem handles setting up the data to be processed and any interaction with any other systems.

For example, if you have an AI subsystem then a good way to parallelize it is to create functions that perform work (i.e) chunk the world data into a buffer and send it off to a thread/cpu to find a path through it. You could have 10 of these working at once. As long as you can design your AI subsystem can defer looking for the best path until the threads are finished processing it.

It's kind of like programming in a purely functional language. If you know a function has no side effects then you know its ripe for parallelization.

I love all the different forms of component design but the beauty of their design is how well they can glue into different systems. I don't think trying to force them to be parallel is going to be a good clean solution. I feel it's better to separate the communication/interaction with the components/systems and the real grunt processing into functions.

This reminds me of the Tim Sweeney - The next generation of programming languages Where he shows statistics on how much code/time is spent in game play systems and how much code/time is spent on hard numerical crunching. The numerical crunching part is where you want to multi-thread the game play systems should be able to have references to shared data or events wherever they want.

-= Dave

#77 thre3dee   Members   -  Reputation: 100

Like
0Likes
Like

Posted 01 December 2007 - 11:23 AM

Well i had a quick look at the thread post and not at the rest of it so objectively here is my idea of a component based architecture for systems:

// System base class (used for Video or Audio subsystems etc)
class System { };

struct NamedIndex
{
int index;
string name;
};

// Holds a database of named and indexed systems
class SystemDatabase
{
private:
map <NamedIndex, System> mSystems;

public:
// registers a system with the database
void SetSystem (string name, int index, System * system) { }

// gets a system
template <class T
T * GetSystem (string name, int index = 0) {
Entry e = { index, name };
map <Entry, System>::iterator i = mSystems.find (e);
if (i == mSystems.end ())
return 0;
return dynamic_cast <T *> i->second;
}
};

// Sub class of System
class SceneGraph : public System
{
public:
SceneGraph (SystemDatabase * db) {
db->SetSystem ("scene", 0, this);
}
};

// to access a system
int main ()
{
SystemDatabase db;
SceneGraph * graph = new SceneGraph (db);

if (db->GetSystem ("scene", 0)) {
printf ("Yay it worked");
}
}


While this still allows objects to depend on other systems, it separates them from the object itself by having to ask the database for a type of system and it giving it whatevers there.

As for components and entities:

class Component;

// This collection of components abstracts it away from what object it is actually in (which may be a bad thing i dunno)
class ComponentCollection
{
private:
map <NamedIndex, Component *> mComponents;

public:
// sets a component (in implementation)
void SetComponent (string, int idx, Component * c)
{
// ... add component
c->mParent = this;
}

void Update (double dt);
};

class Component
{
private:
ComponentCollection * mParent;

public:
Component (ComponentCollection * parent);
};

// entity
class Entity : public ComponentCollection { };


Having a parent component collection member variable allows them to have a look at the other components by asking the collection for the object under the desired name such as
mParent->GetComponent <SceneNode> ("scene_node", 0)->Move (x, y, z);


#78 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 01 December 2007 - 04:24 PM

Quote:
Original post by David Neubelt
Creating a good parallelization design doesn't mean that you design sub systems to live on a thread/cpu. I don't really feel the component model will ever lend itself well to working on multiple threads. If there are any links to the outside world even if its loosely coupled then, in my opinion, the design of the system is flawed for multiple cpus/threads. Parallelization works best when you work in a functional model. Take input, transform input, create output. You can then take a bunch of these functions queue them up and send them off to do work. This allows one subsystem to use multiple CPU's/threads. The real work being done by functions and the subsystem handles setting up the data to be processed and any interaction with any other systems.

Yes, this is what I was talking about WRT a job-based instead of thread-based system.

#79 supagu   Members   -  Reputation: 148

Like
0Likes
Like

Posted 03 December 2007 - 11:11 PM

using the observer pattern, just wondering how people are handling this notify subject changed callback.

eg.


class Component
{
}

class ComponentA : public Component
{
void SomeFunction()
{
NotifyObservers(?);
}

void SomeOtherFunction()
{
NotifyObservers(?);
}
}

class ComponentB : public Component
{
void NotifySubjectChanged(Component* component, ?)
}




supposed ComponetB is an observer of ComponentA, but component B want to know different information based on if SomeFunction or SomeOtherFunction was called on ComponentA.

hence, some additional event info needs to be sent, i guess this should be an event id. It would be nice if i could just send through the pointer of the function that was called but c++ you cant just cast function pointers to void* unfortunately.

any one have any good ideas what to do here?

#80 vtchill   Members   -  Reputation: 180

Like
0Likes
Like

Posted 04 December 2007 - 06:21 AM

Quote:
Original post by supagu
using the observer pattern, just wondering how people are handling this notify subject changed callback.

eg.

*** Source Snippet Removed ***

supposed ComponetB is an observer of ComponentA, but component B want to know different information based on if SomeFunction or SomeOtherFunction was called on ComponentA.

hence, some additional event info needs to be sent, i guess this should be an event id. It would be nice if i could just send through the pointer of the function that was called but c++ you cant just cast function pointers to void* unfortunately.

any one have any good ideas what to do here?



Well my quickest solution would be to create event classes inherited from a base event class and pass the base event class around to Notify objects of changes. I don't think you should have to pass the entire component for every change.


class BaseEvent
{
BaseEvent(int type, int source, int dest);
int eventType;
int senderId;
int receiverId;
};

class DerivedEvent1 : public BaseEvent
{
DerivedEvent(double information);
double derivedInformation;
};

class DerivedEvent2 : public BaseEvent
{
DerivedEvent(double information1, double information2);
double information1;
double information2;
};

class ComponentA
{
void SomeFunction()
{
BaseEvent *event = new DerivedEvent1(some_info);
NotifyListeners(event);
}

void SomeOtherFunction()
{
BaseEvent *event = new DerivedEvent2(some_info, some_other_info);
NotifyListeners(event);
}
};

class ComponentB
{
void NotifySubjectChanged(BaseEvent *event)
{
switch(event->eventType)
case EVENT_FLY:
{
// cast to specific event here
DerivedEvent1 devent = static_cast<DerivedEvent1*>(event);

// use information contained in derived event here

break;
}
...
}
};






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS