Advertisement

What is the best design for handling Parent-Child relationships in Entity Component System ?

Started by September 26, 2021 11:22 AM
4 comments, last by Kylotan 3 years, 1 month ago

I was thinking about this for the past week but every idea I have seem to have a big problem with it

First I thought about making every entity have a parent even if the parent is just an empty point in the origin, needless to say why was this a bad idea, it'd require extra 4 bytes (because of the parent ID) for every entity

Second I thought about making a parent component, But the problem with this is the amount of sub subroutines I'll have, take this example

for(every entity)
{
	if(entity.hasparent)
	{
		Draw(entity.position + entity.parent.position, entity.size);
	}else
	{
		Draw(entity.position, entity.size);
	}
}

it just feels like bad design and that's without including the really complex part of rotations

also what if the child is a parent of an another entity this way I'd need to get the position of its father and the father of its father

I feel extremely lost at this point, Do you know anything that can help me ?

None

alabdaly891 said:
First I thought about making every entity have a parent even if the parent is just an empty point in the origin, needless to say why was this a bad idea, it'd require extra 4 bytes (because of the parent ID) for every entity

4 bytes overhead per entity is next to nothing, compared to the overhead that pretty much every implementation of “parent as a component” is going to have, even if only a fraction of those is going to have a parent. Say you have 1 million entities, thats 4 MB of data - compared to the 100MBs you are going to need to store all the other data for those million entities, that doesn't really matter. Also, you are going to gain a lot by being able to access the parent-id from a (statically known) field of that class when accessing it, instead of having to fetch an unrelated component somewhere else in memory (and having to check for its existence).
Also, depending on what else your “entity" stores, you might not even need 4 byte overhead at all, at least if you are compiling for 64-bit. I often find that there is some space left due to padding - if you have, say, 4 bools and everything else just larger types, you can embed the parent-id in the padding after those 4 bools without taking up any additional size at all. And even if thats not the case, you would still have 4 byte free for additional data to store later on.

alabdaly891 said:
Second I thought about making a parent component, But the problem with this is the amount of sub subroutines I'll have, take this example

The key is to offer different methods for aquiring positions. This is usually called “local” and “world”:

for(every entity)
{
	Draw(entity.GetWorldPosition(), entity.size);
}

This also has the benefit of making the actual implementation flexible - it doesn't matter whether the parent-id is stored directly in the entity or a separate component; you can also add caching of those calculations (useful if you have a huge nesting-depth and/or are requiring matrices/rotations to inherit as well. And since its behind the “GetWorldPosition”-interface you can change all those properties without having to change the code responsible for drawing.

Advertisement

alabdaly891 said:
First I thought about making every entity have a parent even if the parent is just an empty point in the origin

There are many kinds of “parent/child” relationships.

Here, you are talking specifically about a transform parent. The point of a transform parent is that, if you move/rotate the parent, the child goes with it – the child position is naturally expressed in the reference frame of the parent, so it is “local” to the parent.

I've seen a lot of scene graphs that have tried to jam this concept through everything, and ended up confusing relationship to the point where “when I place a coffee cup on a table, it inherits the wood grain of the table,” which makes no sense. Even inheriting the position from the table is quite tenuous if the position of the cup is physically simulated. The first cat that comes around will knock it off! So, there's a friction/gravity-based relationship between cup/table, but there's not really a position-parenting relationship.

The abstraction that ends up working okay in almost all scene graphs / engines I've seen, is that every object CAN have a parent, and that's probably just a pointer from the entity to another entity. You could also separate out “having position,” because maybe there are entities that don't have position, and thus every position-component CAN have a parent, which would be a position-component attached to some other entity. But, when there's no parent, the position is in global space.

Separating out parentage itself into another component just for that, makes no sense, because there will be many different kinds of parentage – your game is a graph, not a tree.

Also consider that not everything that has a position is an entity. Each bone in an animated character is not its own entity, but it is a possible attachment point for other entities. And each particle in a particle system is not an entity, but it is something that might want to collide against the world. If you want to attach swords to hand bones, it may be enough to say that a TransformComponent can have a parent, and a bone is a TransformComponent (perhaps a special BoneTransformComponent instance.) But if you also want to attach things to particles, you may need to go all fancy and say “things with position” which may not even be components, and then use some kind of flyweight/delegated approach to be able to have a “position” expressed as “the location of particle 138 in that particle system over there” without inserting itself on the inside of said particle system.

enum Bool { True, False, FileNotFound };

And you shouldn't forget about the System design. Usually Systems collect the components they need and don't use any if/else switches for “optional” components. So have a system which consumes entities including parent component and a system that handles all other components

This is another area where the much hyped ‘entity component system’ approach does poorly when faced with real-world problems. In the book Game Engine Architecture the author implies that some engines choose to limit the parent-child ‘chain’ to an arbitrary length (e.g. 3) so that they can be sorted into a fixed number of groups and updated that way. e.g. First you update all horse positions, then you update all positions of people on horses. You still need a way to be able to gather a comnponent and its parent component together for processing, but it can still be done as batch operations and without any recursion or pointers. As with most entity/component/system approaches, it just moves the complexity upstream.

This topic is closed to new replies.

Advertisement