Giter Club home page Giter Club logo

Comments (85)

mhaemmerle avatar mhaemmerle commented on May 10, 2024 6

I'm trying to understand why a singleton disguised as something else should be so tempting.

They already exist in EnTT:

registry.attach<PausedComponent>(registry.create());
bool paused = registry.has<PausedComponent>();

registry.attach<ScoreComponent>(registry.create(), 0);
ScoreComponent &score = registry.get<ScoreComponent>();

I don't want to create an entity for that:

registry.attach<PausedComponent>();
bool paused = registry.has<PausedComponent>();

registry.attach<ScoreComponent>(0);
ScoreComponent &score = registry.get<ScoreComponent>();

I want to revel in the glorious signal goodness without any nagging what-if's:

void onScoreAdded(Registry &registry)
{
    std::cout << "Score: " << registry.get<ScoreComponent>().score << std::endl;
}

void onScoreRemoved(Registry &registry)
{
    // ...
}

registry.tagAdded<ScoreComponent>.connect<&onScoreAdded>();
registry.tagRemoved<ScoreComponent>.connect<&onScoreRemoved>();

It is not, at least from my point of view and you have no arguments so far to prove the opposite. :-)

They're convenient AF. The registry contains my application's complete model. No need for setting up singleton(s). I'am lazy.

from entt.

vblanco20-1 avatar vblanco20-1 commented on May 10, 2024 2

That makes complete sense. I guess Registry&,Entity would be the best, to keep construction and destruction on the same signature.

from entt.

skypjack avatar skypjack commented on May 10, 2024 2

@mhaemmerle

Not yet. I'm working to refactor the signal stuff so as to separate signals from sinks. I want to avoid things like this:

registry.construction<Component>().publish(entity);

In other terms, publish will be be available only within the registry as it ought to be.

To define a reactive system isn't much difficult anyway.
It's trivial in case you want to observe only one component (sort of keep track of all the entities to which Component is attached during a tick or do something as soon as Component is attached to an entity and so on). It's a bit trickier to do when you want to observe multiple components, but still feasible.
Probably I'll provide users with some basic systems from which to inherit for the most common cases in future. I'll put a note in the TODO file so as not to forget it.

from entt.

skypjack avatar skypjack commented on May 10, 2024 1

At a first glance, this is a possible approach:

  • For each type of component it will be possible to attach listeners and be notified about either creation or destruction

  • A listener will receive a single argument, that is the entity involved

  • In case of creation of a component, the signal is sent immediately after the operation

  • In case of destruction of a component, the signal is sent immediately before the operation

  • There will be no signals in case of creation and/or destruction of entities

  • Currently I'm still looking for the best way to send signals for tags

An alternative approach could be this one probably:

  • A single sink to which attach listeners that are notified about the creation of components, listeners will receive both the entity and the numeric identifier of the component involved

  • A single sink to which attach listeners that are notified about the destruction of components, listeners will receive both the entity and the numeric identifier of the component involved

More details will come. What about so far? Questions? Comments?

from entt.

skypjack avatar skypjack commented on May 10, 2024 1

Just a thought (feedbacks are appreciated on this).

To completely avoid allocations, we could limit the number of listeners to one on construction and one on destruction for each component.

Benefits:

  • As I said, no allocations at all
  • Probably better performance
  • Easier to work with when it will come the time to introduce support for custom allocators

Drawbacks:

  • Well, quite obvious, only one listener is accepted, thus users must republish events on an external bus if they want to attach more than one listener
  • Persistent views will affect probably a bit more creation and destruction of components (still to verify if I can avoid this)

In general, it looks like supporting events has a consequence: creation, destruction and iterations cannot be kept all at the minimum.
We must find the best compromise and I vote to keep iterations as lower as possible in time.

What about?

from entt.

vblanco20-1 avatar vblanco20-1 commented on May 10, 2024 1

In my opinion, one single listener is a good idea if it makes it faster. The more generic case would be to have one system listen for the destruction of the component they are handling with an external resource. If one wants to have multiple listeners, he can just then use another kind of external bus, wich allows one to still have multiple listeners if thats what he wants.

from entt.

skypjack avatar skypjack commented on May 10, 2024 1

It's pretty simple. The API will look probably like this:

// Free function
registry.construction<Component>().connect<&func>();
// Member function
registry.destruction<Component>().connect<Class, &Class::member>(& instance);

Something along this line also to disconnect listeners.
As mentioned in the title, signals on component creation/destruction.
I'm still trying to figure out if the same is possible for tags and how to implement it.

I've still to consider any other change to the topic so far.

from entt.

skypjack avatar skypjack commented on May 10, 2024 1

@vblanco20-1

You save a get only in some cases actually. As an example, during a remove the registry doesn't get the component, so to get it for you introduce a performance hit otherwise avoidable in case you weren't interested in the component.

Here the design decision is:

  • Differentiate the signatures for the callbacks (void(Registry &, Component &, Entity) during construction, void(Registry &, Entity) during destruction).
  • Use the same signature and provide users with all the data (it forces a get during destruction that could be avoided otherwise).
  • Use the same signature with the minimum set of data (that is void(Registry &, Entity), the one I proposed).

Why I'm proposing and voting for the last one?

Well, one reason is obvious: I don't want to get anything during a remove and I like to have the same signature in each case.
Moreover, I considered a possible use case of a listener during construction: blueprint. In other terms, when component C is attached to an entity, attach also A and B. In this case, the listener isn't interested at all in the component itself, right?
Because of that, I started thinking that probably there are a lot of cases where the listener isn't interested in the component and it can still get it otherwise because of the reference to the registry. On the other side, a common and straightforward signature for all the cases is tempting me a lot.

I've still to decide about it (I'm refactoring the signal stuff in a sort of signal-plus-sink model, so as to be able to reuse them in the registry without breaking encapsulation), so feel free to give me a good enough reason to follow another approach and let's discuss it!! ;-)

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024 1

Didn't find documention either, but remembered this issue here; maybe even better than documentation as there's a discussion with the reasoning behind it.

from entt.

skypjack avatar skypjack commented on May 10, 2024 1

@mhaemmerle

An issue regarding tags and signals is just popped out.
Unlike components, tags aren't explicitly updated when an entity is destroyed. Because of how entity identifiers are defined (entity + version), it isn't necessary to update tags and thus I never do it (just an optimization).
In other terms, a tag is available to use if it has never been assigned to an entity or in case the entity to which it belongs has a different version (actually it means that the tag no longer belongs to that entity because the latter has been destroyed meanwhile).
So the performance hit signals for tags would introduce is O(N) with the number of tags, at least for some operations.

I'm going to try a different approach.
As far as I can see, you're interested in a sort of reactive systems and they look like a good tool to have actually. Right?
Well, I think we can easily implement them even without signals on tags because of how those systems work. It's possible probably because of their update step that changes a bit the rules of the game. Tags can be treated differently than components and we can still get the same result.

from entt.

skypjack avatar skypjack commented on May 10, 2024 1

@mhaemmerle

In the meantime I added signals for tags. Branch experimental.

from entt.

skypjack avatar skypjack commented on May 10, 2024

By reviewing the codebase I found that a change in this direction can improve things also for persistent views. In fact, they affect to some extent creation and deletion of components even if persistent views aren't used. It can be (probably) avoided.
I put a note in the TODO file for future changes that improve things in general and let users enable or disable signalling for specific components.

I'm closing the issue for it's no longer an open discussion.
I'll work on it for sure sooner or later. ;-)

from entt.

skypjack avatar skypjack commented on May 10, 2024

Reopened this to discuss API and use cases.
I'll put all the details in a comment later.

from entt.

gamagan avatar gamagan commented on May 10, 2024

If this feature is free(ish), this is probably something that people can make use of.

Will this also include notification for entities being created/destroyed? That is, registry.create()/destroy() ?

from entt.

skypjack avatar skypjack commented on May 10, 2024

@gamagan What do you mean with if this feature is free? The whole library is under the MIT license, right?
I don't think it will include notifications for creation and destruction of entities, for it's a great performance hit that I don't think it's worth it. I mean, what are the use cases for that? Can you elaborate a bit? Thank you.

from entt.

gamagan avatar gamagan commented on May 10, 2024

hehe. Free as related to performance.

If getting signals on create()/destroy() will tank performance, then it aint worth it.

Also, its not a must have feature. I was just curious.

Use case:
System(s) listen to create()/destroy() signals. On create(), they inspect if the entity has components they care about. If so, keep reference to entity. on destroy() signal, remove entity from list. You would also subscribe to component add/remove signals, to keep list updated. Then, on the update method, you dont have to iterate over views: u have the exact list u need.

from entt.

skypjack avatar skypjack commented on May 10, 2024

Oh, ok. Then no, unfortunately it's not for free, quite obvious.
Actually, the performance hit is pretty high if compared to the performance of EnTT.

Your use case makes sense, but you can obtain the same by listening at components, right?
If you are going to inspect an entity to see if it has some specific components, then you can just listen at them and do the same after all.
Moreover, consider your statement:

You would also subscribe to component add/remove signals, to keep list updated. Then, on the update method, you dont have to iterate over views: u have the exact list u need.

This way you're replicating more or less the behavior of a persistent view, that is extremely fast and keeps tightly packed its entities.
You can also share a view between different systems, thus reducing memory usage. So... :-)

The fact is that a kind of signals on construction/destruction for components are somehow required to keep updated persistent views. They have a cost, of course, but EnTT cannot work without them, so it's a matter of defining an API to let users use them too.
On the other side, events on construction/destruction of entities aren't required and thus introducing them would mean add a performance hit for no benefits for the framework. Note also that users can still add an extra layer for that if they want, so I prefer to keep them out of the registry if possible.

That's all.

from entt.

gamagan avatar gamagan commented on May 10, 2024

Sounds good.

from entt.

vblanco20-1 avatar vblanco20-1 commented on May 10, 2024

Component dependencies would be a very useful feature. Ive already implemented a version of that on my end as i needed it (as im using wrappers to create the components, those wrappers also check for component dependencies).
For what you are saying, it seems interesting. At the moment im using a "Delete" component that i add when i want to delete something, and then my DeleteSystem checks for all the dependencies. But that is a lot more problematic than just listening for component deletion of a specific type and act on it. Just making a system listen for the destruction/creation of the component they handle would be good enough.

Its important that it only costs performance if you are actively using it. Would it degrade performance if i have 30 components with destroy listener, but i destroy an entity that has none of them and no listeners?

On the listener itself, you say that it would give the entity ID. Is there any possibility it could also give a pointer/reference to the Registry? There are cases where a user might have multiple registries and would want events on both and a way to easily separate them. Or just to use the registry to check for other components on the entity ID from the event. This would allow one to very easily just point the listener into a global function.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@vblanco20-1

Ive already implemented a version of that on my end as i needed it (as im using wrappers to create the components, those wrappers also check for component dependencies).

Can I ask you more details on this? Maybe I'm missing something to which you thought and I could steal your ideas. :-)

Its important that it only costs performance if you are actively using it. Would it degrade performance if i have 30 components with destroy listener, but i destroy an entity that has none of them and no listeners?

In general and trying to sum up, the performance hit in this case is the one of a comparison between iterators for each component.
Imagine you have an array of listeners for each component. If you delete a component, the registry tries to iterate listeners to notify them, so you have at least a sort of:

while(begin != end) { /*... */ }

In case no listeners are registered, begin is equal to end and thus the loop terminates immediately. However, at least a comparison is required, unfortunately.

To be honest, the current version of EnTT already contains something similar to be able to notify persistent views if any.
Creation and destruction of entities and components aren't usually performed frequently and thus a small performance hit on that part is acceptable. This is the thought that drove the choice when I was implementing persistent views.

On the listener itself, you say that it would give the entity ID. Is there any possibility it could also give a pointer/reference to the Registry?

It makes sense actually, good point. It shouldn't be a problem. I'll try to do it as soon as I have time to work on this feature. Stay tuned, I'll give you a feedback on this sooner or later.

from entt.

vblanco20-1 avatar vblanco20-1 commented on May 10, 2024

Can I ask you more details on this? Maybe I'm missing something to which you thought and I could steal your ideas. :-)

Sadly its not useful to you. As you saw from my other posts, im integrating enTT with unreal engine. To do this i use "wrappers" that are unreal engine ActorComponents. This ones act as a container, and when a normal unreal object gets spawned in the world, the ActorComponents get notified and register their internal EnTT component with the global registry. For dependencies, i do a "has" check on this wrappers. For example my HomingBullet entt component has a HomingBulletComponentWrapper. when i spawn a unreal engine actor that has a HomingBulletComponentWrapper (and other wrappers), they register with the ECS, and the bullet starts moving. HomingBullet adds Velocity and Position if they arent already there.

If thats the performance cost of the listeners, thats absolutely no issue. Sounds good.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@vblanco20-1 Good, not a problem.
The idea is to let users attach only member functions. No lambdas, to avoid extra allocations due to the use of std::functions.
What about? Your experience with EnTT is invaluable.
Thank you very much for your help, really appreciated.

from entt.

vblanco20-1 avatar vblanco20-1 commented on May 10, 2024

You already have signals and similar. Easiest would be to just use one of those, no?

from entt.

skypjack avatar skypjack commented on May 10, 2024

@vblanco20-1 Yep and they don't accept lambdas. :-)

from entt.

vblanco20-1 avatar vblanco20-1 commented on May 10, 2024

If you are sending the registry + the entity ID, there is a good chance it can work on free functions, with a completely basic function pointer. If you need to access "persistent" data, you could get it from a tag in the registry.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@vblanco20-1 Actually SigH already works with both free functions and member functions, so why not? I wouldn't limit the whole thing to the sole free functions. It will be easier also to attach directly systems to the registry and you can avoid defining specific tags for your purposes.

from entt.

skypjack avatar skypjack commented on May 10, 2024

I'm experimenting with both the solutions. Some updates:

  • The single listener approach is trickier than what I thought initially, mainly because of the persistent views that break a bit the whole thing. It's quite hard to define an implicit list among them and I haven't find a good way to do that so far.

  • Using SigH and the others seem to be slower than implementing a custom solution tailored to the requirements of the Registry class. It's quite redundant actually, because the code required is very similar to the one of SigH. Anyway, performance are definitely better.

  • Submitting the component to the listeners during creation seems to ruin a bit the performance for unknown reasons, even though it doesn't involve any get or whatever. Submitting the component to the listeners during destruction ruins definitely the performance, mainly because it requires an extra get onto the underlying set that could be avoided otherwise.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

What about using something like persistent views for the created/destroyed components? So one could prepare a persistent view for either component creation or destruction, retrieve it in one or more systems and clear it at the end of the frame?

Depending on how you look at it, it could either be a replacement for the events or a completely separate implementation.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle I see memory usage as the main issue in this case. Sparse sets aren't the best approach to this problem unfortunately.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

Don't let yourself be misled by my use of "persistent view" - that was aiming more at the user's perspective of having to prepare the view before utilizing it :) and thus enabling the collection of added/removed components. The core of my suggestion is to store the added/removed components in a datastructure/view that can be iterated over by the user (potentially multiple times / frame). And just in case we're talking about different things: I'd expect exactly one component type per one of these iterable views/datastructures so that the user doesn't have to filter other types out in systems. Depending on the used datastructure there will obviously be an overhead per stored component - but if it's not outrageously large, it could very well be acceptable and the cost attached to this feature?

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle I'm asking just to fully understand your point.
The registry hasn't the concept of tick/frame/loop, so there is no guarantee that data are meaningful, no matter what is the data structure in use. It would be completely in charge to the user to clean them sooner or later.
Moreover, it requires to copy entity identifiers and components, because the registry recycles all of them as soon as possible to keep at a minimum the memory usage (likely already within the same loop, if possible).
How could it be better than sending a signal and let the users decide what to do?

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

I think it would be perfectly fine to communicate the behaviour of that collection in the documentation. I'am also going out on a leg here, but my assumption is that almost all users of ECS are using them in some kind of game/render loop? Also the reset function on the registry could clear out the collection if the ECS is used in a more on/off way.

The components could obviously not be reused while being in said collection. For the entities you could store identifier and version and return a nullptr when a user trys to retrieve an entity with a correct identifier but an old version.

The "problem" with the pure signal approach is that it's up to the user to collect the components and iterate them in a system, which forces outside knowledge of said system to register the signal handler. Further if the component is about to be reused, it can't be stored in a collection outside the registry, unless they're all copied. For me it doesn't seem to be the ECS approach anymore, where one gets a view into a stream of components that can be iterated upon very fast and where the framework holds all the data and manages it's lifecycle (even more important with custom allocators).

Maybe I should have been clearer about this: I don't think these two ways are mutually exclusive of each other. I just think EnTT managing the collection of added/destroyed components and subsequent iteration and (user-triggered-)destruction of that collection conforms to the managed view-iterate approach of EnTT/ECS. It also opens up the building of composable reactive systems as seen in Entitas-CSharp while being opt-in instead of ... well, there's no way to opt-out in Entitas-CSharp :)

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

[...] my assumption is that almost all users of ECS are using them in some kind of game/render loop?

I'm using EnTT at work and it's not part of a loop actually. :-)

The components could obviously not be reused while being in said collection.

This is the most tricky part. As it works now, destroying a component makes it immediately available to its pool. Instead, you are proposing a kind of two-step destroy process. I've no idea about how to implement it without refactoring a great part of the codebase.

The "problem" with the pure signal approach is that it's up to the user to collect the components and iterate them in a system, which forces outside knowledge of said system to register the signal handler.

What I don't understand is why one would want to store components aside. I mean, as an example I can imagine a blueprint system that reacts to component creation and adds more components to the entity involved. In this case, a signal based approach is more than enough, right? Another example, component deletion: a cleanup function that listens and reacts to perform a more elaborate cleanup for some reasons. Can you elaborate a bit more instead about a case in which component creation or destruction could trigger an operation that requires to store aside the components? I don't see the use case, my fault.

Maybe I should have been clearer about this: I don't think these two ways are mutually exclusive of each other.

Yeah, don't worry. The discussion is interesting and that's why I'm making a lot of questions. Thank you for all of your answers!!

It also opens up the building of composable reactive systems as seen in Entitas-CSharp while being opt-in instead of ... well, there's no way to opt-out in Entitas-CSharp :)

Aren't reactive systems used to react to changes? (Un)Fortunately EnTT doesn't keep track of changes in components, you can freely modify their contents and never tell EnTT you did it. This way it's quite difficult to react to changes... :-)
Anyway we can still react to creation and destruction, that is better than nothing, right? ;-)

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

Aren't reactive systems used to react to changes? (Un)Fortunately EnTT doesn't keep track of changes in components, you can freely modify their contents and never tell EnTT you did it. This way it's quite difficult to react to changes... :-)
Anyway we can still react to creation and destruction, that is better than nothing, right? ;-)

Guess why I was asking for a dedicated method for replacing tags? :) When using add, replace or remove you're telling the framework about your intentions and it can do more than e.g. just swapping out a component for an entity; like dispatching an event, adding the component to a meta collection etc.

What I don't understand is why one would want to store components aside.

Actually you don't need to store the components at all, now that I had more time to think about it. It's just the entity that gets stored; I'am actually confused now why I suggested it in the first place. It would only make sense in very select use cases and even then you could just keep a delta component around.

In the MatchOneEntt conversion from the Entitas-CSharp example I had to define multiple additional components to model reactive behaviour. Specifically GameBoardUpdatedComponent, PositionUpdatedComponent, ScoreUpdatedComponent, UpdateScoreComponent, FallEventComponent and FillEventComponent. That's 6 additionally needed components on top of the existing 18 ones - which equates to 33% more components.
While in the original e.g. the AnimatePositionSystem was working with a collection that observed adding of the PositionComponent to only run when the position changed (which in Entitas-CSharp means that the component gets replaced via e.g. entity.replacePosition(0, 0, 0)), for EnTT I had to add a meta component (PositionUpdatedComponent) to tell the AnimatePositionSystem that it has to run and update the actor position of said entity and then remove the PositionUpdatedComponent (see here).
Similar behaviour for e.g. the FallSystem and the FillSystem which in the original are working with a collection that observes the removal of the GameBoardElementComponent and execute only if there's at least one entity (!!! :-) :-)) in the observed collection.

Below is a list of the systems used and executed in order in the MatchOneEntt example project. Even though the PositionComponent gets assigned in the GameBoardSystem, subsequently used (=read) in some of the following systems, it's only right at the end in the SetViewPositionSystem and AnimatePositionSystem that the comparatively expensive operation of updating the transform of an actor gets executed - if (!!!) the PositionComponent has changed (= added to the entity).

And that .. is why I would love to see change collections in EnTT :)

Add(std::make_shared<ProcessInputSystem>());

Add(std::make_shared<GameBoardSystem>());
Add(std::make_shared<FallSystem>());
Add(std::make_shared<FillSystem>());

Add(std::make_shared<ScoreSystem>());
Add(std::make_shared<ScoreListenerSystem>());

Add(std::make_shared<RemoveViewSystem>());
Add(std::make_shared<AddViewSystem>());
Add(std::make_shared<SetViewPositionSystem>());
Add(std::make_shared<AnimatePositionSystem>());

Add(std::make_shared<DestroySystem>());

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

Guess why I was asking for a dedicated method for replacing tags? :)

I see. :-)

Actually you don't need to store the components at all, now that I had more time to think about it. It's just the entity that gets stored; I'am actually confused now why I suggested it in the first place.

Good. You confused me too. I'm glad we agree on this point.

[...]

In general, as long as there exists a non-const version of get, I think that sending a signal during a replace could be misleading.
I mean, it gives the idea that the framework keeps track of changes, but it isn't true. You can still work around the built-in system by means of a get and errors like this can be very subtle.
Not also that views do not have a replace member function and keep track of changes from them could be even more tricky.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

In general, as long as there exists a non-const version of get, I think that sending a signal during a replace could be misleading.
I mean, it gives the idea that the framework keeps track of changes, but it isn't true. You can still work around the built-in system by means of a get and errors like this can be very subtle.
Not also that views do not have a replace member function and keep track of changes from them could be even more tricky.

It seems like I was unable to explain the concept of reactive systems and their benefits in my previous comments. At this point I think I've said all there is about this topic.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

It seems like I was unable to explain the concept of reactive systems and their benefits in my previous comments.

You succeeded in explaining the whole fact.
However, the two libraries have a very different nature.
I like the concept of reactive systems indeed and I see the benefits in your case (even though I would have done things in a slightly different manner, so as not to create 6 extra components), but they cannot be implemented exactly the same way they have been developed in Entitas. This is a fact.

As an example, the main difference with EnTT is that components in Entitas are (pseudo) immutable. It means that you must update them through a replaceSomething and thus the library can intercept changes and react accordingly.
In EnTT, you can do this instead:

registry.get<Position>(entity) = { 0.f, 0.f };

That is, components are mutable. You can change them and the registry won't know any time soon about it.
Because of this, a collector attached to a replace event can work if and only if you extensively use replace and never try to modify a component directly. Pretty error-prone with the current API, isn't it?
On the other side, you cannot create or destroy components in any way but using assign and remove/destroy. Because of this, it's easy to create signals for those events and users cannot work around it and make mistakes.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

I like the concept of reactive systems indeed and I see the benefits in your case (even though I would have done things in a slightly different manner, so as not to create 6 extra components), but they cannot be implemented exactly the same way they have been developed in Entitas. This is a fact.

I hereby invite you to fork the repository and refactor it to the proper EnTT way :)

Your're right in the regard that this will not work in Entitas:

var entities = context.GetEntities(Matcher<GameEntity>.AllOf(GameMatcher.Position));
foreach(var e in entities) {
    e.position = new Position(0, 0);
}

As an example, the main difference with EnTT is that components in Entitas are (pseudo) immutable.

Though EnTT and Entitas share the exact same behaviour when mutating the component directly. You can do the following in Entitas and it will work perfectly fine (apart from losing support for reactive systems for the mutated component type - in this case Position):

var entities = context.GetEntities(Matcher<GameEntity>.AllOf(GameMatcher.Position));
foreach(var e in entities) {
    var pos = e.position;
    pos .x += 1;
    pos .y += 1;
}

Using the replace method is simply a convention that apparently everybody in the Entitas community is able to follow. When searching the issues of the Entitas repository for replace I've got 89 hits and only one of them was asking a bit of an undecipherable (see here) question about adding or replacing components. That together with the sheer popularity of Entitas shows that in reality it's a non-issue to use the documented API methods for adding/replacing/removing components.

So for me it rather sounds like you've made up your mind and are not interested in reactive systems/collections because then we would be talking about ways to actually make them possible instead of assuming that the average user of EnTT has the intellect of a potatoe and is incapable of grasping the concept of a replace method ;)

Also an easy way for you to direct the conversation into a more productive direction would have been the suggestion to make get really return a const component and maybe even ask for a pull request for it...

from entt.

dbacchet avatar dbacchet commented on May 10, 2024

@mhaemmerle, I read twice the entire thread and I believe you're overreacting a little bit :)

So for me it rather sounds like you've made up your mind and are not interested in reactive systems/collections because then we would be talking about ways to actually make them possible instead of assuming that the average user of EnTT has the intellect of a potatoe and is incapable of grasping the concept of a replace method.

The point that @skypjack was raising was not about brain-dead users that cannot understand how to use a method; was about a design decision and being consistent with that.

Also an easy way for you to direct the conversation into a more productive direction would have been the suggestion to make get really return a const component and maybe even ask for a pull request for it...

Or maybe to ask since the beginning "what's the best way of modeling reactive systems using EnTT"? I feel you want to implement exactly the same semantics of Entitas on top of EnTT, but the design of the library is quite different...
Asking for a const get is essentially asking to redesign the user API. Direct access to the components has been there since the very beginning and I use it intensively in my code for example, because I assume that my developers are smart enough and I want them to be able to touch directly the memory without useless layers of indirection.

I feel like the enhancement you're proposing is interesting, and I'm sure that @skypjack is already thinking on how to make it possible, but probably with an API different than what you have in mind. Wouldn't that solve the problem too?

from entt.

skypjack avatar skypjack commented on May 10, 2024

Thank you @dbacchet and, of course, it goes without saying that I'm thinking about it. You know... :-)
It's a matter of time.

@mhaemmerle

I'm sorry you reacted this way.
Just to be clear, my goal isn't to make a clone of an already existent library. Even more if a requested feature can be a potential performance hit or a design flaw.

Fortunately users of EnTT are smarter enough to help me in designing and defining such a good library step by step. Most of the time they gave me a hint so as to implement something the right way within EnTT, instead of pretending to copy a feature as it is from some other libraries. This time isn't different, I'm getting a lot out of this issue because of you all!!

I would be happy if you decided to continue contributing to EnTT.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

@dbacchet @skypjack Seems like I should've added an emoticon after the second to last paragraph, because it's meant tongue in cheek but sadly left room for a different interpretation. I've dropped the ball by not making that absolutely clear (like in the very beginning of the comment) and fixed that through an edit.

I'am going to wait for the implementation of the Signals on component creation/destruction feature to decide whether EnTT fits the bill for me or not.

Cheers

from entt.

vblanco20-1 avatar vblanco20-1 commented on May 10, 2024

Syntax looks good to me. I could easily get use from it. What is the signature of the signal function?

from entt.

skypjack avatar skypjack commented on May 10, 2024

@vblanco20-1 Probably void(Registry &, Entity). The same both for construction and destruction. It's easy to reuse also in future in case we add signals on replace, create and destroy. Sounds good?
Not sure if it's worth it sending events also on creation/destruction for tags actually. Any suggestion?

from entt.

vblanco20-1 avatar vblanco20-1 commented on May 10, 2024

What about doing the component too? you would save a get(). void(Registry&,Entity,ComponentType)

from entt.

ArnCarveris avatar ArnCarveris commented on May 10, 2024

@skypjack
Would be nice, if will be implemented following API.

// Entity construction
registry.construction().connect<&func>();
// Entity destruction
registry.destruction().connect<Class, &Class::member>(& instance);

from entt.

skypjack avatar skypjack commented on May 10, 2024

@ArnCarveris

What is the expected behavior exactly? Your listener attached to all the pools of components or a listener for construction/destruction of entities?

from entt.

ArnCarveris avatar ArnCarveris commented on May 10, 2024

@skypjack

What is the expected behavior exactly? Your listener attached to all the pools of components or a listener for construction/destruction of entities?

Only when entity was created/destroyed.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@ArnCarveris
The sole drawback I see is the performance hit it introduces. Not along critical paths probably, not sure about it.
What are the use cases for that? I can't figure out why I should be interested in creation/destruction of entities but for debug purposes. Moreover, note that we can obtain almost the same result using a dedicated component and observing it.

from entt.

ArnCarveris avatar ArnCarveris commented on May 10, 2024

@skypjack

The sole drawback I see is the performance hit it introduces.

What performance hit difference between entity & component creation/destruction?

What are the use cases for that? I can't figure out why I should be interested in creation/destruction of entities but for debug purposes.

For internal editor, capturing created entity range group.

Moreover, note that we can obtain almost the same result using a dedicated component and observing it.

No need create extra magic component for observing entity only lifetime.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@ArnCarveris

What performance hit difference between entity & component creation/destruction?

They are two different problems.
Component creation/destruction is somehow required to provide users with persistent views. Moreover, it enables several features otherwise pretty difficult to develop.
Entity creation/destruction isn't required for the purposes of the registry. Adding a signal handler there would mean adding something like this (even if you have no listeners at all):

while(begin != end) { /* ... */ }

So, the performance hit is the one of a few instructions to retrieve and compare a couple of iterators in case you have no listeners.

For internal editor, capturing created entity range group.

Internal editor?

No need create extra magic component for observing entity only lifetime.

Yep. I see that introducing a signal handler means to get rid of extra components to do that. I was only mentioning the fact that it's still possible without a signal handler, that's all.

from entt.

ArnCarveris avatar ArnCarveris commented on May 10, 2024

@skypjack
I see, that is bad idea, there are ways that can be achieved without it.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle It's me or Entitas hasn't a replace event and it triggers a couple of destruction/construction events in place of it?

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

There's a delegate on the entity here and the site where it's called here. This is triggered after calling the respective replace method in the generated extension here (watch out, the last link is from the demo project). Though a user might never use said delegate directly as it's mostly used internally to update the watching collections for the reactive systems.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

I was referring to something like this (first link I found, not sure it's still valid):

Whenever you replace either of those it will be removed from the group (intentionally), because the idea is to remove the old component (=> entity doesn't match anymore) and the replace it with the new component (=> entity matches again). As a result e.ReplaceXyz will always trigger OnEntityAdded AND OnEntityRemoved

Anyway it looks like it's strictly related to the groups. Your links seem to confirm that the common behavior isn't to trigger also added/removed events.

Thank you.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

The groups have an event of type AddedOrRemoved seen here which confirms your initial assumption. Yes, the discussion in the link is a bit older, but the rough concept is still in place. If a user is interested in getting events for added/replaced/removed components, they would create a group for it and use the respective delegate. After adding the new event system to Entitas a couple of weeks ago, there's hardly any need for that anymore though. Also it would be an anti-pattern to attach to an entity's delegate directly as an end-user.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle
I was exploring the different ways to notify listeners about a replacement and I found it.
It seems interesting the idea of deducing that something has been replaced as a consequence of an add/remove sequence and as long as the entity identifier doesn't change in the meantime. The drawback is that listeners on add/remove are triggered as well if the replace works like an alias for sort of remove-then-add the component.
Not sure if it's a viable approach and thus I was asking. ;-)

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

After adding the new event system to Entitas a couple of weeks ago, there's hardly any need for that anymore though.

Where is the documentation of the new event system?
I'm curious to know if the behavior is similar to what I observed.

Thank you.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

Put aside the fact that we cannot detect changes on data members in C++ (the registry doesn't even know what are the data members of a type actually), it seems that the event system of Entitas launches events only when components are added or removed. Am I wrong?
I wouldn't add a per entity signal handler, mainly because it seems a waste in terms of memory usage and also because entities are nothing more than numbers in EnTT after all, so it would be tricky.
That being said, all what remains are events on creation and destruction, that are already exposed as mentioned above:

registry.creation<Component>().connect<&MyFunction>();
registry.creation<Component>().connect<MyClass, &MyClass::myMemberFunction>(& instance);

Where a listener has the following function type:

void(Registry &, Entity);

Using them, we can easily define systems that react on changes and infere even a replacement from a destruction followed by a construction on an entity that doesn't change version. We can also collect changes if required and filters entities with these two signals.

What else do you think is required?
Comments on this from whoever has experience with real world applications are welcome.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

Basically everything in Entitas is using groups under hood one way or the other; those are kept up to date by subscribing to component changes on each entity. You're right in your assumption that the events feature in Entitas only runs when components are added/removed to/from an entity.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

Sparse sets are kind of groups at the end of the day.
The behavior you described is more or less how persistent views work under the hood: they are (tightly packed) groups of entities kept aligned with the registry each time a component in which they are interested is added or removed.

The issue was that the signals to which they where attached weren't exported to the users.
I did it now, everything is made available to the users. I'm testing them and it works pretty well.
As it stands right now, we can create almost everything as an external tool then: blueprint infrastructure, reactive systems, all that comes to our mind and works by observing construction and destruction of components is possible.

Wasn't this what you were asking for after all?

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

Do you have an example of an EnTT reactive system?

from entt.

skypjack avatar skypjack commented on May 10, 2024

A first draft of the changes is available from experimental.

The sole doubt I have is if it's worth it adding signals also for tags.
I mean, a tag is quite easy to observe even without signals: store aside the attachee, in case it's different during the next tick we can deduce that a remove and attach took place and we have both the entity involved at once. An obvious advantage is that we reduce memory usage and we don't introduce any performance hit out of the box. The sole drawback is that the notifications aren't synchronous.
What about this?

Any feedback on my doubt and the changes from experimental is appreciated.

Thank you.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

If the intent of tags is to have guaranteed unique components, than they should have first class signal support. Otherwise the library is shifting the responsibility to it's users and forcing them to write boiler plate code.

How have you solved the generation of signals for components that are directly changed like this?

registry.get<Position>(entity) = { 0.f, 0.f };

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

Otherwise the library is shifting the responsibility to it's users and forcing them to write boiler plate code.

Well, built-in support for signals in the registry introduces performance hits. I can still define systems that do the job under the hood whether required for the users, so as not to force them to do it.
The real issue is that it won't be synchronous. I'm trying to figure out if it can be a problem after all.

How have you solved the generation of signals for components that are directly changed

We cannot really solve it, so... If you get a reference to a component and modify it, the registry won't know about it in any case.
The best I can do is to send a couple of added/removed signals within the replace member function, even though I'm not that sure it would help actually. It works only as long as users consistently use that function throughout the whole codebase.

So far, only assign, remove and destroy triggers events for components.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

Well, built-in support for signals in the registry introduces performance hits. I can still define systems that do the job under the hood whether required for the users, so as not to force them to do it.
The real issue is that it won't be synchronous. I'm trying to figure out if it can be a problem after all.

I have a more radical suggestion: split the current tag implementation into two distinctly different features (single responsibility principle).

The first one would use HashedString to tag entities with the guarantee of being unique in the context of a registry. This would solve the current semantic disconnect of a tag being more than just a tag.

The second one would use regular components, guarantee uniqueness in the context of a registry, manage the owning entity (if necessary at all) and would thus also need to offer the same semantics w.r.t. signals as regular components.

We cannot really solve it, so... If you get a reference to a component and modify it, the registry won't know about it in any case.
The best I can do is to send a couple of added/removed signals within the replace member function, even though I'm not that sure it would help actually.

I agree, think that it is perfectly fine to do so (use replace) and also think that it would actually help. Examples in that vein would be STM's where you have to wrap your code e.g. in an atomic block or call commit when you're done with your changes. You can obviously leave out the atomic or change values directly on an object, but it either wouldn't work or would produce errors directly or further down the line. So developers actually use the API neccessitated by the library/concept and thus get the correct behaviour.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle Isn't this the same as what you have described as tag?

template<HashedString::hash_type>
struct Tag {};

I see a lot of people consider tags just like a name I can attach to something so as to retrieve it easily later on, but I don't see any drawbacks in letting tags be something more than a plain string. I mean, the latter still allows the former.

It makes sense probably to fire removed/added events on replace. Users can still avoid the performance hit using a get. I wouldn't add a third type of signal anyway (kind of replaced). I saw almost no ECS out there did it, probably there is a good reason for that.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

I see a lot of people consider tags just like a name I can attach to something so as to retrieve it easily later on, but I don't see any drawbacks in letting tags be something more than a plain string. I mean, the latter still allows the former.

Maybe sometimes there's merit in what "a lot of people consider"; even if it would just help you decomplect an already pretty dense API and thus implementation. Maybe it would even help you implement signals on unique components in an easy manner. But alas, I get the feeling that you've made up your mind already and know better now than trying to convince you otherwise ;)

It makes sense probably to fire removed/added events on replace. Users can still avoid the performance hit using a get. I wouldn't add a third type of signal anyway (kind of replaced). I saw almost no ECS out there did it, probably there is a good reason for that.

Cool.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

Maybe it would even help you implement signals on unique components in an easy manner.

The problem is all in the fact the components already required signals because of the persistent views, so the performance hit was already there. Any other feature do not use signals, so it would introduce a performance hit.
To be honest, creation and destruction of entities, tags and components aren't usually along critical paths, so I don't think it's a problem to introduce signals all around. My two cents.

Actually I haven't made up my mind already, that's why I'm trying to explore your point of view.
What I'm saying is that what you mentioned as tags is already possible. Do you see it?
The difference in splitting the whole thing in two parts would be that the call point changes from this:

registry.attach<MyStringTag<HashedString{"foo"}>>(entity);

To this:

registry.attach<HashedString{"foo"}>(entity);

Of course, unless you want to use runtime strings, but it doesn't make sense in a high performance tool to do something like this, so I'm excluding it. So, is it shorter? Right. Drawbacks? An extra data structure to work with, probably a map, something that isn't properly memory friendly. Anything else? No, at least I don't see any other difference.
Therefore, it goes without saying that what a lot of people consider is already possible with EnTT, so I don't see why you blame me. :-(

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

a lot of people consider

Actually thinking about printing this on a T_Shirt :)

Any other feature do not use signals, so it would introduce a performance hit.
An extra data structure to work with, probably a map, something that isn't properly memory friendly.

These two assertions are super abstract to me, as there's no number attached. How much slower is it going to be? How much more memory usage are we talking about? Where should the line be drawn w.r.t. to EnTT's promise on performance? Also isn't in line with the EnTT creed that you pay for what you use? Maybe it's time for another round of benchmarks? (On that note, what do you think about converting the unit from seconds to milliseconds? The former is rather pointless for such small numbers ;))

This is purely theoretical at this point: e.g. if a second data structure for string tags would get introduced, it would increase memory usage by the baseline of the structure itself and then should have similar characteristics to the existing one. And if there's someone I'd trust to pick the best data structure for the job, than it's you w.r.t. to EnTT and C++ :)

So what is the big problem that restricts tags from taking part in the new signal goodness? :)

from entt.

skypjack avatar skypjack commented on May 10, 2024

How much slower is it going to be? How much more memory usage are we talking about?

Much, much slower actually!! :-D
At a first glance, in case of no listeners it's a matter of an extra:

while(begin != end) { /* ... */ }

So, a comparison each time a tag is attached or removed?

Memory usage is the one of an empty vector in case of no listeners, so it's mainly implementation defined. However I'd say at least the size of an extra couple of pointers for each tag.

what do you think about converting the unit from seconds to milliseconds?

Actually it could make sense. :-)
I used the same benchmarks of EntityX initially, so as to compare the two libraries, therefore I kept the same pattern on benchmarks.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

So what is the big problem that restricts tags from taking part in the new signal goodness? :)

Actually no problem at all apparently.
What about:

void(Registry<Entity> &, Entity)

Vs:

void(Registry<Entity> &, Entity, tag_type)

The main difference is a couple of signals for each tag type vs a couple of signals shared between the tags and the burden of an if user side.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

Much, much slower actually!! :-D

That is still rather ... abstract ;) And it still sounds too complex for what it's doing IMHO, though let's agree to disagree here, ok? :) As the number of tags is probably way, way smaller than the number of entities it's probably alright no matter what.

void(Registry<Entity> &, Entity, tag_type)

Cool, that's something people can work with and don't have to worry about different behaviours.

        std::pair<
            SigH<void(Registry &, Entity)>,
            SigH<void(Registry &, Entity)>
        > listeners;

Out of curiousity, what's the reason for using a pair here (especially when listeners is private)?

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

I was joking. It's a comparison between pointers in case of no listeners. We are speaking of stardust here. :-D

void(Registry<Entity> &, Entity, tag_type)

So, the if to check if it's the right tag is in charge to the users this way. Right? It makes sense for me.

Out of curiousity, what's the reason for using a pair here (especially when listeners is private)?

The pair is because I keep two separate lists of listeners here: the ones that want to be notified about construction and the ones that want to be notified about destruction.
Why does it sound strange to you?
It's exactly the same as using two data members once compiled, but I had to find only one name this way. :-D

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

So, the if to check if it's the right tag is in charge to the users this way. Right? It makes sense for me.

Got a more full-fledged example here?

It's exactly the same as using two data members once compiled, but I had to find only one name this way. :-D

It's just ... odd ;) Also godbolt.org says it's ~40 instructions more :P

constructionSignal, destructionSignal
onConstruction, onDestruction

Splitting hairs here.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

Got a more full-fledged example here?

Sort of:

void f(Registry<Entity> &registry, Entity entity) {
    // ... You already know that is the right tag
}

registry.construction<MyTag>().connect<&f>();

Vs:

void f(Registry<Entity> &registry, Entity entity, tag_type type) {
    if(type == registry.tag<MyTag>()) {
        // ... You check the type
    }
}

registry.connect<&f>();

Out of my mind, this won't be probably the real API, but it gives you an idea of what I'm speaking about.

Also godbolt.org says it's ~40 instructions more

Really? Using -O3? O.o
It's definitely odd, I agree.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

Out of my mind, this won't be probably the real API, but it gives you an idea of what I'm speaking about.

Obviously not having to do the comparison would be better, but it's a good enough start for now.

Really? Using -O3? O.o
It's definitely odd, I agree.

Added -O3 and the difference is even bigger. I'am probably doing something wrong, so here's the link :)

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

You could also use two maps for tags to trade memory for speed.

Component -> Entity
Entity -> Component

If you'd split the EnTT tags feature into actual tags and singleton components, you could use two maps for the tags and one map for the singleton components, as they actually don't need an entity to work.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

as they actually don't need an entity to work.

I didn't get this point. Single instance components need an entity to work actually.
As an example of use I've been told of by an user, imagine a Camera component you want to be unique. It's attached to an entity that has also a Transform and who knows what other components.
When you want to update all the positions of your entities, you can get the transform from the entity that has the camera (attacchee), then perform a multiplication.

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

You could solve that with string tags.

I may be biased, but I really like this explanation of unique components. You can get at the entity behind the unique component, but it's frowned upon.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

In Entitas, unique components are components. So they need an entity. Am I wrong?

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

They are components, Entitas generates the code to extend the Entity-class as well as the Context-class (using C# extension feature), so they still need an entity. When accessing the component, Entitas does an iteration over a collection containing exactly one entity. Which is ... sub-optimal IMO :)

Edit: Entitas uses partial classes to extend Entity/Context and C# extensions for the Matcher.

When removing the component, the entity gets destroyed as seen here.

So, instead of doing the proper thing and building up an index in the context, they've bent the existing tools quite a lot to enable unique components...

To summarize: if one would use an index for unique components, no entities are needed. They'd be regular structs which can be called components to describe their POD nature.

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

If one doesn't want to assign a single instance component to an entity (to be able to retrieve other components as in the example I made), why putting that component in a registry or ECS or whatever?
I don't understand. I mean, make it a singleton and that's all. Is it only so as to be able to say - I don't use singletons for they are bad?
Come on, that's exactly the same thing, used the same way, pretty well buried into a data structure so that it's hard to recognize. Right?

from entt.

mhaemmerle avatar mhaemmerle commented on May 10, 2024

Of all the things mentioned in my comment and the Entitas cookbook ... that's the one thing you cherry-pick to get mad about? ;) Dude, I don't think I can help you at this point ;)

from entt.

skypjack avatar skypjack commented on May 10, 2024

@mhaemmerle

Ahahahah... Well, we were speaking about tags and single instance components, right? :-)
At the end of the day, Entitas just uses two different data structures to handle exactly the same thing. It doesn't make much sense for me but I'm pretty sure there is a reason for that.

With a C++ ECS (not necessarily EnTT) you can use:

  • either a tag component if you want more complex single instance components
  • or a template class that accepts a non-type template parameter (the hash of a string) if all what you want is to assign a name to an entity

And that's all. This is how C++ works. The rest is syntactic sugar.

Do you want to see something like this as part of the API?

registry.tag<HashedString {"foobar"}>(entity);

It's trivial to do and the library already supports something similar out of the box. Why similar? Because what is missed is syntactic sugar and nothing more as I said, under the hood it would work exactly the same way it does today.
There is no reason to add a map to the registry to keep track of something we can already map to tags quite easily.

The fact is that you asked to separate tags in two parts a few comments ago: one (let me say) string based to assign to entities, the other one component based and entity free.
An example, you said? Entitas. I pointed out that Entitas doesn't work this way and you replied that it should instead. I pointed out that it wouldn't make sense anyway because those would be only singletons buried within the registry or within Entitas and that's it...

It's me that noted only this in the Entitas cookbook, now. :-)

I'm not looking for help right now. I'm trying to understand why a singleton disguised as something else should be so tempting.
It is not, at least from my point of view and you have no arguments so far to prove the opposite. :-)

So, it's time to reset the discussion. I think we went far from the topic.
What was the initial request? I even forgot it.

from entt.

skypjack avatar skypjack commented on May 10, 2024

Created a dedicated issue to discuss this (let me say) singleton mode for tags.
I'm closing this issue because I made available signals for components on experimental.
Thank you @mhaemmerle and all you guys for participating.

from entt.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.