Skip to content
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

Change SceneInstanceReady to trigger an observer. #13859

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion crates/bevy_scene/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ impl Plugin for ScenePlugin {
app.init_asset::<DynamicScene>()
.init_asset::<Scene>()
.init_asset_loader::<SceneLoader>()
.add_event::<SceneInstanceReady>()
.init_resource::<SceneSpawner>()
.add_systems(SpawnScene, (scene_spawner, scene_spawner_system).chain());

Expand Down
56 changes: 38 additions & 18 deletions crates/bevy_scene/src/scene_spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ use bevy_utils::{tracing::error, HashMap, HashSet};
use thiserror::Error;
use uuid::Uuid;

/// Emitted when [`crate::SceneInstance`] becomes ready to use.
/// Triggered on a scene's parent entity when [`crate::SceneInstance`] becomes ready to use.
///
/// See also [`SceneSpawner::instance_is_ready`].
/// See also [`Trigger`], [`SceneSpawner::instance_is_ready`].
///
/// [`Trigger`]: bevy_ecs::observer::Trigger
#[derive(Clone, Copy, Debug, Eq, PartialEq, Event)]
pub struct SceneInstanceReady {
Comment on lines 21 to 22
Copy link
Member

@mnmaita mnmaita Jun 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor observation (no pun intended 😅) unrelated to the PR changes:

I see that Trigger is defined like this: pub struct Trigger<'w, E, B: Bundle = ()>

Yet E is not required to be an Event. Wondering if this an oversight or was it on purpose? Would Trigger<SceneInstanceReady> still work if SceneInstanceReady doesn't derive Event?

World method signatures are taking generic params or function params like this:

pub fn observe<E: Event, B: Bundle, M>
pub fn trigger(&mut self, event: impl Event)

So maybe adding this constraint would be correct? I'll create a PR with said change but still wanted to write it down somewhere so I don't forget.

Copy link
Contributor Author

@komadori komadori Jun 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a related note: Event is just a marker trait. So, assuming that the EventReader<> functionality will continue to coexist with Trigger<>, I would have thought that "classic" events and observer events should use separate marker traits. I'm not sure what you'd name them though.

/// Entity to which the scene was spawned as a child.
pub parent: Entity,
/// Instance which has been spawned.
pub instance_id: InstanceId,
}

/// Information about a scene instance.
Expand Down Expand Up @@ -356,7 +358,10 @@ impl SceneSpawner {
}
}

world.send_event(SceneInstanceReady { parent });
// Defer via commands otherwise SceneSpawner is not available in the observer.
world
.commands()
.trigger_targets(SceneInstanceReady { instance_id }, parent);
} else {
self.scenes_with_parent.push((instance_id, parent));
}
Expand Down Expand Up @@ -439,7 +444,7 @@ pub fn scene_spawner_system(world: &mut World) {
mod tests {
use bevy_app::App;
use bevy_asset::{AssetPlugin, AssetServer};
use bevy_ecs::event::EventReader;
use bevy_ecs::observer::Trigger;
use bevy_ecs::prelude::ReflectComponent;
use bevy_ecs::query::With;
use bevy_ecs::system::{Commands, Res, ResMut, RunSystemOnce};
Expand Down Expand Up @@ -505,10 +510,14 @@ mod tests {
#[reflect(Component)]
struct ComponentA;

#[derive(Resource, Default)]
struct TriggerCount(u32);

#[test]
fn event() {
let mut app = App::new();
app.add_plugins((AssetPlugin::default(), ScenePlugin));
app.init_resource::<TriggerCount>();

app.register_type::<ComponentA>();
app.world_mut().spawn(ComponentA);
Expand All @@ -530,22 +539,33 @@ mod tests {
},
);

// Check for event arrival.
app.update();
app.world_mut().run_system_once(
move |mut ev_scene: EventReader<'_, '_, SceneInstanceReady>| {
let mut events = ev_scene.read();

// Add observer
app.world_mut().observe(
move |trigger: Trigger<SceneInstanceReady>,
scene_spawner: Res<SceneSpawner>,
mut trigger_count: ResMut<TriggerCount>| {
assert_eq!(
events.next().expect("found no `SceneInstanceReady` event"),
&SceneInstanceReady {
parent: scene_entity
},
"`SceneInstanceReady` contains the wrong parent entity"
trigger.entity(),
scene_entity,
"`SceneInstanceReady` triggered on the wrong parent entity"
);
assert!(
scene_spawner.instance_is_ready(trigger.event().instance_id),
"`InstanceId` is not ready"
);
assert!(events.next().is_none(), "found more than one event");
trigger_count.0 += 1;
},
);

// Check observer is triggered once.
app.update();
app.world_mut()
.run_system_once(|trigger_count: Res<TriggerCount>| {
assert_eq!(
trigger_count.0, 1,
"wrong number of `SceneInstanceReady` triggers"
);
});
}

#[test]
Expand Down