Jump to content

  • Log In with Google      Sign In   
  • Create Account

entities: construction, storage and destruction


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
4 replies to this topic

#1 GorbGorb   Members   -  Reputation: 112

Like
0Likes
Like

Posted 09 February 2012 - 01:38 PM

Hi, I'm not sure how to implement an entity system, as described in http://t-machine.org...opment-part-1/. Especially construction, communication between components and managing entity lifetime is giving me a hard time. Here are a few alternatives I thought about:

1) Store indexes to components in the entity object
a) in an std::map (or a sorted vector). Since map lookup is slow, I have to store additional references in components that need to access other components very often (for example, the physics component needs fast access to the transform component).
memory overhead per component:
- in map: 1 int component id , 1 int/ptr for access to specific component
- in component: optional fast-access references, optional ptr to entity object
b) in an array that is large enough to hold an index for each component type in my entity system. Finding a specific component of an entity is just indexing an array, but entities with few components suffer from a heavy memory overhead. In addition, I get cache misses if I access that lookup table very often.
memory overhead per component:
- in lookup table: 1 int component id , even for nonexistent components
- in component: 1 ptr to component entity object
2) Destroy references to components after construction and keep a reference count in the entity object
After construction, each component stores references to other components itself. Afterwards, the reference table or map is destroyed. When processing a component, I have to check wether the entity is still valid or deleted.
memory overhead per component:
- 1 ptr to reference count
- references to all possibly needed components, cannot query a component past construction

Currently, I tend towards 1a). I like that it's possible to add or remove components from entities during their entire lifetime, and it probably scales better if I want to add many different components. What are your thoughts?

Sponsor:

#2 M4573R   Members   -  Reputation: 144

Like
0Likes
Like

Posted 09 February 2012 - 06:15 PM

I troubled myself over #1 for a while too, then just ending up pushing components onto the back of a vector and doing linear look-ups. Like you said, one component will hold onto another component internally and will only do a look up on construction. Plus I'm never going to have many entities with more than a handful of components. As far as cleanup, when a component is destroyed, I immediately send a notification to anyone who cares so they can do whats needed to remove the reference. Then I can delete it right then and there.

#3 Hodgman   Moderators   -  Reputation: 30385

Like
0Likes
Like

Posted 09 February 2012 - 06:25 PM

(1b) doesn't scale.
(2) requires all components to know about their parent entity, which seems unnecessary (IMHO), and also duplicates the responsibility of lifetime management into every component class instead of leaving it in the singular entity class.
(1a) doesn't require a map or a sorted vector -- an unsorted vector/array would work too. On a data-set of a half-dozen items, a linear/brute-force search is probably going to be just as quick as a binary-search.
As for "[having] to store additional references in components that need to access other components very often", IMHO this is a good thing! Connecting components together at creation time makes any inter-component communication explicit, which makes reasoning about dependencies easier.
I'd probably use something like:
int SystemIndex(u32 handle) { return handle>>24; }
int InstanceIndex(u32 handle) { return handle & 0xffffff; }
class Entity
{
 void Delete( Subsustem* subSystems )
 {
  for( int i=0, end=numComponents; i!=end; ++i )
  {
   u32 handle = componentHandles[i];
   subSystems[SystemIndex(handle)].Delete(InstanceIndex(handle));
  }
 }
 int numComponents;
 u32* componentHandles;
}


#4 Krohm   Crossbones+   -  Reputation: 3119

Like
0Likes
Like

Posted 09 February 2012 - 11:43 PM

2) Destroy references to components after construction and keep a reference count in the entity object
After construction, each component stores references to other components itself. Afterwards, the reference table or map is destroyed. When processing a component, I have to check wether the entity is still valid or deleted.
memory overhead per component:
- 1 ptr to reference count
- references to all possibly needed components, cannot query a component past construction

I advise against this as well. I've been trying to keep reference counts in line for a while... there's no way to do so. Not in my real world at least, where scripts are to be thrown together with ease. Reference counting is, in my opinion, an hassle, unless automated. I also experimented with "block" lifetimes; those didn't work.
In the end, I had to use a garbage collector.
I advise against "checking if the entity is still valid or deleted". It's just backwards.

#5 GorbGorb   Members   -  Reputation: 112

Like
0Likes
Like

Posted 10 February 2012 - 08:46 AM

Thanks for the answers so far, confirmed me in my thoughts.

int SystemIndex(u32 handle) { return handle>>24; }
int InstanceIndex(u32 handle) { return handle & 0xffffff; }

Funny, I thought about the exact same thing right after starting this thread =)

For now I will go with 1a) and an unsorted vector. I also thought about creating a lookup table from 1b) only once and using it for querying components after construction, reusing the memory for the next entity creation.

Another aspect I want to improve in my design is loading and storing entity prototypes. I want to be able to load entities from files, create a prototype and spawn them later. The prototype class is implemented with type erasure, maybe a little bit like boost::any[] (not with slow built-in rtti though). The problems arise when I have to modify new entities slightly, for example setting position and velocity or changing a flag for this specific entity. Currently, I buffer all spawn requests to ensure my component data structures stay valid while iterating, so I can't just change a certain property right after spawning the entity.
The solution I've come up is to pass another prototype object to the spawn function which overwrites standard components if set.
This sounds rather over-engineered to me. How is that problem typically solved?




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