A case for implementating relationships #1056
Replies: 3 comments 6 replies
-
Ehi, sorry for the late reply, I was out on vacation. 🙂
🤔 EnTT doesn't recommend anything actually. This is one of the possible approaches to the problem though.
I'm not sure about the goal of this discussion, sorry. Are you suggesting a possible implementation or asking for a feedback on this approach? |
Beta Was this translation helpful? Give feedback.
-
I was referring to the proposed solution for representing hierarchies on the wiki: Could you elaborate a little bit on the entityx-like aproach? I'm not familiar with the implementation details of the library.
It's meant as a pitch for a possible implementation of relationships inside entt. In a way that I felt could be a good fit for the library as it has very similar characteristics with regards to memory usage/layout and time complexity. Though feedback is very much appreciated as well.
Same here, which was sort of the trigger for me to try and come up with some options.
I would like to contest this notion. If we assume the linked list approach from the wiki is a sound solution using the regular component storage, my suggested implementation offers similar memory use and potentially improved performance due to internal optimizations (I say potentially , because I can only really know for sure by measuring an actual implementation). I'm not sure what you mean by no data. This solution allows for arbitrary data to be associated with the relationship (including empty types), just like with regular components.
To some extent. Though there are some possibilities for trading some more memory for performance and vice versa. This is based on a one-to-many implementation, as I feel it is the most useful variant. Adding a new relationship is either O(n) where n is the number of relationships already assigned to an entity, or O(1) if you store the index of the last link as well (not currently in my aforementioned solution, but probably recommended?). Performing a get(Entity entity) (either subject or object) is identical to regular components. Iteration: checking for the presence of an entity in a storage when iterating multiple types is identical to components.
It is, which is sort of the premise of the solution. The difference is that it's integrated in the library which simplifies implementation for the user. For instance, if I have a relation for attacking (for combat between entities). I could have an Attacking component, which allows me to request entities that are attacking others, but if I want to know if entity x is being attacked without running through all my Attacking components, I need to implement a component for the reverse relationship as well and maintain the dual administration. In my proposed solution, I only need something like an Attacking relationship and I can request the information from either side of the relationship at the same speed as I would a regular component. I hope that made sense, my brain is only partially functional at this hour :P. |
Beta Was this translation helpful? Give feedback.
-
Oh, just something that's been buzzing in my head for a while and I'd like to try.
How so? The gut feeling says O(1) if you prepend. Isn't it the case? 🤔
Two entities, an element in a vector, an update who knows where in memory. It sounds... expensive, for the common case. Isn't it?
Can you make a concrete example from a real world case of yours? Just to talk about something concrete.
This is the oddest part imho. Let's reset it for a moment and speak in these terms instead: how would you design it if you could specialize a storage? Because we can and it's likely easier to implement something that works better? Dunno, just thinking aloud, never really concerned about this problem so far to be honest, so bear with me. 🙂
As an user, how can you tell which one is a relationship here? struct X {};
struct Y {}; I think I'm missing this part. I'm not a fan of an API that duplicates for the purpose (as in |
Beta Was this translation helpful? Give feedback.
-
Hi,
I've been using entt to implement a development tool. My (currently hypothetical) users will interface with a thin layer that is built on top of entt (but is analogous in use). Overal I've been happy with how things are shaping up, with the exception of offering relationship support. Currently I use the approach that is recommended by entt (the one that uses linked list logic). Functionally, it works, but I have some issues with it:
My idea started as basically the linked list approach, but moving it into the storage. But after iterating on this for a while I came up with this:
I will be using relationship terminology like this: subject object
A regular component storage looks something like this:
The most general implementation (many-to-many relationship) would look something like this:
At first glance, this might seem like a lot, but all of the new arrays will have identical capacity and sizes to entity_array. Which means you can optimize for space usage very well. Also, you can have variations on this for one-to-one and one-to-many relationships that will be more compact. the nexts and prevs could also be combined into 1 data-structure. I'm not entirely sure what would be most efficient.
The way it works is that you can add the same "component" multiple times to the same (subject) entity but with multiple object entities. when the component is added for the first time, the corresponding entry in nexts and prevs is the null-index. Following components will add the index of the second component in the nexts-field corresponding to the first component, etc. The rest is analogous.
Iterating these requires some expansion of the view. Adding extra type lists for these newly introduced mechanisms, next to the included and excluded components. Due to template specialization, the current implementations can remain intact.
Iteration of one-to-one and one-to-many relationships would be identical to components, but instead of just passing in a reference to the data, you pass in a struct that contains a reference to the data and the "other" entity.
Iteration of many-to-one and many-to-many is a bit more complicated. My current idea is that the outer loop would acquire the regular components for each entity (like now) and there would be an inner-loop that iterates all the different relationships for that entity, the data of the regular components would be passed to the lambda repeatedly.
Dealing with multiple relationships in a single query is ambiguous I think. Currently, my idea is to allow only a single multi-entity query in a single view. one-to-one, one-to-many could remain multiple.
Beta Was this translation helpful? Give feedback.
All reactions