bevy_bundled_observers/
lib.rs

1//! This crate provides the [`observers`] macro. See its documentation for further info.
2
3pub use bevy_ecs;
4use bevy_ecs::{component::HookContext, prelude::*, world::DeferredWorld};
5
6/// A macro for setting [`Observer`]s on an entity from within a [`Bundle`]. It is similar to the [`children`] macro, but for observers.
7///
8/// ```rust
9/// # use bevy::prelude::*;
10/// # use bevy_bundled_observers::observers;
11/// # #[derive(Event)] struct OnCollect;
12///
13/// fn coin() -> impl Bundle {
14///     (
15///         Name::new("Coin"),
16///         observers![|_: Trigger<OnCollect>| {
17///             info!("You collected a coin!");
18///         }],
19///     )
20/// }
21/// ```
22#[macro_export]
23macro_rules! observers {
24    [$($observer:expr),*$(,)?] => {
25       $crate::Observers(vec![$($crate::bevy_ecs::observer::Observer::new($observer)),*])
26    };
27}
28
29/// A component that sets observers on an entity when inserted. This is the underlying mechanism for the [`observers`] macro.
30///
31/// The component is immediately emptied and promptly removed after insertion.
32///
33/// The code example below shows what the [`observers`] macro expands to.
34///
35/// ```rust
36/// # use bevy::prelude::*;
37/// # use bevy_bundled_observers::Observers;
38/// # #[derive(Event)] struct OnCollect;
39///
40/// fn coin() -> impl Bundle {
41///     (
42///         Name::new("Coin"),
43///         Observers(vec![Observer::new(|_: Trigger<OnCollect>| {
44///             info!("You collected a coin!");
45///         })]),
46///     )
47/// }
48/// ```
49#[derive(Component)]
50#[component(on_insert = on_insert)]
51pub struct Observers(pub Vec<Observer>);
52
53fn on_insert(mut world: DeferredWorld, context: HookContext) {
54    let mut component: Mut<Observers> = world.get_mut(context.entity).unwrap();
55
56    let observers = core::mem::take(&mut component.0)
57        .into_iter()
58        .map(move |observer| observer.with_entity(context.entity));
59
60    let mut commands = world.commands();
61    commands.spawn_batch(observers);
62    commands.entity(context.entity).remove::<Observers>();
63}
64
65#[cfg(test)]
66mod test {
67    use bevy_ecs::prelude::*;
68
69    #[test]
70    fn test() {
71        #[derive(Event, Debug)]
72        struct OnFoo;
73
74        #[derive(Component, Debug, PartialEq, Eq)]
75        struct Bar(i32);
76
77        let mut world = World::new();
78
79        let entity = world
80            .spawn((
81                Bar(0),
82                observers![|trigger: Trigger<OnFoo>, mut query: Query<&mut Bar>| {
83                    query.get_mut(trigger.target()).unwrap().0 += 1;
84                }],
85            ))
86            .id();
87
88        world.trigger_targets(OnFoo, entity);
89
90        assert_eq!(world.get::<Bar>(entity), Some(&Bar(1)));
91    }
92}