Remove entity placeholder from observers (#19440)
# Objective `Entity::PLACEHOLDER` acts as a magic number that will *probably* never really exist, but it certainly could. And, `Entity` has a niche, so the only reason to use `PLACEHOLDER` is as an alternative to `MaybeUninit` that trades safety risks for logic risks. As a result, bevy has generally advised against using `PLACEHOLDER`, but we still use if for a lot internally. This pr starts removing internal uses of it, starting from observers. ## Solution Change all trigger target related types from `Entity` to `Option<Entity>` Small migration guide to come. ## Testing CI ## Future Work This turned a lot of code from ```rust trigger.target() ``` to ```rust trigger.target().unwrap() ``` The extra panic is no worse than before; it's just earlier than panicking after passing the placeholder to something else. But this is kinda annoying. I would like to add a `TriggerMode` or something to `Event` that would restrict what kinds of targets can be used for that event. Many events like `Removed` etc, are always triggered with a target. We can make those have a way to assume Some, etc. But I wanted to save that for a future pr.
This commit is contained in:
parent
860ff7819b
commit
064e5e48b4
@ -340,8 +340,8 @@ let mut world = World::new();
|
|||||||
let entity = world.spawn_empty().id();
|
let entity = world.spawn_empty().id();
|
||||||
|
|
||||||
world.add_observer(|trigger: Trigger<Explode>, mut commands: Commands| {
|
world.add_observer(|trigger: Trigger<Explode>, mut commands: Commands| {
|
||||||
println!("Entity {} goes BOOM!", trigger.target());
|
println!("Entity {} goes BOOM!", trigger.target().unwrap());
|
||||||
commands.entity(trigger.target()).despawn();
|
commands.entity(trigger.target().unwrap()).despawn();
|
||||||
});
|
});
|
||||||
|
|
||||||
world.flush();
|
world.flush();
|
||||||
|
@ -1143,7 +1143,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
if archetype.has_replace_observer() {
|
if archetype.has_replace_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_REPLACE,
|
ON_REPLACE,
|
||||||
entity,
|
Some(entity),
|
||||||
archetype_after_insert.iter_existing(),
|
archetype_after_insert.iter_existing(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -1328,7 +1328,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
if new_archetype.has_add_observer() {
|
if new_archetype.has_add_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_ADD,
|
ON_ADD,
|
||||||
entity,
|
Some(entity),
|
||||||
archetype_after_insert.iter_added(),
|
archetype_after_insert.iter_added(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -1346,7 +1346,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
if new_archetype.has_insert_observer() {
|
if new_archetype.has_insert_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_INSERT,
|
ON_INSERT,
|
||||||
entity,
|
Some(entity),
|
||||||
archetype_after_insert.iter_inserted(),
|
archetype_after_insert.iter_inserted(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -1365,7 +1365,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
if new_archetype.has_insert_observer() {
|
if new_archetype.has_insert_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_INSERT,
|
ON_INSERT,
|
||||||
entity,
|
Some(entity),
|
||||||
archetype_after_insert.iter_added(),
|
archetype_after_insert.iter_added(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -1519,7 +1519,7 @@ impl<'w> BundleRemover<'w> {
|
|||||||
if self.old_archetype.as_ref().has_replace_observer() {
|
if self.old_archetype.as_ref().has_replace_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_REPLACE,
|
ON_REPLACE,
|
||||||
entity,
|
Some(entity),
|
||||||
bundle_components_in_archetype(),
|
bundle_components_in_archetype(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -1534,7 +1534,7 @@ impl<'w> BundleRemover<'w> {
|
|||||||
if self.old_archetype.as_ref().has_remove_observer() {
|
if self.old_archetype.as_ref().has_remove_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_REMOVE,
|
ON_REMOVE,
|
||||||
entity,
|
Some(entity),
|
||||||
bundle_components_in_archetype(),
|
bundle_components_in_archetype(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -1785,7 +1785,7 @@ impl<'w> BundleSpawner<'w> {
|
|||||||
if archetype.has_add_observer() {
|
if archetype.has_add_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_ADD,
|
ON_ADD,
|
||||||
entity,
|
Some(entity),
|
||||||
bundle_info.iter_contributed_components(),
|
bundle_info.iter_contributed_components(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -1800,7 +1800,7 @@ impl<'w> BundleSpawner<'w> {
|
|||||||
if archetype.has_insert_observer() {
|
if archetype.has_insert_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_INSERT,
|
ON_INSERT,
|
||||||
entity,
|
Some(entity),
|
||||||
bundle_info.iter_contributed_components(),
|
bundle_info.iter_contributed_components(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
|
@ -68,7 +68,7 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. It may
|
/// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. It may
|
||||||
/// be [`Entity::PLACEHOLDER`].
|
/// be [`None`] if the trigger is not for a particular entity.
|
||||||
///
|
///
|
||||||
/// Observable events can target specific entities. When those events fire, they will trigger
|
/// Observable events can target specific entities. When those events fire, they will trigger
|
||||||
/// any observers on the targeted entities. In this case, the `target()` and `observer()` are
|
/// any observers on the targeted entities. In this case, the `target()` and `observer()` are
|
||||||
@ -81,7 +81,7 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> {
|
|||||||
///
|
///
|
||||||
/// This is an important distinction: the entity reacting to an event is not always the same as
|
/// This is an important distinction: the entity reacting to an event is not always the same as
|
||||||
/// the entity triggered by the event.
|
/// the entity triggered by the event.
|
||||||
pub fn target(&self) -> Entity {
|
pub fn target(&self) -> Option<Entity> {
|
||||||
self.trigger.target
|
self.trigger.target
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,7 +341,7 @@ pub struct ObserverTrigger {
|
|||||||
/// The [`ComponentId`]s the trigger targeted.
|
/// The [`ComponentId`]s the trigger targeted.
|
||||||
components: SmallVec<[ComponentId; 2]>,
|
components: SmallVec<[ComponentId; 2]>,
|
||||||
/// The entity the trigger targeted.
|
/// The entity the trigger targeted.
|
||||||
pub target: Entity,
|
pub target: Option<Entity>,
|
||||||
/// The location of the source code that triggered the observer.
|
/// The location of the source code that triggered the observer.
|
||||||
pub caller: MaybeLocation,
|
pub caller: MaybeLocation,
|
||||||
}
|
}
|
||||||
@ -416,7 +416,7 @@ impl Observers {
|
|||||||
pub(crate) fn invoke<T>(
|
pub(crate) fn invoke<T>(
|
||||||
mut world: DeferredWorld,
|
mut world: DeferredWorld,
|
||||||
event_type: ComponentId,
|
event_type: ComponentId,
|
||||||
target: Entity,
|
target: Option<Entity>,
|
||||||
components: impl Iterator<Item = ComponentId> + Clone,
|
components: impl Iterator<Item = ComponentId> + Clone,
|
||||||
data: &mut T,
|
data: &mut T,
|
||||||
propagate: &mut bool,
|
propagate: &mut bool,
|
||||||
@ -455,8 +455,8 @@ impl Observers {
|
|||||||
observers.map.iter().for_each(&mut trigger_observer);
|
observers.map.iter().for_each(&mut trigger_observer);
|
||||||
|
|
||||||
// Trigger entity observers listening for this kind of trigger
|
// Trigger entity observers listening for this kind of trigger
|
||||||
if target != Entity::PLACEHOLDER {
|
if let Some(target_entity) = target {
|
||||||
if let Some(map) = observers.entity_observers.get(&target) {
|
if let Some(map) = observers.entity_observers.get(&target_entity) {
|
||||||
map.iter().for_each(&mut trigger_observer);
|
map.iter().for_each(&mut trigger_observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -469,8 +469,8 @@ impl Observers {
|
|||||||
.iter()
|
.iter()
|
||||||
.for_each(&mut trigger_observer);
|
.for_each(&mut trigger_observer);
|
||||||
|
|
||||||
if target != Entity::PLACEHOLDER {
|
if let Some(target_entity) = target {
|
||||||
if let Some(map) = component_observers.entity_map.get(&target) {
|
if let Some(map) = component_observers.entity_map.get(&target_entity) {
|
||||||
map.iter().for_each(&mut trigger_observer);
|
map.iter().for_each(&mut trigger_observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -695,7 +695,7 @@ impl World {
|
|||||||
unsafe {
|
unsafe {
|
||||||
world.trigger_observers_with_data::<_, E::Traversal>(
|
world.trigger_observers_with_data::<_, E::Traversal>(
|
||||||
event_id,
|
event_id,
|
||||||
Entity::PLACEHOLDER,
|
None,
|
||||||
targets.components(),
|
targets.components(),
|
||||||
event_data,
|
event_data,
|
||||||
false,
|
false,
|
||||||
@ -708,7 +708,7 @@ impl World {
|
|||||||
unsafe {
|
unsafe {
|
||||||
world.trigger_observers_with_data::<_, E::Traversal>(
|
world.trigger_observers_with_data::<_, E::Traversal>(
|
||||||
event_id,
|
event_id,
|
||||||
target_entity,
|
Some(target_entity),
|
||||||
targets.components(),
|
targets.components(),
|
||||||
event_data,
|
event_data,
|
||||||
E::AUTO_PROPAGATE,
|
E::AUTO_PROPAGATE,
|
||||||
@ -999,20 +999,20 @@ mod tests {
|
|||||||
world.add_observer(
|
world.add_observer(
|
||||||
|obs: Trigger<OnAdd, A>, mut res: ResMut<Order>, mut commands: Commands| {
|
|obs: Trigger<OnAdd, A>, mut res: ResMut<Order>, mut commands: Commands| {
|
||||||
res.observed("add_a");
|
res.observed("add_a");
|
||||||
commands.entity(obs.target()).insert(B);
|
commands.entity(obs.target().unwrap()).insert(B);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
world.add_observer(
|
world.add_observer(
|
||||||
|obs: Trigger<OnRemove, A>, mut res: ResMut<Order>, mut commands: Commands| {
|
|obs: Trigger<OnRemove, A>, mut res: ResMut<Order>, mut commands: Commands| {
|
||||||
res.observed("remove_a");
|
res.observed("remove_a");
|
||||||
commands.entity(obs.target()).remove::<B>();
|
commands.entity(obs.target().unwrap()).remove::<B>();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
world.add_observer(
|
world.add_observer(
|
||||||
|obs: Trigger<OnAdd, B>, mut res: ResMut<Order>, mut commands: Commands| {
|
|obs: Trigger<OnAdd, B>, mut res: ResMut<Order>, mut commands: Commands| {
|
||||||
res.observed("add_b");
|
res.observed("add_b");
|
||||||
commands.entity(obs.target()).remove::<A>();
|
commands.entity(obs.target().unwrap()).remove::<A>();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
world.add_observer(|_: Trigger<OnRemove, B>, mut res: ResMut<Order>| {
|
world.add_observer(|_: Trigger<OnRemove, B>, mut res: ResMut<Order>| {
|
||||||
@ -1181,7 +1181,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
world.spawn_empty().observe(system);
|
world.spawn_empty().observe(system);
|
||||||
world.add_observer(move |obs: Trigger<EventA>, mut res: ResMut<Order>| {
|
world.add_observer(move |obs: Trigger<EventA>, mut res: ResMut<Order>| {
|
||||||
assert_eq!(obs.target(), Entity::PLACEHOLDER);
|
assert_eq!(obs.target(), None);
|
||||||
res.observed("event_a");
|
res.observed("event_a");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1208,7 +1208,7 @@ mod tests {
|
|||||||
.observe(|_: Trigger<EventA>, mut res: ResMut<Order>| res.observed("a_1"))
|
.observe(|_: Trigger<EventA>, mut res: ResMut<Order>| res.observed("a_1"))
|
||||||
.id();
|
.id();
|
||||||
world.add_observer(move |obs: Trigger<EventA>, mut res: ResMut<Order>| {
|
world.add_observer(move |obs: Trigger<EventA>, mut res: ResMut<Order>| {
|
||||||
assert_eq!(obs.target(), entity);
|
assert_eq!(obs.target().unwrap(), entity);
|
||||||
res.observed("a_2");
|
res.observed("a_2");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1628,7 +1628,7 @@ mod tests {
|
|||||||
|
|
||||||
world.add_observer(
|
world.add_observer(
|
||||||
|trigger: Trigger<EventPropagating>, query: Query<&A>, mut res: ResMut<Order>| {
|
|trigger: Trigger<EventPropagating>, query: Query<&A>, mut res: ResMut<Order>| {
|
||||||
if query.get(trigger.target()).is_ok() {
|
if query.get(trigger.target().unwrap()).is_ok() {
|
||||||
res.observed("event");
|
res.observed("event");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1651,7 +1651,7 @@ mod tests {
|
|||||||
fn observer_modifies_relationship() {
|
fn observer_modifies_relationship() {
|
||||||
fn on_add(trigger: Trigger<OnAdd, A>, mut commands: Commands) {
|
fn on_add(trigger: Trigger<OnAdd, A>, mut commands: Commands) {
|
||||||
commands
|
commands
|
||||||
.entity(trigger.target())
|
.entity(trigger.target().unwrap())
|
||||||
.with_related_entities::<crate::hierarchy::ChildOf>(|rsc| {
|
.with_related_entities::<crate::hierarchy::ChildOf>(|rsc| {
|
||||||
rsc.spawn_empty();
|
rsc.spawn_empty();
|
||||||
});
|
});
|
||||||
|
@ -123,8 +123,8 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
|
|||||||
/// struct Explode;
|
/// struct Explode;
|
||||||
///
|
///
|
||||||
/// world.add_observer(|trigger: Trigger<Explode>, mut commands: Commands| {
|
/// world.add_observer(|trigger: Trigger<Explode>, mut commands: Commands| {
|
||||||
/// println!("Entity {} goes BOOM!", trigger.target());
|
/// println!("Entity {} goes BOOM!", trigger.target().unwrap());
|
||||||
/// commands.entity(trigger.target()).despawn();
|
/// commands.entity(trigger.target().unwrap()).despawn();
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
/// world.flush();
|
/// world.flush();
|
||||||
@ -157,7 +157,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
|
|||||||
/// # struct Explode;
|
/// # struct Explode;
|
||||||
/// world.entity_mut(e1).observe(|trigger: Trigger<Explode>, mut commands: Commands| {
|
/// world.entity_mut(e1).observe(|trigger: Trigger<Explode>, mut commands: Commands| {
|
||||||
/// println!("Boom!");
|
/// println!("Boom!");
|
||||||
/// commands.entity(trigger.target()).despawn();
|
/// commands.entity(trigger.target().unwrap()).despawn();
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
/// world.entity_mut(e2).observe(|trigger: Trigger<Explode>, mut commands: Commands| {
|
/// world.entity_mut(e2).observe(|trigger: Trigger<Explode>, mut commands: Commands| {
|
||||||
|
@ -23,7 +23,7 @@ use super::{unsafe_world_cell::UnsafeWorldCell, Mut, World, ON_INSERT, ON_REPLAC
|
|||||||
///
|
///
|
||||||
/// This means that in order to add entities, for example, you will need to use commands instead of the world directly.
|
/// This means that in order to add entities, for example, you will need to use commands instead of the world directly.
|
||||||
pub struct DeferredWorld<'w> {
|
pub struct DeferredWorld<'w> {
|
||||||
// SAFETY: Implementors must not use this reference to make structural changes
|
// SAFETY: Implementers must not use this reference to make structural changes
|
||||||
world: UnsafeWorldCell<'w>,
|
world: UnsafeWorldCell<'w>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
if archetype.has_replace_observer() {
|
if archetype.has_replace_observer() {
|
||||||
self.trigger_observers(
|
self.trigger_observers(
|
||||||
ON_REPLACE,
|
ON_REPLACE,
|
||||||
entity,
|
Some(entity),
|
||||||
[component_id].into_iter(),
|
[component_id].into_iter(),
|
||||||
MaybeLocation::caller(),
|
MaybeLocation::caller(),
|
||||||
);
|
);
|
||||||
@ -197,7 +197,7 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
if archetype.has_insert_observer() {
|
if archetype.has_insert_observer() {
|
||||||
self.trigger_observers(
|
self.trigger_observers(
|
||||||
ON_INSERT,
|
ON_INSERT,
|
||||||
entity,
|
Some(entity),
|
||||||
[component_id].into_iter(),
|
[component_id].into_iter(),
|
||||||
MaybeLocation::caller(),
|
MaybeLocation::caller(),
|
||||||
);
|
);
|
||||||
@ -738,7 +738,7 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
pub(crate) unsafe fn trigger_observers(
|
pub(crate) unsafe fn trigger_observers(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: ComponentId,
|
event: ComponentId,
|
||||||
target: Entity,
|
target: Option<Entity>,
|
||||||
components: impl Iterator<Item = ComponentId> + Clone,
|
components: impl Iterator<Item = ComponentId> + Clone,
|
||||||
caller: MaybeLocation,
|
caller: MaybeLocation,
|
||||||
) {
|
) {
|
||||||
@ -761,7 +761,7 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
pub(crate) unsafe fn trigger_observers_with_data<E, T>(
|
pub(crate) unsafe fn trigger_observers_with_data<E, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: ComponentId,
|
event: ComponentId,
|
||||||
mut target: Entity,
|
target: Option<Entity>,
|
||||||
components: impl Iterator<Item = ComponentId> + Clone,
|
components: impl Iterator<Item = ComponentId> + Clone,
|
||||||
data: &mut E,
|
data: &mut E,
|
||||||
mut propagate: bool,
|
mut propagate: bool,
|
||||||
@ -769,18 +769,20 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
) where
|
) where
|
||||||
T: Traversal<E>,
|
T: Traversal<E>,
|
||||||
{
|
{
|
||||||
|
Observers::invoke::<_>(
|
||||||
|
self.reborrow(),
|
||||||
|
event,
|
||||||
|
target,
|
||||||
|
components.clone(),
|
||||||
|
data,
|
||||||
|
&mut propagate,
|
||||||
|
caller,
|
||||||
|
);
|
||||||
|
let Some(mut target) = target else { return };
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
Observers::invoke::<_>(
|
|
||||||
self.reborrow(),
|
|
||||||
event,
|
|
||||||
target,
|
|
||||||
components.clone(),
|
|
||||||
data,
|
|
||||||
&mut propagate,
|
|
||||||
caller,
|
|
||||||
);
|
|
||||||
if !propagate {
|
if !propagate {
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(traverse_to) = self
|
if let Some(traverse_to) = self
|
||||||
.get_entity(target)
|
.get_entity(target)
|
||||||
@ -792,6 +794,15 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Observers::invoke::<_>(
|
||||||
|
self.reborrow(),
|
||||||
|
event,
|
||||||
|
Some(target),
|
||||||
|
components.clone(),
|
||||||
|
data,
|
||||||
|
&mut propagate,
|
||||||
|
caller,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2371,7 +2371,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
if archetype.has_despawn_observer() {
|
if archetype.has_despawn_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_DESPAWN,
|
ON_DESPAWN,
|
||||||
self.entity,
|
Some(self.entity),
|
||||||
archetype.components(),
|
archetype.components(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -2385,7 +2385,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
if archetype.has_replace_observer() {
|
if archetype.has_replace_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_REPLACE,
|
ON_REPLACE,
|
||||||
self.entity,
|
Some(self.entity),
|
||||||
archetype.components(),
|
archetype.components(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -2400,7 +2400,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
if archetype.has_remove_observer() {
|
if archetype.has_remove_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_REMOVE,
|
ON_REMOVE,
|
||||||
self.entity,
|
Some(self.entity),
|
||||||
archetype.components(),
|
archetype.components(),
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
@ -5749,7 +5749,9 @@ mod tests {
|
|||||||
let entity = world
|
let entity = world
|
||||||
.spawn_empty()
|
.spawn_empty()
|
||||||
.observe(|trigger: Trigger<TestEvent>, mut commands: Commands| {
|
.observe(|trigger: Trigger<TestEvent>, mut commands: Commands| {
|
||||||
commands.entity(trigger.target()).insert(TestComponent(0));
|
commands
|
||||||
|
.entity(trigger.target().unwrap())
|
||||||
|
.insert(TestComponent(0));
|
||||||
})
|
})
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
@ -5769,7 +5771,7 @@ mod tests {
|
|||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.add_observer(
|
world.add_observer(
|
||||||
|trigger: Trigger<OnAdd, TestComponent>, mut commands: Commands| {
|
|trigger: Trigger<OnAdd, TestComponent>, mut commands: Commands| {
|
||||||
commands.entity(trigger.target()).despawn();
|
commands.entity(trigger.target().unwrap()).despawn();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let entity = world.spawn_empty().id();
|
let entity = world.spawn_empty().id();
|
||||||
|
@ -394,7 +394,7 @@ mod tests {
|
|||||||
trigger: Trigger<FocusedInput<KeyboardInput>>,
|
trigger: Trigger<FocusedInput<KeyboardInput>>,
|
||||||
mut query: Query<&mut GatherKeyboardEvents>,
|
mut query: Query<&mut GatherKeyboardEvents>,
|
||||||
) {
|
) {
|
||||||
if let Ok(mut gather) = query.get_mut(trigger.target()) {
|
if let Ok(mut gather) = query.get_mut(trigger.target().unwrap()) {
|
||||||
if let Key::Character(c) = &trigger.input.logical_key {
|
if let Key::Character(c) = &trigger.input.logical_key {
|
||||||
gather.0.push_str(c.as_str());
|
gather.0.push_str(c.as_str());
|
||||||
}
|
}
|
||||||
|
@ -551,7 +551,7 @@ pub(crate) fn add_light_view_entities(
|
|||||||
trigger: Trigger<OnAdd, (ExtractedDirectionalLight, ExtractedPointLight)>,
|
trigger: Trigger<OnAdd, (ExtractedDirectionalLight, ExtractedPointLight)>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
if let Ok(mut v) = commands.get_entity(trigger.target()) {
|
if let Ok(mut v) = commands.get_entity(trigger.target().unwrap()) {
|
||||||
v.insert(LightViewEntities::default());
|
v.insert(LightViewEntities::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -561,7 +561,7 @@ pub(crate) fn extracted_light_removed(
|
|||||||
trigger: Trigger<OnRemove, (ExtractedDirectionalLight, ExtractedPointLight)>,
|
trigger: Trigger<OnRemove, (ExtractedDirectionalLight, ExtractedPointLight)>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
if let Ok(mut v) = commands.get_entity(trigger.target()) {
|
if let Ok(mut v) = commands.get_entity(trigger.target().unwrap()) {
|
||||||
v.try_remove::<LightViewEntities>();
|
v.try_remove::<LightViewEntities>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -571,7 +571,7 @@ pub(crate) fn remove_light_view_entities(
|
|||||||
query: Query<&LightViewEntities>,
|
query: Query<&LightViewEntities>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
if let Ok(entities) = query.get(trigger.target()) {
|
if let Ok(entities) = query.get(trigger.target().unwrap()) {
|
||||||
for v in entities.0.values() {
|
for v in entities.0.values() {
|
||||||
for e in v.iter().copied() {
|
for e in v.iter().copied() {
|
||||||
if let Ok(mut v) = commands.get_entity(e) {
|
if let Ok(mut v) = commands.get_entity(e) {
|
||||||
|
@ -55,13 +55,13 @@
|
|||||||
//! // Spawn your entity here, e.g. a Mesh.
|
//! // Spawn your entity here, e.g. a Mesh.
|
||||||
//! // When dragged, mutate the `Transform` component on the dragged target entity:
|
//! // When dragged, mutate the `Transform` component on the dragged target entity:
|
||||||
//! .observe(|trigger: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>| {
|
//! .observe(|trigger: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>| {
|
||||||
//! let mut transform = transforms.get_mut(trigger.target()).unwrap();
|
//! let mut transform = transforms.get_mut(trigger.target().unwrap()).unwrap();
|
||||||
//! let drag = trigger.event();
|
//! let drag = trigger.event();
|
||||||
//! transform.rotate_local_y(drag.delta.x / 50.0);
|
//! transform.rotate_local_y(drag.delta.x / 50.0);
|
||||||
//! })
|
//! })
|
||||||
//! .observe(|trigger: Trigger<Pointer<Click>>, mut commands: Commands| {
|
//! .observe(|trigger: Trigger<Pointer<Click>>, mut commands: Commands| {
|
||||||
//! println!("Entity {} goes BOOM!", trigger.target());
|
//! println!("Entity {} goes BOOM!", trigger.target().unwrap());
|
||||||
//! commands.entity(trigger.target()).despawn();
|
//! commands.entity(trigger.target().unwrap()).despawn();
|
||||||
//! })
|
//! })
|
||||||
//! .observe(|trigger: Trigger<Pointer<Over>>, mut events: EventWriter<Greeting>| {
|
//! .observe(|trigger: Trigger<Pointer<Over>>, mut events: EventWriter<Greeting>| {
|
||||||
//! events.write(Greeting);
|
//! events.write(Greeting);
|
||||||
|
@ -94,14 +94,14 @@ impl Plugin for SyncWorldPlugin {
|
|||||||
app.init_resource::<PendingSyncEntity>();
|
app.init_resource::<PendingSyncEntity>();
|
||||||
app.add_observer(
|
app.add_observer(
|
||||||
|trigger: Trigger<OnAdd, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
|
|trigger: Trigger<OnAdd, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
|
||||||
pending.push(EntityRecord::Added(trigger.target()));
|
pending.push(EntityRecord::Added(trigger.target().unwrap()));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
app.add_observer(
|
app.add_observer(
|
||||||
|trigger: Trigger<OnRemove, SyncToRenderWorld>,
|
|trigger: Trigger<OnRemove, SyncToRenderWorld>,
|
||||||
mut pending: ResMut<PendingSyncEntity>,
|
mut pending: ResMut<PendingSyncEntity>,
|
||||||
query: Query<&RenderEntity>| {
|
query: Query<&RenderEntity>| {
|
||||||
if let Ok(e) = query.get(trigger.target()) {
|
if let Ok(e) = query.get(trigger.target().unwrap()) {
|
||||||
pending.push(EntityRecord::Removed(*e));
|
pending.push(EntityRecord::Removed(*e));
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -512,14 +512,14 @@ mod tests {
|
|||||||
|
|
||||||
main_world.add_observer(
|
main_world.add_observer(
|
||||||
|trigger: Trigger<OnAdd, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
|
|trigger: Trigger<OnAdd, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
|
||||||
pending.push(EntityRecord::Added(trigger.target()));
|
pending.push(EntityRecord::Added(trigger.target().unwrap()));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
main_world.add_observer(
|
main_world.add_observer(
|
||||||
|trigger: Trigger<OnRemove, SyncToRenderWorld>,
|
|trigger: Trigger<OnRemove, SyncToRenderWorld>,
|
||||||
mut pending: ResMut<PendingSyncEntity>,
|
mut pending: ResMut<PendingSyncEntity>,
|
||||||
query: Query<&RenderEntity>| {
|
query: Query<&RenderEntity>| {
|
||||||
if let Ok(e) = query.get(trigger.target()) {
|
if let Ok(e) = query.get(trigger.target().unwrap()) {
|
||||||
pending.push(EntityRecord::Removed(*e));
|
pending.push(EntityRecord::Removed(*e));
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -721,7 +721,7 @@ mod tests {
|
|||||||
.expect("Failed to run dynamic scene builder system.")
|
.expect("Failed to run dynamic scene builder system.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Entity) {
|
fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Option<Entity>) {
|
||||||
// Add observer
|
// Add observer
|
||||||
app.world_mut().add_observer(
|
app.world_mut().add_observer(
|
||||||
move |trigger: Trigger<SceneInstanceReady>,
|
move |trigger: Trigger<SceneInstanceReady>,
|
||||||
@ -773,7 +773,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Check trigger.
|
// Check trigger.
|
||||||
observe_trigger(&mut app, scene_id, Entity::PLACEHOLDER);
|
observe_trigger(&mut app, scene_id, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -792,7 +792,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Check trigger.
|
// Check trigger.
|
||||||
observe_trigger(&mut app, scene_id, Entity::PLACEHOLDER);
|
observe_trigger(&mut app, scene_id, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -816,7 +816,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Check trigger.
|
// Check trigger.
|
||||||
observe_trigger(&mut app, scene_id, scene_entity);
|
observe_trigger(&mut app, scene_id, Some(scene_entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -840,7 +840,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Check trigger.
|
// Check trigger.
|
||||||
observe_trigger(&mut app, scene_id, scene_entity);
|
observe_trigger(&mut app, scene_id, Some(scene_entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -194,7 +194,7 @@ fn update_cursors(
|
|||||||
fn on_remove_cursor_icon(trigger: Trigger<OnRemove, CursorIcon>, mut commands: Commands) {
|
fn on_remove_cursor_icon(trigger: Trigger<OnRemove, CursorIcon>, mut commands: Commands) {
|
||||||
// Use `try_insert` to avoid panic if the window is being destroyed.
|
// Use `try_insert` to avoid panic if the window is being destroyed.
|
||||||
commands
|
commands
|
||||||
.entity(trigger.target())
|
.entity(trigger.target().unwrap())
|
||||||
.try_insert(PendingCursor(Some(CursorSource::System(
|
.try_insert(PendingCursor(Some(CursorSource::System(
|
||||||
convert_system_cursor_icon(SystemCursorIcon::Default),
|
convert_system_cursor_icon(SystemCursorIcon::Default),
|
||||||
))));
|
))));
|
||||||
|
@ -65,12 +65,12 @@ fn change_material(
|
|||||||
mut asset_materials: ResMut<Assets<StandardMaterial>>,
|
mut asset_materials: ResMut<Assets<StandardMaterial>>,
|
||||||
) {
|
) {
|
||||||
// Get the `ColorOverride` of the entity, if it does not have a color override, skip
|
// Get the `ColorOverride` of the entity, if it does not have a color override, skip
|
||||||
let Ok(color_override) = color_override.get(trigger.target()) else {
|
let Ok(color_override) = color_override.get(trigger.target().unwrap()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterate over all children recursively
|
// Iterate over all children recursively
|
||||||
for descendants in children.iter_descendants(trigger.target()) {
|
for descendants in children.iter_descendants(trigger.target().unwrap()) {
|
||||||
// Get the material of the descendant
|
// Get the material of the descendant
|
||||||
if let Some(material) = mesh_materials
|
if let Some(material) = mesh_materials
|
||||||
.get(descendants)
|
.get(descendants)
|
||||||
|
@ -70,12 +70,12 @@ fn play_animation_when_ready(
|
|||||||
) {
|
) {
|
||||||
// The entity we spawned in `setup_mesh_and_animation` is the trigger's target.
|
// The entity we spawned in `setup_mesh_and_animation` is the trigger's target.
|
||||||
// Start by finding the AnimationToPlay component we added to that entity.
|
// Start by finding the AnimationToPlay component we added to that entity.
|
||||||
if let Ok(animation_to_play) = animations_to_play.get(trigger.target()) {
|
if let Ok(animation_to_play) = animations_to_play.get(trigger.target().unwrap()) {
|
||||||
// The SceneRoot component will have spawned the scene as a hierarchy
|
// The SceneRoot component will have spawned the scene as a hierarchy
|
||||||
// of entities parented to our entity. Since the asset contained a skinned
|
// of entities parented to our entity. Since the asset contained a skinned
|
||||||
// mesh and animations, it will also have spawned an animation player
|
// mesh and animations, it will also have spawned an animation player
|
||||||
// component. Search our entity's descendants to find the animation player.
|
// component. Search our entity's descendants to find the animation player.
|
||||||
for child in children.iter_descendants(trigger.target()) {
|
for child in children.iter_descendants(trigger.target().unwrap()) {
|
||||||
if let Ok(mut player) = players.get_mut(child) {
|
if let Ok(mut player) = players.get_mut(child) {
|
||||||
// Tell the animation player to start the animation and keep
|
// Tell the animation player to start the animation and keep
|
||||||
// repeating it.
|
// repeating it.
|
||||||
|
@ -47,7 +47,10 @@ fn observe_on_step(
|
|||||||
transforms: Query<&GlobalTransform>,
|
transforms: Query<&GlobalTransform>,
|
||||||
mut seeded_rng: ResMut<SeededRng>,
|
mut seeded_rng: ResMut<SeededRng>,
|
||||||
) {
|
) {
|
||||||
let translation = transforms.get(trigger.target()).unwrap().translation();
|
let translation = transforms
|
||||||
|
.get(trigger.target().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.translation();
|
||||||
// Spawn a bunch of particles.
|
// Spawn a bunch of particles.
|
||||||
for _ in 0..14 {
|
for _ in 0..14 {
|
||||||
let horizontal = seeded_rng.0.r#gen::<Dir2>() * seeded_rng.0.gen_range(8.0..12.0);
|
let horizontal = seeded_rng.0.r#gen::<Dir2>() * seeded_rng.0.gen_range(8.0..12.0);
|
||||||
|
@ -40,7 +40,7 @@ fn disable_entities_on_click(
|
|||||||
valid_query: Query<&DisableOnClick>,
|
valid_query: Query<&DisableOnClick>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
let clicked_entity = trigger.target();
|
let clicked_entity = trigger.target().unwrap();
|
||||||
// Windows and text are entities and can be clicked!
|
// Windows and text are entities and can be clicked!
|
||||||
// We definitely don't want to disable the window itself,
|
// We definitely don't want to disable the window itself,
|
||||||
// because that would cause the app to close!
|
// because that would cause the app to close!
|
||||||
|
@ -78,14 +78,14 @@ fn attack_armor(entities: Query<Entity, With<Armor>>, mut commands: Commands) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn attack_hits(trigger: Trigger<Attack>, name: Query<&Name>) {
|
fn attack_hits(trigger: Trigger<Attack>, name: Query<&Name>) {
|
||||||
if let Ok(name) = name.get(trigger.target()) {
|
if let Ok(name) = name.get(trigger.target().unwrap()) {
|
||||||
info!("Attack hit {}", name);
|
info!("Attack hit {}", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A callback placed on [`Armor`], checking if it absorbed all the [`Attack`] damage.
|
/// A callback placed on [`Armor`], checking if it absorbed all the [`Attack`] damage.
|
||||||
fn block_attack(mut trigger: Trigger<Attack>, armor: Query<(&Armor, &Name)>) {
|
fn block_attack(mut trigger: Trigger<Attack>, armor: Query<(&Armor, &Name)>) {
|
||||||
let (armor, name) = armor.get(trigger.target()).unwrap();
|
let (armor, name) = armor.get(trigger.target().unwrap()).unwrap();
|
||||||
let attack = trigger.event_mut();
|
let attack = trigger.event_mut();
|
||||||
let damage = attack.damage.saturating_sub(**armor);
|
let damage = attack.damage.saturating_sub(**armor);
|
||||||
if damage > 0 {
|
if damage > 0 {
|
||||||
@ -110,14 +110,14 @@ fn take_damage(
|
|||||||
mut app_exit: EventWriter<AppExit>,
|
mut app_exit: EventWriter<AppExit>,
|
||||||
) {
|
) {
|
||||||
let attack = trigger.event();
|
let attack = trigger.event();
|
||||||
let (mut hp, name) = hp.get_mut(trigger.target()).unwrap();
|
let (mut hp, name) = hp.get_mut(trigger.target().unwrap()).unwrap();
|
||||||
**hp = hp.saturating_sub(attack.damage);
|
**hp = hp.saturating_sub(attack.damage);
|
||||||
|
|
||||||
if **hp > 0 {
|
if **hp > 0 {
|
||||||
info!("{} has {:.1} HP", name, hp.0);
|
info!("{} has {:.1} HP", name, hp.0);
|
||||||
} else {
|
} else {
|
||||||
warn!("💀 {} has died a gruesome death", name);
|
warn!("💀 {} has died a gruesome death", name);
|
||||||
commands.entity(trigger.target()).despawn();
|
commands.entity(trigger.target().unwrap()).despawn();
|
||||||
app_exit.write(AppExit::Success);
|
app_exit.write(AppExit::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,12 +117,16 @@ fn on_add_mine(
|
|||||||
query: Query<&Mine>,
|
query: Query<&Mine>,
|
||||||
mut index: ResMut<SpatialIndex>,
|
mut index: ResMut<SpatialIndex>,
|
||||||
) {
|
) {
|
||||||
let mine = query.get(trigger.target()).unwrap();
|
let mine = query.get(trigger.target().unwrap()).unwrap();
|
||||||
let tile = (
|
let tile = (
|
||||||
(mine.pos.x / CELL_SIZE).floor() as i32,
|
(mine.pos.x / CELL_SIZE).floor() as i32,
|
||||||
(mine.pos.y / CELL_SIZE).floor() as i32,
|
(mine.pos.y / CELL_SIZE).floor() as i32,
|
||||||
);
|
);
|
||||||
index.map.entry(tile).or_default().insert(trigger.target());
|
index
|
||||||
|
.map
|
||||||
|
.entry(tile)
|
||||||
|
.or_default()
|
||||||
|
.insert(trigger.target().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove despawned mines from our index
|
// Remove despawned mines from our index
|
||||||
@ -131,19 +135,19 @@ fn on_remove_mine(
|
|||||||
query: Query<&Mine>,
|
query: Query<&Mine>,
|
||||||
mut index: ResMut<SpatialIndex>,
|
mut index: ResMut<SpatialIndex>,
|
||||||
) {
|
) {
|
||||||
let mine = query.get(trigger.target()).unwrap();
|
let mine = query.get(trigger.target().unwrap()).unwrap();
|
||||||
let tile = (
|
let tile = (
|
||||||
(mine.pos.x / CELL_SIZE).floor() as i32,
|
(mine.pos.x / CELL_SIZE).floor() as i32,
|
||||||
(mine.pos.y / CELL_SIZE).floor() as i32,
|
(mine.pos.y / CELL_SIZE).floor() as i32,
|
||||||
);
|
);
|
||||||
index.map.entry(tile).and_modify(|set| {
|
index.map.entry(tile).and_modify(|set| {
|
||||||
set.remove(&trigger.target());
|
set.remove(&trigger.target().unwrap());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn explode_mine(trigger: Trigger<Explode>, query: Query<&Mine>, mut commands: Commands) {
|
fn explode_mine(trigger: Trigger<Explode>, query: Query<&Mine>, mut commands: Commands) {
|
||||||
// If a triggered event is targeting a specific entity you can access it with `.target()`
|
// If a triggered event is targeting a specific entity you can access it with `.target()`
|
||||||
let id = trigger.target();
|
let id = trigger.target().unwrap();
|
||||||
let Ok(mut entity) = commands.get_entity(id) else {
|
let Ok(mut entity) = commands.get_entity(id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -50,7 +50,7 @@ fn remove_component(
|
|||||||
|
|
||||||
fn react_on_removal(trigger: Trigger<OnRemove, MyComponent>, mut query: Query<&mut Sprite>) {
|
fn react_on_removal(trigger: Trigger<OnRemove, MyComponent>, mut query: Query<&mut Sprite>) {
|
||||||
// The `OnRemove` trigger was automatically called on the `Entity` that had its `MyComponent` removed.
|
// The `OnRemove` trigger was automatically called on the `Entity` that had its `MyComponent` removed.
|
||||||
let entity = trigger.target();
|
let entity = trigger.target().unwrap();
|
||||||
if let Ok(mut sprite) = query.get_mut(entity) {
|
if let Ok(mut sprite) = query.get_mut(entity) {
|
||||||
sprite.color = Color::srgb(0.5, 1., 1.);
|
sprite.color = Color::srgb(0.5, 1., 1.);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,10 @@ fn tick_timers(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap<B: Bundle>(trigger: Trigger<Unwrap>, world: &mut World) {
|
fn unwrap<B: Bundle>(trigger: Trigger<Unwrap>, world: &mut World) {
|
||||||
if let Ok(mut target) = world.get_entity_mut(trigger.target()) {
|
if let Some(mut target) = trigger
|
||||||
|
.target()
|
||||||
|
.and_then(|target| world.get_entity_mut(target).ok())
|
||||||
|
{
|
||||||
if let Some(DelayedComponent(bundle)) = target.take::<DelayedComponent<B>>() {
|
if let Some(DelayedComponent(bundle)) = target.take::<DelayedComponent<B>>() {
|
||||||
target.insert(bundle);
|
target.insert(bundle);
|
||||||
}
|
}
|
||||||
|
@ -48,13 +48,13 @@ fn setup_scene(
|
|||||||
.observe(on_click_spawn_cube)
|
.observe(on_click_spawn_cube)
|
||||||
.observe(
|
.observe(
|
||||||
|out: Trigger<Pointer<Out>>, mut texts: Query<&mut TextColor>| {
|
|out: Trigger<Pointer<Out>>, mut texts: Query<&mut TextColor>| {
|
||||||
let mut text_color = texts.get_mut(out.target()).unwrap();
|
let mut text_color = texts.get_mut(out.target().unwrap()).unwrap();
|
||||||
text_color.0 = Color::WHITE;
|
text_color.0 = Color::WHITE;
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.observe(
|
.observe(
|
||||||
|over: Trigger<Pointer<Over>>, mut texts: Query<&mut TextColor>| {
|
|over: Trigger<Pointer<Over>>, mut texts: Query<&mut TextColor>| {
|
||||||
let mut color = texts.get_mut(over.target()).unwrap();
|
let mut color = texts.get_mut(over.target().unwrap()).unwrap();
|
||||||
color.0 = bevy::color::palettes::tailwind::CYAN_400.into();
|
color.0 = bevy::color::palettes::tailwind::CYAN_400.into();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -102,7 +102,7 @@ fn on_click_spawn_cube(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_drag_rotate(drag: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>) {
|
fn on_drag_rotate(drag: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>) {
|
||||||
if let Ok(mut transform) = transforms.get_mut(drag.target()) {
|
if let Ok(mut transform) = transforms.get_mut(drag.target().unwrap()) {
|
||||||
transform.rotate_y(drag.delta.x * 0.02);
|
transform.rotate_y(drag.delta.x * 0.02);
|
||||||
transform.rotate_x(drag.delta.y * 0.02);
|
transform.rotate_x(drag.delta.y * 0.02);
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ fn update_material_on<E>(
|
|||||||
// versions of this observer, each triggered by a different event and with a different hardcoded
|
// versions of this observer, each triggered by a different event and with a different hardcoded
|
||||||
// material. Instead, the event type is a generic, and the material is passed in.
|
// material. Instead, the event type is a generic, and the material is passed in.
|
||||||
move |trigger, mut query| {
|
move |trigger, mut query| {
|
||||||
if let Ok(mut material) = query.get_mut(trigger.target()) {
|
if let Ok(mut material) = query.get_mut(trigger.target().unwrap()) {
|
||||||
material.0 = new_material.clone();
|
material.0 = new_material.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,7 +191,7 @@ fn rotate(mut query: Query<&mut Transform, With<Shape>>, time: Res<Time>) {
|
|||||||
|
|
||||||
/// An observer to rotate an entity when it is dragged
|
/// An observer to rotate an entity when it is dragged
|
||||||
fn rotate_on_drag(drag: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>) {
|
fn rotate_on_drag(drag: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>) {
|
||||||
let mut transform = transforms.get_mut(drag.target()).unwrap();
|
let mut transform = transforms.get_mut(drag.target().unwrap()).unwrap();
|
||||||
transform.rotate_y(drag.delta.x * 0.02);
|
transform.rotate_y(drag.delta.x * 0.02);
|
||||||
transform.rotate_x(drag.delta.y * 0.02);
|
transform.rotate_x(drag.delta.y * 0.02);
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,13 @@ fn setup_scene(
|
|||||||
.observe(on_click_spawn_cube)
|
.observe(on_click_spawn_cube)
|
||||||
.observe(
|
.observe(
|
||||||
|out: Trigger<Pointer<Out>>, mut texts: Query<&mut TextColor>| {
|
|out: Trigger<Pointer<Out>>, mut texts: Query<&mut TextColor>| {
|
||||||
let mut text_color = texts.get_mut(out.target()).unwrap();
|
let mut text_color = texts.get_mut(out.target().unwrap()).unwrap();
|
||||||
text_color.0 = Color::WHITE;
|
text_color.0 = Color::WHITE;
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.observe(
|
.observe(
|
||||||
|over: Trigger<Pointer<Over>>, mut texts: Query<&mut TextColor>| {
|
|over: Trigger<Pointer<Over>>, mut texts: Query<&mut TextColor>| {
|
||||||
let mut color = texts.get_mut(over.target()).unwrap();
|
let mut color = texts.get_mut(over.target().unwrap()).unwrap();
|
||||||
color.0 = bevy::color::palettes::tailwind::CYAN_400.into();
|
color.0 = bevy::color::palettes::tailwind::CYAN_400.into();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -80,7 +80,7 @@ fn on_click_spawn_cube(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_drag_rotate(drag: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>) {
|
fn on_drag_rotate(drag: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>) {
|
||||||
if let Ok(mut transform) = transforms.get_mut(drag.target()) {
|
if let Ok(mut transform) = transforms.get_mut(drag.target().unwrap()) {
|
||||||
transform.rotate_y(drag.delta.x * 0.02);
|
transform.rotate_y(drag.delta.x * 0.02);
|
||||||
transform.rotate_x(drag.delta.y * 0.02);
|
transform.rotate_x(drag.delta.y * 0.02);
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ fn setup_atlas(
|
|||||||
// An observer listener that changes the target entity's color.
|
// An observer listener that changes the target entity's color.
|
||||||
fn recolor_on<E: Debug + Clone + Reflect>(color: Color) -> impl Fn(Trigger<E>, Query<&mut Sprite>) {
|
fn recolor_on<E: Debug + Clone + Reflect>(color: Color) -> impl Fn(Trigger<E>, Query<&mut Sprite>) {
|
||||||
move |ev, mut sprites| {
|
move |ev, mut sprites| {
|
||||||
let Ok(mut sprite) = sprites.get_mut(ev.target()) else {
|
let Ok(mut sprite) = sprites.get_mut(ev.target().unwrap()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
sprite.color = color;
|
sprite.color = color;
|
||||||
|
@ -281,7 +281,7 @@ mod animation {
|
|||||||
animation: Res<Animation>,
|
animation: Res<Animation>,
|
||||||
mut players: Query<(Entity, &mut AnimationPlayer)>,
|
mut players: Query<(Entity, &mut AnimationPlayer)>,
|
||||||
) {
|
) {
|
||||||
for child in children.iter_descendants(trigger.target()) {
|
for child in children.iter_descendants(trigger.target().unwrap()) {
|
||||||
if let Ok((entity, mut player)) = players.get_mut(child) {
|
if let Ok((entity, mut player)) = players.get_mut(child) {
|
||||||
let mut transitions = AnimationTransitions::new();
|
let mut transitions = AnimationTransitions::new();
|
||||||
transitions
|
transitions
|
||||||
|
@ -70,7 +70,7 @@ fn universal_button_click_behavior(
|
|||||||
mut trigger: Trigger<Pointer<Click>>,
|
mut trigger: Trigger<Pointer<Click>>,
|
||||||
mut button_query: Query<(&mut BackgroundColor, &mut ResetTimer)>,
|
mut button_query: Query<(&mut BackgroundColor, &mut ResetTimer)>,
|
||||||
) {
|
) {
|
||||||
let button_entity = trigger.target();
|
let button_entity = trigger.target().unwrap();
|
||||||
if let Ok((mut color, mut reset_timer)) = button_query.get_mut(button_entity) {
|
if let Ok((mut color, mut reset_timer)) = button_query.get_mut(button_entity) {
|
||||||
// This would be a great place to play a little sound effect too!
|
// This would be a great place to play a little sound effect too!
|
||||||
color.0 = PRESSED_BUTTON.into();
|
color.0 = PRESSED_BUTTON.into();
|
||||||
|
@ -93,7 +93,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
mut commands: Commands
|
mut commands: Commands
|
||||||
| {
|
| {
|
||||||
if trigger.event().button == PointerButton::Primary {
|
if trigger.event().button == PointerButton::Primary {
|
||||||
commands.entity(trigger.target()).despawn();
|
commands.entity(trigger.target().unwrap()).despawn();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ fn setup(mut commands: Commands) {
|
|||||||
.observe(
|
.observe(
|
||||||
|mut trigger: Trigger<Pointer<Click>>,
|
|mut trigger: Trigger<Pointer<Click>>,
|
||||||
mut focus: ResMut<InputFocus>| {
|
mut focus: ResMut<InputFocus>| {
|
||||||
focus.0 = Some(trigger.target());
|
focus.0 = Some(trigger.target().unwrap());
|
||||||
trigger.propagate(false);
|
trigger.propagate(false);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -91,7 +91,7 @@ fn test(
|
|||||||
|
|
||||||
fn on_drag_viewport(drag: Trigger<Pointer<Drag>>, mut node_query: Query<&mut Node>) {
|
fn on_drag_viewport(drag: Trigger<Pointer<Drag>>, mut node_query: Query<&mut Node>) {
|
||||||
if matches!(drag.button, PointerButton::Secondary) {
|
if matches!(drag.button, PointerButton::Secondary) {
|
||||||
let mut node = node_query.get_mut(drag.target()).unwrap();
|
let mut node = node_query.get_mut(drag.target().unwrap()).unwrap();
|
||||||
|
|
||||||
if let (Val::Px(top), Val::Px(left)) = (node.top, node.left) {
|
if let (Val::Px(top), Val::Px(left)) = (node.top, node.left) {
|
||||||
node.left = Val::Px(left + drag.delta.x);
|
node.left = Val::Px(left + drag.delta.x);
|
||||||
@ -102,7 +102,7 @@ fn on_drag_viewport(drag: Trigger<Pointer<Drag>>, mut node_query: Query<&mut Nod
|
|||||||
|
|
||||||
fn on_drag_cuboid(drag: Trigger<Pointer<Drag>>, mut transform_query: Query<&mut Transform>) {
|
fn on_drag_cuboid(drag: Trigger<Pointer<Drag>>, mut transform_query: Query<&mut Transform>) {
|
||||||
if matches!(drag.button, PointerButton::Primary) {
|
if matches!(drag.button, PointerButton::Primary) {
|
||||||
let mut transform = transform_query.get_mut(drag.target()).unwrap();
|
let mut transform = transform_query.get_mut(drag.target().unwrap()).unwrap();
|
||||||
transform.rotate_y(drag.delta.x * 0.02);
|
transform.rotate_y(drag.delta.x * 0.02);
|
||||||
transform.rotate_x(drag.delta.y * 0.02);
|
transform.rotate_x(drag.delta.y * 0.02);
|
||||||
}
|
}
|
||||||
|
12
release-content/migration-guides/observer_triggers.md
Normal file
12
release-content/migration-guides/observer_triggers.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: Observer Triggers
|
||||||
|
pull_requests: [19440]
|
||||||
|
---
|
||||||
|
|
||||||
|
Observers may be triggered on particular entities or globally.
|
||||||
|
Previously, a global trigger would claim to trigger on a particular `Entity`, `Entity::PLACEHOLDER`.
|
||||||
|
For correctness and transparency, triggers have been changed to `Option<Entity>`.
|
||||||
|
|
||||||
|
`Trigger::target` now returns `Option<Entity>` and `ObserverTrigger::target` is now of type `Option<Entity>`.
|
||||||
|
If you were checking for `Entity::PLACEHOLDER`, migrate to handling the `None` case.
|
||||||
|
If you were not checking for `Entity::PLACEHOLDER`, migrate to unwrapping, as `Entity::PLACEHOLDER` would have caused a panic before, at a later point.
|
Loading…
Reference in New Issue
Block a user