-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Open
Labels
A-ECSEntities, components, systems, and eventsEntities, components, systems, and eventsC-FeatureA new feature, making something new possibleA new feature, making something new possibleC-UsabilityA targeted quality-of-life change that makes Bevy easier to useA targeted quality-of-life change that makes Bevy easier to use
Description
What problem does this solve or what need does it fill?
In the current entity spawn process, hooks/observers for components that are added directly are executed in the order they written in code.
However, the hooks/observers of their require components are executed in a completely non-deterministic order.
What solution would you like?
It might be better to invoke hook/observer according to declaration order of the Require
components.
What alternative(s) have you considered?
There's no way to control the execution order of hooks/observers for require
components.
Additional context
use std::fmt::Debug;
use bevy::ecs::{component::Component, lifecycle::HookContext, world::{DeferredWorld, World}};
fn main() {
let mut world = World::default();
world.spawn(ABC);
world.spawn(GFEDCBA);
}
fn print_hook<T: Component + Debug>(
world: DeferredWorld,
HookContext { entity, ..}: HookContext,
){
println!("on_add {:?}",world.get::<T>(entity));
}
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<A>)]
struct A;
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<B>)]
struct B;
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<C>)]
struct C;
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<D>)]
struct D;
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<E>)]
struct E;
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<F>)]
struct F;
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<G>)]
struct G;
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<ABC>)]
#[require(A,B,C)]
struct ABC;
#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::<GFEDCBA>)]
#[require(G,F,E,D,C,B,A)]
struct GFEDCBA;
output:
on_add Some(ABC)
on_add Some(B)
on_add Some(C)
on_add Some(A)
on_add Some(GFEDCBA)
on_add Some(E)
on_add Some(B)
on_add Some(F)
on_add Some(C)
on_add Some(D)
on_add Some(G)
on_add Some(A)
bevy/crates/bevy_ecs/src/bundle.rs
Lines 543 to 555 in 1fb5a62
let required_components = required_components | |
.0 | |
.into_iter() | |
.map(|(component_id, v)| { | |
// Safety: These ids came out of the passed `components`, so they must be valid. | |
let info = unsafe { components.get_info_unchecked(component_id) }; | |
storages.prepare_component(info); | |
// This adds required components to the component_ids list _after_ using that list to remove explicitly provided | |
// components. This ordering is important! | |
component_ids.push(component_id); | |
v.constructor | |
}) | |
.collect(); |
The reason is that BundleInfo::component_ids
gets required component IDs from RequiredComponents
, which internally uses a HashMap
for storage.
I'm not sure if this is a bug, so I'm posting issue here.
Metadata
Metadata
Assignees
Labels
A-ECSEntities, components, systems, and eventsEntities, components, systems, and eventsC-FeatureA new feature, making something new possibleA new feature, making something new possibleC-UsabilityA targeted quality-of-life change that makes Bevy easier to useA targeted quality-of-life change that makes Bevy easier to use