1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
//! Insert components on loaded scenes.
use bevy::{ecs::system::EntityCommands, ecs::world::EntityRef, prelude::*, scene::InstanceId};
/// Marker Component for scenes that were succesfully hooked.
#[derive(Component)]
#[non_exhaustive]
pub struct SceneLoaded;
/// Add this as a component to any entity to trigger `hook`'s
/// [`Hook::hook_entity`] method when the scene is loaded.
#[derive(Component)]
pub struct SceneHook {
instance: InstanceId,
hook: Box<dyn Hook>,
}
impl SceneHook {
pub(crate) fn new<T: Hook>(instance: InstanceId, hook: T) -> Self {
let hook = Box::new(hook);
Self { instance, hook }
}
pub(crate) fn new_comp<C, F>(instance: InstanceId, hook: F) -> Self
where
F: Fn(&C, &mut EntityCommands) + Send + Sync + 'static,
C: Component,
{
let hook = move |e: &EntityRef, cmds: &mut EntityCommands| match e.get::<C>() {
Some(comp) => hook(comp, cmds),
None => {}
};
Self::new(instance, hook)
}
}
/// Handle adding components to entites named in a loaded scene.
///
/// # Example
///
/// ```rust
/// use bevy::prelude::*;
/// use bevy::ecs::{world::EntityRef, system::EntityCommands};
/// use bevy_scene_hook::{Hook, HookingSceneSpawner};
/// # type DeckData = Scene;
/// # type Image = Scene;
///
/// #[derive(Component)]
/// struct CardNo(usize);
///
/// #[derive(Clone)]
/// struct DeckAssets { player: Handle<DeckData>, oppo: Handle<DeckData> }
///
/// #[derive(Clone)]
/// struct CardAssets {
/// front_faces: Vec<Handle<Image>>,
/// back_face: Handle<Image>,
/// }
/// struct SceneAssets {
/// deck: DeckAssets,
/// cards: CardAssets,
/// }
/// impl Hook for SceneAssets {
/// fn hook_entity(&self, entity: &EntityRef, cmds: &mut EntityCommands) {
/// match entity.get::<Name>().map(|t|t.as_str()) {
/// Some("PlayerDeck") => cmds.insert(self.deck.player.clone_weak()),
/// Some("OppoDeck") => cmds.insert(self.deck.oppo.clone_weak()),
/// Some("Card") => {
/// let card_no = entity.get::<CardNo>().unwrap();
/// cmds.insert(self.cards.front_faces[card_no.0].clone_weak())
/// }
/// _ => cmds,
/// };
/// }
/// }
/// fn load_scene(
/// mut scene_spawner: HookingSceneSpawner,
/// asset_server: Res<AssetServer>,
/// deck_assets: Res<DeckAssets>,
/// cards_assets: Res<CardAssets>,
/// ) {
/// let loader = SceneAssets {
/// deck: deck_assets.clone(),
/// cards: cards_assets.clone(),
/// };
/// scene_spawner.with_hook(asset_server.load("scene.glb#Scene0"), loader);
/// }
/// ```
pub trait Hook: Send + Sync + 'static {
/// Add [`Component`]s or do anything with entity in the spawned scene
/// refered by `entity_ref`.
///
/// This runs once for all entities in the spawned scene, once loaded.
fn hook_entity(&self, entity_ref: &EntityRef, commands: &mut EntityCommands);
}
pub fn run_hooks(
unloaded_instances: Query<(Entity, &SceneHook), Without<SceneLoaded>>,
scene_manager: Res<SceneSpawner>,
world: &World,
mut cmds: Commands,
) {
for (entity, hooked) in unloaded_instances.iter() {
if let Some(entities) = scene_manager.iter_instance_entities(hooked.instance) {
for entity_ref in entities.filter_map(|e| world.get_entity(e)) {
let mut cmd = cmds.entity(entity_ref.id());
hooked.hook.hook_entity(&entity_ref, &mut cmd);
}
cmds.entity(entity).insert(SceneLoaded);
}
}
}
impl<F: Fn(&EntityRef, &mut EntityCommands) + Send + Sync + 'static> Hook for F {
fn hook_entity(&self, entity_ref: &EntityRef, commands: &mut EntityCommands) {
(self)(entity_ref, commands)
}
}