Still really just dipping my toes into ECS and trying to learn all the shiny new things. As a first approach I wanted to do a simple physics task such as tracking and summing forces on an object but I quickly ran into a problem that I don’t understand how to solve.
Typically what I would want to do for such a setup is something like this:
Where a component is placed on an entity that component stores a dictionary of forces affecting the entity, indexed by name.
However I can’t use Dictionaries in components, so I thought maybe something like:
PhysicsForce: Component
string name;
float value;
And then place one component for each force on the entity. However I can’t place multiple entities of he same type on an entity.
So how would I implement something like this?
In some of my searching I came across Dynamic Buffer which might be wha I should be looking at, but I couldn’t find much on this that I actually understood so i’m not 100% sure if this is what I want or not.
It’s not against ECS patterns at all. Have you read the Data Oriented Design book by Fabien? Mike Acton (who is leading the ECS design at Unity) recommends it.
In it, data oriented design is compared to relational database tables. In a relational database how do you handle storing a list of data? You put it in another table and use a foreign key.
So just trying to follow the logic here. Please correct me if I am wrong.
I would have something like this:
Entity that represents the things that can be moved by these forces
→ Components, Moveable [flag component]
Entities that are essentially sub or linked entities that represent a force
→ Components, LinkedEntity:Entity, Name:string, Force:float
System for processing moveable entities, pseudo code might look something like:
moveableEntities = Get Entities with Moveable component
foreach moveableEntity in moveableEntities
forceEntities - GetEntities with components: Name, Force, LinkedEntity WHERE LinkedEntity = moveableEntity
// sum forces and apply transform
So essentially I would do a reverse look up to find the applicable sub entities that have the entity I am currently processing as its linked entity.
In the case of this specific example though you aren’t processing these forces independently. They need to be handled in the context of the parent, or linked entity, as all the forces need to be summed and acted upon as a whole. Therefor iterating through the ForceEntities doesn’t make sense, even though it would be simpler in terms of implementation.
And this is where I’m currently getting hung up again as I am having trouble translating my pseudo code above into actual Unity ECS code. Getting and iterating over entities is fairly simple and explained in tutorials. But iterating over entities, and then looking up another set of entities with specific criteria inside the iteration is something I haven’t found an example of yet.
I might be wrong on this, but the only problem I see with treating unity’s ECS as a relational database with separate tables for lists is that you’d be doing memory jumps across chunks to lookup related entities. From what I’ve read, one of the core performance benefits of ECS comes only when you process memory in a linear manner instead. Coming from an OOP background, I also had trouble figuring out how to handle many-to-many or one-to-many relationships in ECS.
If you look into some of the examples, this is where non-permanent system state in the form of native containers comes in (like the NativeMultiHashMap advice above). The way I ended up wrapping my head around it is that system state is a sort of pre-processed data cache, and the ECS update loop is sort of like an assembly line of data. One system prepares the data and builds (or updates) a native data structure. Then other (preferably jobified) systems later in the loop can access the pre-processed data structure and operate on the entities themselves.
There’s probably better ways of doing it, but just for reference I see two ways to approach a problem like yours:
Option A - Dynamic Buffers
Since the relationship is a one-to-many (one movable object to many forces) you can represent forces in a dynamic buffer component on the movable object. Dynamic buffers act as a list you can attach to the entity.
You can then make a system that iterates over entities with Movable, and add up their dynamic buffer elements, placing the result on the Movable component (i.e. Movable.ForceSum) for other systems that may need it later on. Since this process only accesses data on a single entity at a time, you can jobify it pretty easily.
You can further make it more efficient by having whatever system adds/removes a force to the entity to mark it as changed, perhaps as a flag property on the Movable component (i.e. Movable.IsChanged). The system that adds up the forces then only needs to iterate over the entities with the flag on. I believe a flag property is more efficient than a flag component (even if it forces an if/else), since the latter changes the chunk structure. This might not be true if unity ECS makes adding/removing empty flag components more efficient later on.
Option B - System State
Have a separate entity archetype for a force, one which references the entity it affects as a property. Whenever a force is added/removed, you delete the force-entity.
Make a system that sorts force-entities by the entity they affect into a public NativeMultiHashMap. You can use SystemStateComponent to detect when new force-entities are created/destroyed, so that the system only iterates over changed force-entities to make it faster. Depending on what you need to do elsewhere with the forces, this system could either make:
A map[entity] => force_sum. If the only thing you need is adding the forces up and accessing the result elsewhere.
A map[entity] => (force_value, force_value, force_value) if you need to know the individual values later on for whatever reason.
A map[entity] => (force-entity, force-entity, force-entity) if you need to actually access more info about each individual force. But this will likely result in a random memory lookup later on.
Hi Hans, thank you so much for this. Option A seems to be what I am looking for, so I guess I need to figure out these Dynamic Buffers. When I first looked at the documentation for them, they seemed more complex and confusing than simply a component with a variable length array.
Can I ask you to clarify please, or direct me to relevant documentation, what you mean by Property versus Component? Obviously I know what a property is in terms of vanilla C#, but I am not sure what it means in terms of Unity ECS.
The idea is that you’d loop over the belongs_to entities and do some work on them independently of their siblings. Normalizing your data is a starting point, not the final destination. If you can’t work on the belongs_to entities outside of the context of their siblings you’d denormalize your “DB” and store the siblings together somewhere–in this case in dynamic buffers.
In the case of accumulating the values of each sibling though, you only need somewhere to store the accumulated values–your map[entity] => force_sum.
If you need to do this, map[entity] => (force-entity, force-entity, force-entity) or this map[entity] => (force_value, force_value, force_value), however, you can use a shared component to group the siblings together into the same chunk (or 2 or more consecutive chunks if they’re too big to fit in 1). You get essentially the same functionality without needing to add them to another data structure plus it makes looping over the entities to accumulate the forces faster.
I would imagine you could just store the accumulated value in the shared component (not really sure what happens when you start changing shared component values as opposed to making a new shared component).
Also this might not be a great solution if you have only a few child entities per parent because you’d end up with a lot of underutilized chunks.
I have to admit that most of that went over my head. I feel like i’m learning to program all over again in a brand new language with ECS. More than a brand new language even. I mean I’ve learned to program in many, many languages in my time and adopting each wasn’t too hard because I understood the fundamentals and it was just different syntax. But Unity ECS makes me feel like I don’t even understand the fundamentals anymore
This is a great discussion though so I value everyone’s input! I’m starting to think though that my first steps into ECS were taken incorrectly and I just inadvertently jumped into the deep end of the wave pool when I should be starting out over in the kiddie pool.
Hans is talking about setting a boolean member on a component to true vs attaching a component called Flagged to the Entity.
In the first case you’d loop over the components and check if the boolean was true. In the second case, you’d add the Flagged component to your EntityArchetypeQuery.
In option 1, you’d have Moveable on every entity, but when an entity is dirty you’d add an empty IsDirty component to the entity. Then the system that sums the forces would do a query for all entities with a Moveable component and an IsDirty component so that it would only iterate over dirty entities.
In option 2 you’d give Moveable an IsDirty property. Then the force accumulator system would loop over all entities with a Moveable component and you’d have to check IsDirty on every one.
Option 1 would be the better option, but right now the ECS would have to move each entity to another chunk when you add or remove the IsDirty component. Later on, from what I understand, they are going to change that so that you can add or remove empty “flag” components without moving things between chunks.
Since you would clear the buffer each frame, the system that sums up the buffer would not spend much time for 0 sized buffers. You will not have a big hit if you simply loop through all entities (you could use the changedfilter for chunks in case whole chunks are not being updated already today)
The shared component idea is quite interesting, since as you say it would keep the related entities close to each other. I didn’t think about that at all. I haven’t used them much, so I wouldn’t really know what happens when you change their values either. I wonder now what would be the best approach for a many-to-many relationship, and needing to do the equivalent of a join statement in SQL. The first thing that comes to mind is to do the three tables thing with entities (entity type A, entity type B, links of As and Bs) but I’d guess this would require some heavy GetComponent(entity) usage inside iterations if not cached on a native container somehow first.