Skip to content

Commit

Permalink
Improve tween event APIs (#26)
Browse files Browse the repository at this point in the history
Currently tween event implementation is directly inside the tween module while these 2 are unrelated.

Isolate tween event into its own module

Additionally.

Add better plugin API instead of making users manually registering system and event.
Implement EntityEvent for TweenEvent
  • Loading branch information
Multirious authored Jun 11, 2024
1 parent eb0a671 commit 38c7a75
Show file tree
Hide file tree
Showing 6 changed files with 377 additions and 225 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ resolver = "2"
bevy = { version = "0.13.0", default-features = false }
bevy_time_runner = { version = "0.1.4", default-features = false, features = ["bevy_app", "bevy_reflect"] }
bevy_lookup_curve = { version = "0.2.1", optional = true }
bevy_eventlistener = { version = "0.7.0", optional = true }

[dev-dependencies]
bevy-inspector-egui = "0.23"
Expand All @@ -46,7 +47,7 @@ bevy_render = [ "bevy/bevy_render"]
# Add some built-in interpolator related to sprite
bevy_sprite = [ "bevy/bevy_sprite", "bevy_render" ]
# Add entity-targeted events with bevy_eventlistener
bevy_eventlistener = [ "bevy_time_runner/bevy_eventlistener" ]
bevy_eventlistener = [ "dep:bevy_eventlistener", "bevy_time_runner/bevy_eventlistener" ]
# Supports for `bevy_lookup_curve` (https://github.com/villor/bevy_lookup_curve)
bevy_lookup_curve = [ "dep:bevy_lookup_curve" ]

Expand Down
18 changes: 17 additions & 1 deletion examples/demo/entity_event.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use bevy::prelude::*;
use bevy_eventlistener::prelude::*;
use bevy_tween::{
bevy_time_runner::TimeRunnerEnded, combinator::forward, prelude::*,
bevy_time_runner::TimeRunnerEnded,
combinator::{event, forward, sequence},
prelude::*,
};

fn main() {
Expand All @@ -25,4 +27,18 @@ fn setup(mut commands: Commands) {
.animation()
.repeat(Repeat::times(5))
.insert(forward(Duration::from_secs_f32(0.5)));

commands
.spawn(On::<TweenEvent<&'static str>>::run(
|listener: Listener<TweenEvent<&'static str>>| {
println!("{}", listener.data);
},
))
.animation()
.insert(sequence((
forward(Duration::from_secs_f32(3.)),
event("event"),
forward(Duration::from_secs_f32(0.5)),
event("listener"),
)));
}
15 changes: 11 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ pub use bevy_time_runner;
pub mod interpolate;
pub mod interpolation;
pub mod tween;
pub mod tween_event;

pub mod combinator;

Expand All @@ -385,7 +386,8 @@ pub mod prelude {

pub use crate::combinator::{AnimationBuilderExt, TransformTargetStateExt};

pub use crate::tween::{IntoTarget, TweenEvent, TweenEventData};
pub use crate::tween::IntoTarget;
pub use crate::tween_event::{TweenEvent, TweenEventData};

#[cfg(feature = "bevy_asset")]
pub use crate::tween::AssetDynTween;
Expand Down Expand Up @@ -413,8 +415,8 @@ pub use tween::component_tween_system;
pub use tween::resource_dyn_tween_system;
pub use tween::resource_tween_system;

pub use tween::tween_event_system;
pub use tween::tween_event_taking_system;
pub use tween_event::tween_event_system;
pub use tween_event::tween_event_taking_system;

/// Default plugins for using crate.
///
Expand All @@ -423,17 +425,22 @@ pub use tween::tween_event_taking_system;
/// - [`interpolate::DefaultInterpolatorsPlugin`]
/// - [`interpolate::DefaultDynInterpolatorsPlugin`]
/// - [`interpolation::EaseFunctionPlugin`]
/// - [`tween_event::DefaultTweenEventPlugins`]
pub struct DefaultTweenPlugins;

impl PluginGroup for DefaultTweenPlugins {
fn build(self) -> bevy::app::PluginGroupBuilder {
let default_tween_event_plugins =
tween_event::DefaultTweenEventPlugins::plugins();
#[allow(clippy::let_and_return)]
let group = PluginGroupBuilder::start::<DefaultTweenPlugins>()
.add(TweenCorePlugin::default())
.add(interpolate::DefaultInterpolatorsPlugin)
.add(interpolate::DefaultDynInterpolatorsPlugin)
.add(interpolation::EaseFunctionPlugin)
.add(tween::DefaultTweenEventsPlugin);
// waiting for add_group method in 0.14
.add(default_tween_event_plugins.0)
.add(default_tween_event_plugins.1);
#[cfg(feature = "bevy_lookup_curve")]
let group = group.add(interpolation::bevy_lookup_curve::BevyLookupCurveInterpolationPlugin);
group
Expand Down
233 changes: 79 additions & 154 deletions src/tween.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,15 @@
//!
//! # Tween
//!
//! **Plugins**:
//! - [`DefaultTweenEventsPlugin`]
//!
//! **Components**:
//! - [`Tween<T, I>`]
//! - [`SkipTween`]
//! - [`TweenInterpolationValue`]
//! - [`TweenEventData`]
//!
//! **Events**:
//! - [`TweenEvent`]
//!
//! **Systems**
//! - [`component_tween_system`]
//! - [`resource_tween_system`]
//! - [`asset_tween_system`]
//! - [`tween_event_system`]
//! - [`tween_event_taking_system`]
//!
//! **Targets**:
//! - [`TargetComponent`]
Expand Down Expand Up @@ -221,11 +212,9 @@
//! [`DefaultDynInterpolatorsPlugin`]: crate::interpolate::DefaultDynInterpolatorsPlugin
use bevy::prelude::*;
use bevy_time_runner::TimeSpanProgress;

use crate::combinator::TargetState;
use crate::interpolate::Interpolator;
use crate::{utils, BevyTweenRegisterSystems};

mod systems;
#[cfg(feature = "bevy_asset")]
Expand All @@ -240,15 +229,14 @@ pub use systems::{
apply_resource_tween_system, resource_dyn_tween_system,
resource_tween_system,
};
pub use systems::{tween_event_system, tween_event_taking_system};

/// Skip a tween from tweening.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Component, Reflect)]
#[reflect(Component)]
pub struct SkipTween;

/// Automatically managed by an [`Interpolation`] such as [`EaseFunction`] and
/// [`EaseClosure`] when a tween has the component [`TimeSpanProgress`].
/// [`EaseClosure`] when a tween has the component [`TimeSpanProgress`](bevy_time_runner::TimeSpanProgress).
/// See [`sample_interpolations_system`]
///
/// [`sample_interpolations_system`]: crate::interpolation::sample_interpolations_system
Expand Down Expand Up @@ -639,147 +627,6 @@ impl<A: Asset, const N: usize> From<&[Handle<A>; N]> for TargetAsset<A> {
}
}

/// Default event and systems:
/// - [`tween_event_system::<()>`], [`TweenEvent<()>`]
/// - [`tween_event_system::<&'static str>`], [`TweenEvent<&'static str>`]
pub struct DefaultTweenEventsPlugin;

impl Plugin for DefaultTweenEventsPlugin {
fn build(&self, app: &mut App) {
app.add_tween_systems(systems::tween_event_system::<()>)
.add_event::<TweenEvent>()
.add_tween_systems(systems::tween_event_system::<&'static str>)
.add_event::<TweenEvent<&'static str>>();
}
}

/// Fires [`TweenEvent`] whenever [`TimeSpanProgress`] and [`TweenEventData`] exist in the same entity.
///
/// # Examples
///
/// ```
#[doc = utils::doc_test_boilerplate!()]
/// use bevy_tween::bevy_time_runner::{TimeRunner, TimeSpan};
///
/// commands
/// .spawn((TimeRunner::new(Duration::from_secs(3))))
/// .with_children(|c| {
/// // The event will be fired once at the second 1.
/// c.spawn((
/// TimeSpan::try_from(
/// Duration::from_secs(1)..=Duration::from_secs(1),
/// ).unwrap(),
/// TweenEventData::new(),
/// ));
///
/// // The event will be fired repetitively every frame
/// // between the second 2 and 3.
/// c.spawn((
/// TimeSpan::try_from(
/// Duration::from_secs(2)..Duration::from_secs(3),
/// ).unwrap(),
/// TweenEventData::new(),
/// ));
/// });
/// ```
///
/// ## Using custom data
///
/// You have to register [`tween_event_system`] or [`tween_event_taking_system`]
/// before using custom data with [`TweenEvent<Data>`]. And add your custom event.
/// Check [`DefaultTweenEventsPlugin`] for built-in events.
/// ```no_run
/// use bevy::prelude::*;
/// use bevy_tween::prelude::*;
/// use bevy_tween::tween_event_system;
///
/// #[derive(Clone)]
/// enum MyTweenData {
/// Idle,
/// Fly,
/// }
///
/// fn main() {
/// App::new()
/// .add_plugins((DefaultPlugins, DefaultTweenPlugins))
/// .add_tween_systems(tween_event_system::<MyTweenData>)
/// .add_event::<TweenEvent<MyTweenData>>()
/// .run();
/// }
/// ```
/// ```
#[doc = utils::doc_test_boilerplate!()]
/// # #[derive(Clone)]
/// # enum MyTweenData {
/// # Idle,
/// # Fly,
/// # }
/// # use bevy_tween::bevy_time_runner::{TimeRunner, TimeSpan};
/// commands
/// .spawn(TimeRunner::new(Duration::from_secs(5)))
/// .with_children(|c| {
///
/// // The `TweenEvent<MyTweenData>` event will be fired once at the second 2.
/// c.spawn((
/// TimeSpan::try_from(
/// Duration::from_secs(2)..=Duration::from_secs(2),
/// ).unwrap(),
/// TweenEventData::with_data(MyTweenData::Idle),
/// ));
///
/// // The `TweenEvent<MyTweenData>` event will be fired once at the second 3.
/// c.spawn((
/// TimeSpan::try_from(
/// Duration::from_secs(2)..=Duration::from_secs(2),
/// ).unwrap(),
/// TweenEventData::with_data(MyTweenData::Fly),
/// ));
/// });
/// ```
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Component, Reflect)]
#[reflect(Component)]
pub struct TweenEventData<Data = ()>(pub Option<Data>)
where
Data: Send + Sync + 'static;

impl<Data: Send + Sync + 'static> TweenEventData<Data> {
/// Create new [`TweenEventData`] with custom user data.
pub fn with_data(data: Data) -> Self {
TweenEventData(Some(data))
}
}

impl TweenEventData<()> {
/// Create new [`TweenEventData`] with no custom user data, simply `Some(())`.
pub fn new() -> Self {
TweenEventData(Some(()))
}
}

impl<Data> TweenEventData<Data>
where
Data: Send + Sync + 'static,
{
/// Create new [`TweenEventData`] with `None` value.
pub fn none() -> Self {
TweenEventData(None)
}
}

/// Fires whenever [`TimeSpanProgress`] and [`TweenEventData`] exist in the same entity
/// by [`tween_event_system`] or [`tween_event_taking_system`].
#[derive(Debug, Clone, PartialEq, Event, Reflect)]
pub struct TweenEvent<Data = ()> {
/// Custom user data
pub data: Data,
/// Progress percentage of the tween
pub progress: TimeSpanProgress,
/// Sampled value of an interpolation.
pub interpolation_value: Option<f32>,
/// The entity
pub entity: Entity,
}

/// Trait for type to convert into a target type.
pub trait IntoTarget {
/// The target type
Expand Down Expand Up @@ -901,3 +748,81 @@ impl<A: Asset> IntoTarget for &Vec<Handle<A>> {
TargetAsset::assets(self.iter().cloned())
}
}

#[deprecated(
since = "0.6.0",
note = "use `bevy_tween::tween_event::TweenEvent` instead"
)]
#[allow(missing_docs)]
#[doc(hidden)]
pub type TweenEvent<Data> = crate::tween_event::TweenEvent<Data>;

#[deprecated(
since = "0.6.0",
note = "use `bevy_tween::tween_event::TweenEventData` instead"
)]
#[allow(missing_docs)]
#[doc(hidden)]
pub type TweenEventData<Data> = crate::tween_event::TweenEventData<Data>;

#[deprecated(
since = "0.6.0",
note = "use `bevy_tween::tween_event::DefaultTweenEventPlugins` instead"
)]
#[allow(missing_docs)]
#[doc(hidden)]
pub type DefaultTweenEventsPlugin =
crate::tween_event::DefaultTweenEventPlugins;

#[deprecated(
since = "0.6.0",
note = "use `bevy_tween::tween_event::tween_event_system` instead or `TweenEventPlugin` if you're registering custom tween event"
)]
#[allow(missing_docs)]
#[doc(hidden)]
#[allow(deprecated)]
#[allow(clippy::type_complexity)]
pub fn tween_event_system<Data>(
q_tween_event_data: Query<
(
Entity,
&TweenEventData<Data>,
&bevy_time_runner::TimeSpanProgress,
Option<&TweenInterpolationValue>,
),
Without<SkipTween>,
>,
event_writer: EventWriter<TweenEvent<Data>>,
) where
Data: Clone + Send + Sync + 'static,
{
crate::tween_event::tween_event_system(q_tween_event_data, event_writer)
}

#[deprecated(
since = "0.6.0",
note = "use `bevy_tween::tween_event::tween_event_taking_system` instead or `TweenEventTakingPlugin` if you're registering custom tween event"
)]
#[allow(missing_docs)]
#[doc(hidden)]
#[allow(deprecated)]
#[allow(clippy::type_complexity)]
pub fn tween_event_taking_system<Data>(
q_tween_event_data: Query<
(
Entity,
&mut TweenEventData<Data>,
&bevy_time_runner::TimeSpanProgress,
Option<&TweenInterpolationValue>,
),
Without<SkipTween>,
>,
event_writer: EventWriter<TweenEvent<Data>>,
) where
Data: Send + Sync + 'static,
{
crate::tween_event::tween_event_taking_system(
q_tween_event_data,
event_writer,
)
}
Loading

0 comments on commit 38c7a75

Please sign in to comment.