-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Track spawn Tick
of entities, offer methods, query data SpawnDetails
and query filter Spawned
#19047
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I really like this idea: it's useful and consistent. That said, we should see how this affects spawning performance, both in the worst case and on more realistic cases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Over all, looks good. Just some minor questions, etc.
One thing we should consider is benching this. I have a feeling that having a separate vec for SpawnedOrDespawned
, separate from EntityMeta
would be faster. Almost every iteration cares about where the entity is. Asking when it was spawned and by who is more like a map thing IIUC. But this might be better as a separate PR.
Another option is making an observer and event for Spawned
. I feel like that's going to be the primary use of this, and exposing an observer might be better anyway for this use case. Then again, it makes sense to ask when an entity was spawned. Ex: This particle entity has been around for 100 ticks; despawn it. (Not that each particle should be an entity.) In other words, this might be a cheaper alternative to a timer component in some cases.
Anyway, that's my thoughts. I'm generally on board with this, provided the separate vecs and observer APIs are investigated (maybe in future prs).
Co-authored-by: Eagster <79881080+ElliottjPierce@users.noreply.github.com>
Co-authored-by: Eagster <79881080+ElliottjPierce@users.noreply.github.com>
Regarding introducing an event for observers, I considered it but I think the PR is big enough as is. I also imagine spawns triggering observers will have a more noticeable performance impact on all spawns than what I did here. |
…_spawn_despawn as a variant of Entities::set
…into track-spawn-tick
I did another change. Previously, However, since with this change the method is no longer noop even when you deactivate tracking locations, this always added to the number of accesses on So I removed that method entirely and added
When doing spawn benchmarks, this change greatly reduced the regressions from up to +6% down to +/-1%. I will do a separate post with benchmarks once I am sure I do them correctly. |
It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note. Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes. |
At minimum, you can probably just copy over your showcase section to a new file following the instructions posted above. This is super neat! |
I wrote release notes. I wanted to do it detailed, I hope not too much. rendered (up to date to following commits) I was also close to give a short recap that |
Oh these are perfect! Thank you!!! |
Made a sentence more clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good stuff! Thanks for the release note; it'll need an editing pass but capturing the motivation and usage is the key thing at this stage.
Merge conflicts, sorry! |
@alice-i-cecile resolved them. 👍 |
…ls` and query filter `Spawned` (bevyengine#19047) In my own project I was encountering the issue to find out which entities were spawned after applying commands. I began maintaining a vector of all entities with generational information before and after applying the command and diffing it. This was awfully complicated though and has no constant complexity but grows with the number of entities. Looking at `EntyMeta` it seemed obvious to me that struct can track the tick just as it does with `MaybeLocation`, updated from the same call. After that it became almost a given to also introduce query data `SpawnDetails` which offers methods to get the spawn tick and location, and query filter `Spawned` that filters entities out that were not spawned since the last run. I expanded a few tests and added new ones, though maybe I forgot a group of tests that should be extended too. I basically searched `bevy_ecs` for mentions of `Changed` and `Added` to see where the tests and docs are. Benchmarks of spawn/despawn can be found [here](bevyengine#19047 (comment)). --- From the added docs, systems with equal complexity since the filter is not archetypal: ```rs fn system1(q: Query<Entity, Spawned>) { for entity in &q { /* entity spawned */ } } fn system2(query: Query<(Entity, SpawnDetails)>) { for (entity, spawned) in &query { if spawned.is_spawned() { /* entity spawned */ } } } ``` `SpawnedDetails` has a few more methods: ```rs fn print_spawn_details(query: Query<(Entity, SpawnDetails)>) { for (entity, spawn_details) in &query { if spawn_details.is_spawned() { print!("new "); } println!( "entity {:?} spawned at {:?} by {:?}", entity, spawn_details.spawned_at(), spawn_details.spawned_by() ); } } ``` No public api was changed, I only added to it. That is why I added no migration guide. - query data `SpawnDetails` - query filter `Spawned` - method `Entities::entity_get_spawned_or_despawned_at` - method `EntityRef::spawned_at` - method `EntityMut::spawned_at` - method `EntityWorldMut::spawned_at` - method `UnsafeEntityCell::spawned_at` - method `FilteredEntityRef::spawned_at` - method `FilteredEntityMut::spawned_at` - method `EntityRefExcept::spawned_at` - method `EntityMutExcept::spawned_at` --------- Co-authored-by: Eagster <79881080+ElliottjPierce@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
…ls` and query filter `Spawned` (bevyengine#19047) # Objective In my own project I was encountering the issue to find out which entities were spawned after applying commands. I began maintaining a vector of all entities with generational information before and after applying the command and diffing it. This was awfully complicated though and has no constant complexity but grows with the number of entities. ## Solution Looking at `EntyMeta` it seemed obvious to me that struct can track the tick just as it does with `MaybeLocation`, updated from the same call. After that it became almost a given to also introduce query data `SpawnDetails` which offers methods to get the spawn tick and location, and query filter `Spawned` that filters entities out that were not spawned since the last run. ## Testing I expanded a few tests and added new ones, though maybe I forgot a group of tests that should be extended too. I basically searched `bevy_ecs` for mentions of `Changed` and `Added` to see where the tests and docs are. Benchmarks of spawn/despawn can be found [here](bevyengine#19047 (comment)). --- ## Showcase From the added docs, systems with equal complexity since the filter is not archetypal: ```rs fn system1(q: Query<Entity, Spawned>) { for entity in &q { /* entity spawned */ } } fn system2(query: Query<(Entity, SpawnDetails)>) { for (entity, spawned) in &query { if spawned.is_spawned() { /* entity spawned */ } } } ``` `SpawnedDetails` has a few more methods: ```rs fn print_spawn_details(query: Query<(Entity, SpawnDetails)>) { for (entity, spawn_details) in &query { if spawn_details.is_spawned() { print!("new "); } println!( "entity {:?} spawned at {:?} by {:?}", entity, spawn_details.spawned_at(), spawn_details.spawned_by() ); } } ``` ## Changes No public api was changed, I only added to it. That is why I added no migration guide. - query data `SpawnDetails` - query filter `Spawned` - method `Entities::entity_get_spawned_or_despawned_at` - method `EntityRef::spawned_at` - method `EntityMut::spawned_at` - method `EntityWorldMut::spawned_at` - method `UnsafeEntityCell::spawned_at` - method `FilteredEntityRef::spawned_at` - method `FilteredEntityMut::spawned_at` - method `EntityRefExcept::spawned_at` - method `EntityMutExcept::spawned_at` --------- Co-authored-by: Eagster <79881080+ElliottjPierce@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Objective
In my own project I was encountering the issue to find out which entities were spawned after applying commands. I began maintaining a vector of all entities with generational information before and after applying the command and diffing it. This was awfully complicated though and has no constant complexity but grows with the number of entities.
Solution
Looking at
EntyMeta
it seemed obvious to me that struct can track the tick just as it does withMaybeLocation
, updated from the same call. After that it became almost a given to also introduce query dataSpawnDetails
which offers methods to get the spawn tick and location, and query filterSpawned
that filters entities out that were not spawned since the last run.Testing
I expanded a few tests and added new ones, though maybe I forgot a group of tests that should be extended too. I basically searched
bevy_ecs
for mentions ofChanged
andAdded
to see where the tests and docs are.Benchmarks of spawn/despawn can be found here.
Showcase
From the added docs, systems with equal complexity since the filter is not archetypal:
SpawnedDetails
has a few more methods:Changes
No public api was changed, I only added to it. That is why I added no migration guide.
SpawnDetails
Spawned
Entities::entity_get_spawned_or_despawned_at
EntityRef::spawned_at
EntityMut::spawned_at
EntityWorldMut::spawned_at
UnsafeEntityCell::spawned_at
FilteredEntityRef::spawned_at
FilteredEntityMut::spawned_at
EntityRefExcept::spawned_at
EntityMutExcept::spawned_at