) {
),
),
backward(secs(0.2)),
- event_for(secs(0.2), "small_boom"),
- event("boom"),
+ message_for(secs(0.2), "small_boom"),
+ message("boom"),
tween(
secs(1.),
EaseKind::CircularOut,
diff --git a/src/combinator/animation_combinators.rs b/src/combinator/animation_combinators.rs
index 9eb9b79f..735fad1b 100644
--- a/src/combinator/animation_combinators.rs
+++ b/src/combinator/animation_combinators.rs
@@ -1,5 +1,8 @@
use super::AnimationCommands;
-use crate::prelude::TweenEventData;
+use crate::{
+ prelude::TweenEventData,
+ tween_event::{EventEmittingTween, MessageEmittingTween},
+};
use bevy::prelude::*;
use bevy_time_runner::TimeSpan;
use std::time::Duration;
@@ -77,7 +80,47 @@ where
}
}
-/// Combinator for creating an tween event.
+/// Combinator for creating both a tween event and message.
+///
+/// Event will be emitted at current position.
+///
+/// Position is not mutated because the event has no length.
+///
+///
+///
+/// Your event should be registered with [`TweenEventPlugin`](crate::tween_event::TweenEventPlugin).
+///
+///
+pub fn send(
+ event_data: Data,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch(event_data, (MessageEmittingTween, EventEmittingTween))
+}
+
+/// Combinator for dispatching a tween message.
+///
+/// Message will be emitted at current position.
+///
+/// Position is not mutated because the event has no length.
+///
+///
+///
+/// Your event should be registered with [`TweenEventPlugin`](crate::tween_event::TweenEventPlugin).
+///
+///
+pub fn message(
+ event_data: Data,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch(event_data, MessageEmittingTween)
+}
+
+/// Combinator for creating a tween event on the animation root containing a [`bevy_time_runner::TimeRunner`].
///
/// Event will be emitted at current position.
///
@@ -91,6 +134,21 @@ where
pub fn event(
event_data: Data,
) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch(event_data, EventEmittingTween)
+}
+
+/// Combinator for dispatching a tween event.
+///
+/// `with` informs how the tween event is dispatched.
+///
+/// Event will be emitted at current position.
+fn dispatch(
+ event_data: Data,
+ with: impl Bundle,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
where
Data: Send + Sync + 'static,
{
@@ -98,11 +156,54 @@ where
a.spawn((
TimeSpan::try_from(*pos..=*pos).unwrap(),
TweenEventData::with_data(event_data),
+ with,
));
}
}
-/// Combinator for creating an tween event.
+/// Combinator for creating both a tween event and message.
+///
+/// Event will be emitted at the provided position.
+///
+/// Position is not mutated because the operation is not relative.
+///
+///
+///
+/// Your event should be registered with [`TweenEventPlugin`](crate::tween_event::TweenEventPlugin).
+///
+///
+pub fn send_at(
+ at: Duration,
+ event_data: Data,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch_at(at, event_data, (MessageEmittingTween, EventEmittingTween))
+}
+
+/// Combinator for dispatching a tween message.
+///
+/// Message will be emitted at the provided position.
+///
+/// Position is not mutated because the operation is not relative.
+///
+///
+///
+/// Your event should be registered with [`TweenEventPlugin`](crate::tween_event::TweenEventPlugin).
+///
+///
+pub fn message_at(
+ at: Duration,
+ event_data: Data,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch_at(at, event_data, MessageEmittingTween)
+}
+
+/// Combinator for creating an tween event on the animation root containing a [`bevy_time_runner::TimeRunner`].
///
/// Event will be emitted at the provided position.
///
@@ -117,6 +218,22 @@ pub fn event_at(
at: Duration,
event_data: Data,
) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch_at(at, event_data, EventEmittingTween)
+}
+
+/// Combinator for dispatching a tween event.
+///
+/// `with` informs how the tween event is dispatched.
+///
+/// Event will be emitted at the provided position.
+fn dispatch_at(
+ at: Duration,
+ event_data: Data,
+ with: impl Bundle,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
where
Data: Send + Sync + 'static,
{
@@ -124,11 +241,58 @@ where
a.spawn((
TimeSpan::try_from(at..=at).unwrap(),
TweenEventData::with_data(event_data),
+ with,
));
}
}
-/// Combinator for creating an tween event.
+/// Combinator for creating both a tween event and message.
+///
+/// Event will be emitted at the current position for provided length every frame.
+///
+/// Position is at the end of the event.
+///
+///
+///
+/// Your event should be registered with [`TweenEventPlugin`](crate::tween_event::TweenEventPlugin).
+///
+///
+pub fn send_for(
+ length: Duration,
+ event_data: Data,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch_for(
+ length,
+ event_data,
+ (MessageEmittingTween, EventEmittingTween),
+ )
+}
+
+/// Combinator for dispatching a tween message.
+///
+/// Message will be emitted at the current position for provided length every frame.
+///
+/// Position is at the end of the event.
+///
+///
+///
+/// Your event should be registered with [`TweenEventPlugin`](crate::tween_event::TweenEventPlugin).
+///
+///
+pub fn message_for(
+ length: Duration,
+ event_data: Data,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch_for(length, event_data, MessageEmittingTween)
+}
+
+/// Combinator for creating an tween event on the animation root containing a [`bevy_time_runner::TimeRunner`].
///
/// Event will be emitted at the current position for provided length every frame.
///
@@ -143,6 +307,24 @@ pub fn event_for(
length: Duration,
event_data: Data,
) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ Data: Send + Sync + 'static,
+{
+ dispatch_for(length, event_data, EventEmittingTween)
+}
+
+/// Combinator for dispatching a tween event.
+///
+/// `with` informs how the tween event is dispatched.
+///
+/// Event will be emitted at the current position for provided length every frame.
+///
+/// Position is at the end of the event.
+fn dispatch_for(
+ length: Duration,
+ event_data: Data,
+ with: impl Bundle,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
where
Data: Send + Sync + 'static,
{
@@ -152,12 +334,59 @@ where
a.spawn((
TimeSpan::try_from(start..end).unwrap(),
TweenEventData::with_data(event_data),
+ with,
));
*pos = end;
}
}
-/// Combinator for creating an tween event.
+/// Combinator for creating both a tween event and message.
+///
+/// Event will be emitted at the provided span every frame.
+///
+/// Position is not mutated because the operation is not relative.
+///
+///
+///
+/// Your event should be registered with [`TweenEventPlugin`](crate::tween_event::TweenEventPlugin).
+///
+///
+pub fn send_exact(
+ span: S,
+ event_data: Data,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ S: TryInto,
+ S::Error: std::fmt::Debug,
+ Data: Send + Sync + 'static,
+{
+ dispatch_exact(span, event_data, (MessageEmittingTween, EventEmittingTween))
+}
+
+/// Combinator for dispatching a tween message.
+///
+/// Message will be emitted at the provided span every frame.
+///
+/// Position is not mutated because the operation is not relative.
+///
+///
+///
+/// Your event should be registered with [`TweenEventPlugin`](crate::tween_event::TweenEventPlugin).
+///
+///
+pub fn message_exact(
+ span: S,
+ event_data: Data,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ S: TryInto,
+ S::Error: std::fmt::Debug,
+ Data: Send + Sync + 'static,
+{
+ dispatch_exact(span, event_data, MessageEmittingTween)
+}
+
+/// Combinator for creating an tween event on the animation root containing a [`bevy_time_runner::TimeRunner`].
///
/// Event will be emitted at the provided span every frame.
///
@@ -172,6 +401,26 @@ pub fn event_exact(
span: S,
event_data: Data,
) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
+where
+ S: TryInto,
+ S::Error: std::fmt::Debug,
+ Data: Send + Sync + 'static,
+{
+ dispatch_exact(span, event_data, EventEmittingTween)
+}
+
+/// Combinator for dispatching a tween event.
+///
+/// `with` informs how the tween event is dispatched.
+///
+/// Event will be emitted at the provided span every frame.
+///
+/// Position is not mutated because the operation is not relative.
+pub fn dispatch_exact(
+ span: S,
+ event_data: Data,
+ with: impl Bundle,
+) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
where
S: TryInto,
S::Error: std::fmt::Debug,
@@ -181,6 +430,7 @@ where
a.spawn((
span.try_into().unwrap(),
TweenEventData::with_data(event_data),
+ with,
));
}
}
diff --git a/src/lib.rs b/src/lib.rs
index b5cd702a..be39db5a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -416,7 +416,7 @@ pub use tween::component_tween_system;
pub use tween::resource_dyn_tween_system;
pub use tween::resource_tween_system;
-pub use tween_event::tween_event_system;
+pub use tween_event::{tween_event_system, tween_message_system};
/// Default plugins for using crate.
pub struct DefaultTweenPlugins
@@ -599,6 +599,7 @@ pub enum TweenSystemSet {
/// - [`tween::asset_tween_system`]
///
/// Events is not necessary related to tweening but their code is still working in the same area.
+ /// - [`tween_event::tween_message_system`]
/// - [`tween_event::tween_event_system`]
ApplyTween,
}
diff --git a/src/tween_event.rs b/src/tween_event.rs
index 9ce7e227..a4770ef6 100644
--- a/src/tween_event.rs
+++ b/src/tween_event.rs
@@ -10,12 +10,13 @@
//! - [`TweenEventData`]
//!
//! **Systems**
+//! - [`tween_message_system`]
//! - [`tween_event_system`]
//!
//! **Events**:
//! - [`TweenEvent`]
//!
-//! Simple event system that fires generic data in a timed manner.
+//! Simple event systems that fires generic data in a timed manner.
//! Your data is stored in [`TweenEventData`] and the event type is [`TweenEvent`].
//! Tween event is not necessarily related to tweening.
//!
@@ -25,12 +26,17 @@
//! See [`DefaultTweenEventPlugins`] for default events which is also added in
//! [`DefaultTweenPlugins`](crate::DefaultTweenPlugins)
-use bevy_time_runner::TimeContext;
+use bevy_time_runner::{TimeContext, TimeRunner};
+use std::fmt::Debug;
use std::marker::PhantomData;
+use tracing::warn;
use bevy::{
app::PluginGroupBuilder,
- ecs::schedule::{InternedScheduleLabel, ScheduleLabel},
+ ecs::{
+ query::QueryData,
+ schedule::{InternedScheduleLabel, ScheduleLabel},
+ },
prelude::*,
};
@@ -69,7 +75,10 @@ where
fn build(&self, app: &mut App) {
app.add_systems(
self.schedule,
- (tween_event_system::)
+ (
+ tween_message_system::,
+ tween_event_system::,
+ )
.in_set(crate::TweenSystemSet::ApplyTween),
)
.add_message::>();
@@ -128,12 +137,16 @@ impl Default for DefaultTweenEventPlugins<()> {
/// Fires [`TweenEvent`] whenever [`TimeSpanProgress`] and [`TweenEventData`] exist in the same entity.
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Component, Reflect)]
#[reflect(Component)]
-#[require(EventEmittingTween)]
pub struct TweenEventData(pub Data)
where
Data: Send + Sync + 'static;
-/// Used to mark event-emitting tweens (tweens with `TweenEventData` for some registered `Data`)
+/// Used to mark message-emitting tweens (tweens that emit a tween event message)
+#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Component, Reflect)]
+#[reflect(Component)]
+pub struct MessageEmittingTween;
+
+/// Used to mark root event-emitting tween (tweens that emit an entity event to the animation root)
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Component, Reflect)]
#[reflect(Component)]
pub struct EventEmittingTween;
@@ -152,8 +165,8 @@ impl TweenEventData<()> {
}
}
-/// Fires whenever [`TimeSpanProgress`] and [`TweenEventData`] exist in the same entity
-/// by [`tween_event_system`].
+/// Fires whenever [`TimeSpanProgress`], [`TweenEventData`], and [`MessageEmittingTween`]/[`EventEmittingTween`] exist in the same entity
+/// by [`tween_message_system`] and [`tween_event_system`].
#[derive(Debug, Clone, PartialEq, Message, Reflect, EntityEvent)]
pub struct TweenEvent {
/// Custom user data
@@ -166,36 +179,98 @@ pub struct TweenEvent {
pub entity: Entity,
}
-/// Fires [`TweenEvent`] with optional user data whenever [`TimeSpanProgress`]
-/// and [`TweenEventData`] exist in the same entity and data is `Some`,
-/// cloning the data.
+/// QueryData used to construct a [`TweenEvent`] for a given [`TweenEventData`]
+#[derive(QueryData)]
+pub struct TweenSystemData {
+ /// The tween entity the event combinator created
+ entity: Entity,
+ /// The data passed to the event combinator
+ event_data: &'static TweenEventData,
+ /// The progress for the event
+ progress: &'static TimeSpanProgress,
+ /// The current interpolation value
+ interpolation_value: Option<&'static TweenInterpolationValue>,
+}
+
+// Note we need to implement event for the data item,
+// due to how the [`QueryData`] derive is implemented.
+impl<'w, 's, Data> TweenSystemDataItem<'w, 's, Data>
+where
+ Data: Clone + Send + Sync + 'static,
+{
+ /// Constructs a tween event for the given tween data entity
+ pub fn event(&self) -> TweenEvent {
+ TweenEvent {
+ data: self.event_data.0.clone(),
+ progress: *self.progress,
+ interpolation_value: self.interpolation_value.map(|v| v.0),
+ entity: self.entity,
+ }
+ }
+
+ /// Constructs a tween event for a different tween data entity
+ pub fn event_on(&self, entity: Entity) -> TweenEvent {
+ TweenEvent {
+ entity,
+ ..self.event()
+ }
+ }
+}
+
+/// Fires a [`TweenEvent`] message with optional user data
+/// whenever [`TimeSpanProgress`] and [`TweenEventData`] exist in the
+/// same entity and data is `Some`, cloning the data.
+#[allow(clippy::type_complexity)]
+pub fn tween_message_system(
+ q_tween_event_data: Query<
+ TweenSystemData,
+ (
+ Without,
+ With>,
+ With,
+ ),
+ >,
+ mut event_writer: MessageWriter>,
+) where
+ Data: Clone + Send + Sync + 'static,
+ TimeCtx: Default + Send + Sync + 'static,
+{
+ q_tween_event_data.iter().for_each(|data| {
+ event_writer.write(data.event());
+ });
+}
+
+/// Fires a [`TweenEvent`] to the animation root with optional user data
+/// whenever [`TimeSpanProgress`] and [`TweenEventData`] exist in the
+/// same entity and data is `Some`, cloning the data.
#[allow(clippy::type_complexity)]
pub fn tween_event_system(
mut commands: Commands,
q_tween_event_data: Query<
+ (&ChildOf, TweenSystemData),
(
- Entity,
- &TweenEventData,
- &TimeSpanProgress,
- Option<&TweenInterpolationValue>,
+ Without,
+ With>,
+ With,
),
- (Without, With>),
>,
- mut event_writer: MessageWriter>,
+ q_tween_roots: Query<(), With>,
) where
Data: Clone + Send + Sync + 'static,
TimeCtx: Default + Send + Sync + 'static,
{
- q_tween_event_data.iter().for_each(
- |(entity, event_data, progress, interpolation_value)| {
- let event = TweenEvent {
- data: event_data.0.clone(),
- progress: *progress,
- interpolation_value: interpolation_value.map(|v| v.0),
- entity,
- };
- commands.trigger(event.clone());
- event_writer.write(event);
- },
- );
+ q_tween_event_data.iter().for_each(|(child_of, data)| {
+ let root = child_of.parent();
+ if !q_tween_roots.contains(root) {
+ warn!(
+ "Could not find an associated animation root for a tween event on entity {:?} with data {:?}\n\
+ help: make sure you have a `TimeRunner` defined on the entity you plan on animating",
+ data.event().entity,
+ std::any::type_name::(),
+ );
+ return;
+ };
+
+ commands.trigger(data.event_on(root));
+ });
}