Skip to content

Commit

Permalink
Asset animator uses Handle<T> on same entity (#107)
Browse files Browse the repository at this point in the history
Change asset animation to behave like component animation, and retrieve
the `Handle<T>` component referencing the asset to animate directly from
a Bevy query on the same `Entity` instead of specifying it manually in
the `AssetAnimator` itself.

Task: #101
  • Loading branch information
djeedai authored Oct 28, 2023
1 parent 7a03bdf commit 89bd32c
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 30 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- The `AssetAnimator<T>` doesn't take any `Handle<T>` anymore; instead the `asset_animator_system::<T>()` retrieves the handle of the asset to animate from the same `Entity` the `AssetAnimator<T>` is attached to. This aligns the behavior with component animation. (#101)

### Fixed

- Fixed a panic when a `TextLens` tried to change a text section that wasn't present.
Expand Down
4 changes: 2 additions & 2 deletions examples/colormaterial_color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ fn setup(
mesh: quad_mesh.clone(),
transform: Transform::from_translation(Vec3::new(x, y, 0.))
.with_scale(Vec3::splat(size)),
material: unique_material.clone(),
material: unique_material,
..default()
},
AssetAnimator::new(unique_material.clone(), tween),
AssetAnimator::new(tween),
));
y -= size * spacing;
if y < -screen_y {
Expand Down
12 changes: 7 additions & 5 deletions src/lens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl Lens<Text> for TextColorLens {
let end: Vec4 = self.end.into();
let value = start.lerp(end, ratio);

if let Some(section) = target.sections.get_mut(self.section){
if let Some(section) = target.sections.get_mut(self.section) {
section.style.color = value.into();
}
}
Expand Down Expand Up @@ -332,7 +332,6 @@ pub struct UiBackgroundColorLens {
pub end: Color,
}


#[cfg(feature = "bevy_ui")]
impl Lens<BackgroundColor> for UiBackgroundColorLens {
fn lerp(&mut self, target: &mut BackgroundColor, ratio: f32) {
Expand Down Expand Up @@ -418,17 +417,20 @@ mod tests {
lens.lerp(&mut text, 0.3);
assert_eq!(text.sections[0].style.color, Color::rgba(0.7, 0., 0.3, 1.0));

let mut lens_section1 = TextColorLens{
let mut lens_section1 = TextColorLens {
start: Color::RED,
end: Color::BLUE,
section: 1,
};

lens_section1.lerp(&mut text, 1.);
//Should not have changed because the lens targets section 1
// Should not have changed because the lens targets section 1
assert_eq!(text.sections[0].style.color, Color::rgba(0.7, 0., 0.3, 1.0));

text.sections.push(TextSection { value: "".to_string(), style: Default::default() });
text.sections.push(TextSection {
value: "".to_string(),
style: Default::default(),
});

lens_section1.lerp(&mut text, 0.3);
assert_eq!(text.sections[1].style.color, Color::rgba(0.7, 0., 0.3, 1.0));
Expand Down
37 changes: 17 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,9 @@ macro_rules! animator_impl {
}

/// Component to control the animation of another component.
///
/// The animated component is the component located on the same entity as the
/// [`Animator<T>`] itself.
#[derive(Component)]
pub struct Animator<T: Component> {
/// Control if this animation is played or not.
Expand Down Expand Up @@ -500,13 +503,15 @@ impl<T: Component> Animator<T> {
}

/// Component to control the animation of an asset.
///
/// The animated asset is the asset referenced by a [`Handle<T>`] component
/// located on the same entity as the [`AssetAnimator<T>`] itself.
#[cfg(feature = "bevy_asset")]
#[derive(Component)]
pub struct AssetAnimator<T: Asset> {
/// Control if this animation is played or not.
pub state: AnimatorState,
tweenable: BoxedTweenable<T>,
handle: Handle<T>,
speed: f32,
}

Expand All @@ -523,21 +528,15 @@ impl<T: Asset + std::fmt::Debug> std::fmt::Debug for AssetAnimator<T> {
impl<T: Asset> AssetAnimator<T> {
/// Create a new asset animator component from a single tweenable.
#[must_use]
pub fn new(handle: Handle<T>, tween: impl Tweenable<T> + 'static) -> Self {
pub fn new(tween: impl Tweenable<T> + 'static) -> Self {
Self {
state: default(),
tweenable: Box::new(tween),
handle,
speed: 1.,
}
}

animator_impl!();

#[must_use]
fn handle(&self) -> Handle<T> {
self.handle.clone()
}
}

#[cfg(test)]
Expand Down Expand Up @@ -773,9 +772,8 @@ mod tests {
Duration::from_secs(1),
DummyLens { start: 0., end: 1. },
);
let animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween);
let animator = AssetAnimator::new(tween);
assert_eq!(animator.state, AnimatorState::default());
assert_eq!(animator.handle(), Handle::<DummyAsset>::default());
let tween = animator;
assert_eq!(tween.tweenable().progress(), 0.);
}
Expand All @@ -789,8 +787,7 @@ mod tests {
Duration::from_secs(1),
DummyLens { start: 0., end: 1. },
);
let animator =
AssetAnimator::new(Handle::<DummyAsset>::default(), tween).with_state(state);
let animator = AssetAnimator::new(tween).with_state(state);
assert_eq!(animator.state, state);

// impl Debug
Expand All @@ -805,12 +802,12 @@ mod tests {
#[cfg(feature = "bevy_asset")]
#[test]
fn asset_animator_controls() {
let tween = Tween::new(
let tween: Tween<DummyAsset> = Tween::new(
EaseFunction::QuadraticInOut,
Duration::from_secs(1),
DummyLens { start: 0., end: 1. },
);
let mut animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween);
let mut animator = AssetAnimator::new(tween);
assert_eq!(animator.state, AnimatorState::Playing);
assert_approx_eq!(animator.tweenable().progress(), 0.);

Expand Down Expand Up @@ -843,37 +840,37 @@ mod tests {
#[cfg(feature = "bevy_asset")]
#[test]
fn asset_animator_speed() {
let tween = Tween::new(
let tween: Tween<DummyAsset> = Tween::new(
EaseFunction::QuadraticInOut,
Duration::from_secs(1),
DummyLens { start: 0., end: 1. },
);

let mut animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween);
let mut animator = AssetAnimator::new(tween);
assert_approx_eq!(animator.speed(), 1.); // default speed

animator.set_speed(2.4);
assert_approx_eq!(animator.speed(), 2.4);

let tween = Tween::new(
let tween: Tween<DummyAsset> = Tween::new(
EaseFunction::QuadraticInOut,
Duration::from_secs(1),
DummyLens { start: 0., end: 1. },
);

let animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween).with_speed(3.5);
let animator = AssetAnimator::new(tween).with_speed(3.5);
assert_approx_eq!(animator.speed(), 3.5);
}

#[cfg(feature = "bevy_asset")]
#[test]
fn asset_animator_set_tweenable() {
let tween = Tween::new(
let tween: Tween<DummyAsset> = Tween::new(
EaseFunction::QuadraticInOut,
Duration::from_secs(1),
DummyLens { start: 0., end: 1. },
);
let mut animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween);
let mut animator = AssetAnimator::new(tween);

let tween2 = Tween::new(
EaseFunction::QuadraticInOut,
Expand Down
6 changes: 3 additions & 3 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,14 @@ pub fn component_animator_system<T: Component>(
pub fn asset_animator_system<T: Asset>(
time: Res<Time>,
assets: ResMut<Assets<T>>,
mut query: Query<(Entity, &mut AssetAnimator<T>)>,
mut query: Query<(Entity, &Handle<T>, &mut AssetAnimator<T>)>,
events: ResMut<Events<TweenCompleted>>,
) {
let mut events: Mut<Events<TweenCompleted>> = events.into();
let mut target = AssetTarget::new(assets);
for (entity, mut animator) in query.iter_mut() {
for (entity, handle, mut animator) in query.iter_mut() {
if animator.state != AnimatorState::Paused {
target.handle = animator.handle().clone();
target.handle = handle.clone();
if !target.is_valid() {
continue;
}
Expand Down

0 comments on commit 89bd32c

Please sign in to comment.