Skip to content
Snippets Groups Projects
Commit 07b94b74 authored by Jerome Humbert's avatar Jerome Humbert
Browse files

Add sequence and tracks for complex animations

- Add `Sequence<T>` for chained tweens
- Add `Tracks<T>` for tracks of sequences running in parallel
- Move most animation-related properties to the new `Tweens<T>` struct
- Add `sequence` example
parent 65d19bd5
No related branches found
No related tags found
No related merge requests found
......@@ -7,9 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add `Animator<T>::is_paused()` and `AssetAnimator<T>::is_paused()` to query when a tweening animation is in its pause phase, if any.
- Add `Animator<T>::direction()` and `AssetAnimator<T>::direction()` to query the playback direction of a tweening animation (forward or backward).
- Add `Animator<T>::progress()` and `AssetAnimator<T>::progress()` to query the progres ratio in [0:1] of a tweening animation.
- Add `Tween<T>` describing a single tween animation, independently of its target (asset or component).
- Add `Tween<T>::is_paused()` to query when a tweening animation is in its pause phase, if any.
- Add `Tween<T>::direction()` to query the playback direction of a tweening animation (forward or backward).
- Add `Tween<T>::progress()` to query the progres ratio in [0:1] of a tweening animation.
- Enable multiple lenses per animator via "tracks", the ability to add multiple tween animations running in parallel on the same component.
- Enable sequences of tween animations running serially, one after the other, for each track of an animator, allowing to create more complex animations.
### Fixed
- Perform spherical linear interpolation (slerp) for `Quat` rotation of `Transform` animation via `TransformRotationLens`, instead of mere linear interpolation leaving the quaternion non-normalized.
## [0.2.0] - 2022-01-09
......
......@@ -5,6 +5,12 @@
Tweening animation plugin for the Bevy game engine.
## Features
- [x] Versatile customizable lens system to animate any field of any component or asset.
- [x] Sequence of tweening animations chained together, running one after the other, to create complex animations.
- [x] Multiple tweening animations per component, running in parallel, to animate different fields with different parameters.
## Usage
### System setup
......@@ -33,12 +39,13 @@ commands
},
..Default::default()
})
// Add an Animator component to perform the animation
// Add an Animator component to perform the animation. This is a shortcut to
// create both an Animator and a Tween, and assign the Tween to the Animator.
.insert(Animator::new(
// Use a quadratic easing on both endpoints
EaseFunction::QuadraticInOut,
// Loop animation back and forth over 1 second, with a 0.5 second
// pause after each cycle (start -> end -> start).
// pause after each cycle (start -> end -> start -> pause -> ...).
TweeningType::PingPong {
duration: Duration::from_secs(1),
pause: Some(Duration::from_millis(500)),
......@@ -55,7 +62,9 @@ commands
## Predefined Lenses
The naming scheme for predefined lenses is `"<TargetName><FieldName>Lens"`, where `<TargetName>` is the name of the target component or asset which is queried, and `<FieldName>` is the field which is mutated in place.
A small number of predefined lenses are available for the most common use cases, which also serve as examples. Users are encouraged to write their own lens to tailor the animation to their use case.
The naming scheme for predefined lenses is `"<TargetName><FieldName>Lens"`, where `<TargetName>` is the name of the target Bevy component or asset type which is queried by the internal animation system to be modified, and `<FieldName>` is the field which is mutated in place by the lens. All predefined lenses modify a single field. Custom lenses can be written which modify multiple fields at once.
### Bevy Components
......@@ -74,27 +83,26 @@ The naming scheme for predefined lenses is `"<TargetName><FieldName>Lens"`, wher
|---|---|---|
| [`ColorMaterial`](https://docs.rs/bevy/0.6.0/bevy/sprite/struct.ColorMaterial.html) | [`color`](https://docs.rs/bevy/0.6.0/bevy/sprite/struct.ColorMaterial.html#structfield.color) | [`ColorMaterialColorLens`](https://docs.rs/bevy_tweening/latest/bevy_tweening/struct.ColorMaterialColorLens.html) |
### Custom component support
### Custom lens
To be able to animate some fields of a custom component, a custom lens need to be implemented for that component, which **linearly** interpolates the field(s) of that component.
A custom lens allows animating any field or group of fields of a Bevy component or asset. A custom lens is a type implementing the `Lens` trait, which is generic over the type of component or asset.
```rust
#[derive(Component)]
struct CustomComponent(f32);
struct CustomLens {
struct MyXAxisLens {
start: f32,
end: f32,
}
impl Lens<CustomComponent> for CustomLens {
fn lerp(&self, target: &mut CustomComponent, ratio: f32) -> f32 {
target.0 = self.start + (self.end - self.start) * ratio;
impl Lens<Tranform> for MyXAxisLens {
fn lerp(&self, target: &mut Tranform, ratio: f32) -> f32 {
let start = Vec3::new(self.start, 0., 0.);
let end = Vec3::new(self.end, 0., 0.);
target.translation = start + (end - start) * ratio;
}
}
```
This process can also be used to interpolate fields of existing Bevy built-in components for which a predfined lens is not provided.
Note that the lens always **linearly** interpolates the field(s) of the component or asset. The type of easing applied modifies the rate at which the `ratio` parameter evolves, and is applied before the `lerp()` function is invoked.
The basic formula for lerp (linear interpolation) is either of:
......@@ -103,7 +111,27 @@ The basic formula for lerp (linear interpolation) is either of:
The two formulations are mathematically equivalent, but one may be more suited than the other depending on the type interpolated and the operations available, and the potential floating-point precision errors.
Then, the system `component_animator_system::<CustomComponent>` needs to be added to the application.
### Custom component support
Custom components are animated like built-in Bevy ones, via a lens.
```rust
#[derive(Component)]
struct MyCustomComponent(f32);
struct MyCustomLens {
start: f32,
end: f32,
}
impl Lens<MyCustomComponent> for MyCustomLens {
fn lerp(&self, target: &mut MyCustomComponent, ratio: f32) -> f32 {
target.0 = self.start + (self.end - self.start) * ratio;
}
}
```
Then, in addition, the system `component_animator_system::<CustomComponent>` needs to be added to the application. This system will extract each frame all `CustomComponent` instances with an `Animator<CustomComponent>` on the same entity, and animate the component via its animator.
## Custom asset support
......@@ -153,6 +181,14 @@ cargo run --example ui_position --features="bevy/bevy_winit"
![ui_position](https://raw.githubusercontent.com/djeedai/bevy_tweening/main/examples/ui_position.gif)
### [`sequence`](examples/sequence.rs)
```rust
cargo run --example sequence --features="bevy/bevy_winit"
```
![sequence](https://raw.githubusercontent.com/djeedai/bevy_tweening/main/examples/sequence.gif)
## Ease Functions
Many [ease functions](https://docs.rs/interpolation/0.2.0/interpolation/enum.EaseFunction.html) are available:
......
examples/sequence.gif

58.6 KiB

use bevy::prelude::*;
use bevy_tweening::*;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
App::default()
.insert_resource(WindowDescriptor {
title: "Sequence".to_string(),
width: 600.,
height: 600.,
vsync: true,
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(TweeningPlugin)
.add_startup_system(setup)
.run();
Ok(())
}
fn setup(mut commands: Commands) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
let size = 25.;
let margin = 40.;
let screen_x = 600.;
let screen_y = 600.;
let center = Vec3::new(screen_x / 2., screen_y / 2., 0.);
let dests = &[
Vec3::new(margin, margin, 0.),
Vec3::new(screen_x - margin, margin, 0.),
Vec3::new(screen_x - margin, screen_y - margin, 0.),
Vec3::new(margin, screen_y - margin, 0.),
Vec3::new(margin, margin, 0.),
];
let tweens = dests
.windows(2)
.map(|pair| {
Tween::new(
EaseFunction::QuadraticInOut,
TweeningType::Once {
duration: Duration::from_secs(1),
},
TransformPositionLens {
start: pair[0] - center,
end: pair[1] - center,
},
)
})
.collect();
commands
.spawn_bundle(SpriteBundle {
sprite: Sprite {
color: Color::RED,
custom_size: Some(Vec2::new(size, size)),
..Default::default()
},
..Default::default()
})
.insert(Animator::new_seq(tweens));
}
......@@ -90,8 +90,7 @@ pub struct TransformRotationLens {
impl Lens<Transform> for TransformRotationLens {
fn lerp(&mut self, target: &mut Transform, ratio: f32) {
let value = self.start + (self.end - self.start) * ratio;
target.rotation = value;
target.rotation = self.start.slerp(self.end, ratio); // FIXME - This slerps the shortest path only! https://docs.rs/bevy/latest/bevy/math/struct.Quat.html#method.slerp
}
}
......
......@@ -226,30 +226,18 @@ impl std::ops::Not for TweeningDirection {
}
}
/// Component to control the animation of another component.
#[derive(Component)]
pub struct Animator<T> {
/// Single tweening animation instance.
pub struct Tween<T> {
ease_function: EaseMethod,
timer: Timer,
/// Control if this animation is played or not.
pub state: AnimatorState,
paused: bool,
tweening_type: TweeningType,
direction: TweeningDirection,
lens: Box<dyn Lens<T> + Send + Sync + 'static>,
}
impl<T: std::fmt::Debug> std::fmt::Debug for Animator<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Animator")
.field("state", &self.state)
.finish()
}
}
impl<T> Animator<T> {
/// Create a new animator component from an easing function, tweening type, and a lens.
/// The type `T` of the component to animate can generally be deducted from the lens type itself.
impl<T> Tween<T> {
/// Create a new tween animation.
pub fn new<L>(
ease_function: impl Into<EaseMethod>,
tweening_type: TweeningType,
......@@ -258,14 +246,13 @@ impl<T> Animator<T> {
where
L: Lens<T> + Send + Sync + 'static,
{
Animator {
Tween {
ease_function: ease_function.into(),
timer: match tweening_type {
TweeningType::Once { duration } => Timer::new(duration, false),
TweeningType::Loop { duration, .. } => Timer::new(duration, false),
TweeningType::PingPong { duration, .. } => Timer::new(duration, false),
},
state: AnimatorState::Playing,
paused: false,
tweening_type,
direction: TweeningDirection::Forward,
......@@ -302,23 +289,175 @@ impl<T> Animator<T> {
}
}
fn tick(&mut self, delta: Duration, target: &mut T) {
self.timer.tick(delta);
if self.paused {
if self.timer.just_finished() {
match self.tweening_type {
TweeningType::Once { duration } => {
self.timer.set_duration(duration);
}
TweeningType::Loop { duration, .. } => {
self.timer.set_duration(duration);
}
TweeningType::PingPong { duration, .. } => {
self.timer.set_duration(duration);
}
}
self.timer.reset();
self.paused = false;
}
} else {
if self.timer.duration().as_secs_f32() != 0. {
let progress = self.progress();
let factor = self.ease_function.sample(progress);
self.apply(target, factor);
}
if self.timer.finished() {
match self.tweening_type {
TweeningType::Once { .. } => {
//commands.entity(entity).remove::<Animator>();
}
TweeningType::Loop { pause, .. } => {
if let Some(pause) = pause {
self.timer.set_duration(pause);
self.paused = true;
}
self.timer.reset();
}
TweeningType::PingPong { pause, .. } => {
if let Some(pause) = pause {
self.timer.set_duration(pause);
self.paused = true;
}
self.timer.reset();
self.direction = !self.direction;
}
}
}
}
}
#[inline(always)]
fn apply(&mut self, target: &mut T, ratio: f32) {
self.lens.lerp(target, ratio);
}
}
struct Sequence<T> {
tweens: Vec<Tween<T>>,
index: usize,
}
impl<T> Sequence<T> {
pub fn new<I>(tweens: I) -> Self
where
I: IntoIterator<Item = Tween<T>>,
{
Sequence {
tweens: tweens.into_iter().collect(),
index: 0,
}
}
pub fn from_single(tween: Tween<T>) -> Self {
Sequence {
tweens: vec![tween],
index: 0,
}
}
fn tick(&mut self, delta: Duration, target: &mut T) {
if self.index < self.tweens.len() {
let tween = &mut self.tweens[self.index];
tween.tick(delta, target);
if tween.progress() >= 1.0 {
self.index += 1;
}
}
}
}
struct Tracks<T> {
tracks: Vec<Sequence<T>>,
}
/// Component to control the animation of another component.
#[derive(Component)]
pub struct Animator<T: Component> {
/// Control if this animation is played or not.
pub state: AnimatorState,
tracks: Tracks<T>,
}
impl<T: Component + std::fmt::Debug> std::fmt::Debug for Animator<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Animator")
.field("state", &self.state)
.finish()
}
}
impl<T: Component> Animator<T> {
/// Create a new animator component from an easing function, tweening type, and a lens.
/// The type `T` of the component to animate can generally be deducted from the lens type itself.
/// This creates a new [`Tween`] instance then assign it to a newly created animator.
pub fn new<L>(
ease_function: impl Into<EaseMethod>,
tweening_type: TweeningType,
lens: L,
) -> Self
where
L: Lens<T> + Send + Sync + 'static,
{
let tween = Tween::new(ease_function, tweening_type, lens);
Animator {
state: AnimatorState::Playing,
tracks: Tracks {
tracks: vec![Sequence::from_single(tween)],
},
}
}
/// Create a new animator component from a single tween instance.
pub fn new_single(tween: Tween<T>) -> Self {
Animator {
state: AnimatorState::Playing,
tracks: Tracks {
tracks: vec![Sequence::from_single(tween)],
},
}
}
/// Create a new animator component from a sequence of tween instances.
/// The tweens are played in order, one after the other. They all must be non-looping.
pub fn new_seq(tweens: Vec<Tween<T>>) -> Self {
for t in &tweens {
assert!(matches!(t.tweening_type, TweeningType::Once { .. }));
}
Animator {
state: AnimatorState::Playing,
tracks: Tracks {
tracks: vec![Sequence::new(tweens)],
},
}
}
#[allow(dead_code)]
fn tracks(&self) -> &Tracks<T> {
&self.tracks
}
fn tracks_mut(&mut self) -> &mut Tracks<T> {
&mut self.tracks
}
}
/// Component to control the animation of an asset.
#[derive(Component)]
pub struct AssetAnimator<T: Asset> {
ease_function: EaseMethod,
timer: Timer,
/// Control if this animation is played or not.
pub state: AnimatorState,
paused: bool,
tweening_type: TweeningType,
direction: TweeningDirection,
lens: Box<dyn Lens<T> + Send + Sync + 'static>,
tracks: Tracks<T>,
handle: Handle<T>,
}
......@@ -343,48 +482,39 @@ impl<T: Asset> AssetAnimator<T> {
where
L: Lens<T> + Send + Sync + 'static,
{
let tween = Tween::new(ease_function, tweening_type, lens);
AssetAnimator {
ease_function: ease_function.into(),
timer: match tweening_type {
TweeningType::Once { duration } => Timer::new(duration, false),
TweeningType::Loop { duration, .. } => Timer::new(duration, false),
TweeningType::PingPong { duration, .. } => Timer::new(duration, false),
},
state: AnimatorState::Playing,
paused: false,
tweening_type,
direction: TweeningDirection::Forward,
lens: Box::new(lens),
tracks: Tracks {
tracks: vec![Sequence::from_single(tween)],
},
handle,
}
}
/// A boolean indicating whether the animation is currently in the pause phase of a loop.
///
/// The [`TweeningType::Loop`] and [`TweeningType::PingPong`] tweening types are looping over
/// infinitely, with an optional pause between each loop. This function returns `true` if the
/// animation is currently under such pause. For [`TweeningType::Once`], which has no pause,
/// this always returns `false`.
pub fn is_paused(&self) -> bool {
self.paused
}
/// The current animation direction.
///
/// See [`TweeningDirection`] for details.
pub fn direction(&self) -> TweeningDirection {
self.direction
/// Create a new animator component from a single tween instance.
pub fn new_single(handle: Handle<T>, tween: Tween<T>) -> Self {
AssetAnimator {
state: AnimatorState::Playing,
tracks: Tracks {
tracks: vec![Sequence::from_single(tween)],
},
handle,
}
}
/// Current animation progress ratio between 0 and 1.
///
/// For reversed playback ([`TweeningDirection::Backward`]), the ratio goes from 0 at the
/// end point (beginning of backward playback) to 1 at the start point (end of backward
/// playback).
pub fn progress(&self) -> f32 {
match self.direction {
TweeningDirection::Forward => self.timer.percent(),
TweeningDirection::Backward => self.timer.percent_left(),
/// Create a new animator component from a sequence of tween instances.
/// The tweens are played in order, one after the other. They all must be non-looping.
pub fn new_seq(handle: Handle<T>, tweens: Vec<Tween<T>>) -> Self {
for t in &tweens {
assert!(matches!(t.tweening_type, TweeningType::Once { .. }));
}
AssetAnimator {
state: AnimatorState::Playing,
tracks: Tracks {
tracks: vec![Sequence::new(tweens)],
},
handle,
}
}
......@@ -392,15 +522,106 @@ impl<T: Asset> AssetAnimator<T> {
self.handle.clone()
}
#[inline(always)]
fn apply(&mut self, target: &mut T, ratio: f32) {
self.lens.lerp(target, ratio);
#[allow(dead_code)]
fn tracks(&self) -> &Tracks<T> {
&self.tracks
}
fn tracks_mut(&mut self) -> &mut Tracks<T> {
&mut self.tracks
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tween_tick() {
let mut tween = Tween {
ease_function: EaseMethod::Linear,
timer: Timer::from_seconds(1.0, false),
paused: false,
tweening_type: TweeningType::Once {
duration: Duration::from_secs_f32(1.0),
},
direction: TweeningDirection::Forward,
lens: Box::new(TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
}),
};
let mut transform = Transform::default();
tween.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform, Transform::from_translation(Vec3::splat(0.2)));
tween.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform, Transform::from_translation(Vec3::splat(0.4)));
tween.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform, Transform::from_translation(Vec3::splat(0.6)));
tween.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform, Transform::from_translation(Vec3::splat(0.8)));
tween.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform, Transform::from_translation(Vec3::splat(1.0)));
tween.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform, Transform::from_translation(Vec3::splat(1.0)));
}
#[test]
fn seq_tick() {
let tween1 = Tween {
ease_function: EaseMethod::Linear,
timer: Timer::from_seconds(1.0, false),
paused: false,
tweening_type: TweeningType::Once {
duration: Duration::from_secs_f32(1.0),
},
direction: TweeningDirection::Forward,
lens: Box::new(TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
}),
};
let tween2 = Tween {
ease_function: EaseMethod::Linear,
timer: Timer::from_seconds(1.0, false),
paused: false,
tweening_type: TweeningType::Once {
duration: Duration::from_secs_f32(1.0),
},
direction: TweeningDirection::Forward,
lens: Box::new(TransformRotationLens {
start: Quat::IDENTITY,
end: Quat::from_rotation_x(180_f32.to_radians()),
}),
};
let mut seq = Sequence::new([tween1, tween2]);
let mut transform = Transform::default();
// First, translation alone (0->1)
seq.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform, Transform::from_translation(Vec3::splat(0.2)));
seq.tick(Duration::from_secs_f32(0.8), &mut transform);
assert_eq!(transform, Transform::from_translation(Vec3::splat(1.0)));
// Then, rotation alone, on top of final translation (1->2)
seq.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform.translation, Vec3::splat(1.0));
assert!(transform
.rotation
.abs_diff_eq(Quat::from_rotation_x(36_f32.to_radians()), 1e-5));
seq.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform.translation, Vec3::splat(1.0));
assert!(transform
.rotation
.abs_diff_eq(Quat::from_rotation_x(72_f32.to_radians()), 1e-5));
seq.tick(Duration::from_secs_f32(0.6), &mut transform);
assert_eq!(transform.translation, Vec3::splat(1.0));
assert!(transform
.rotation
.abs_diff_eq(Quat::from_rotation_x(180_f32.to_radians()), 1e-5));
seq.tick(Duration::from_secs_f32(0.2), &mut transform);
assert_eq!(transform.translation, Vec3::splat(1.0));
assert!(transform
.rotation
.abs_diff_eq(Quat::from_rotation_x(180_f32.to_radians()), 1e-5));
}
#[test]
fn animator_new() {
......@@ -415,9 +636,14 @@ mod tests {
end: Quat::from_axis_angle(Vec3::Z, std::f32::consts::PI / 2.),
},
);
assert_eq!(animator.is_paused(), false);
assert_eq!(animator.direction(), TweeningDirection::Forward);
assert_eq!(animator.progress(), 0.);
let tracks = animator.tracks();
assert_eq!(tracks.tracks.len(), 1);
let seq = &tracks.tracks[0];
assert_eq!(seq.tweens.len(), 1);
let tween = &seq.tweens[0];
assert_eq!(tween.is_paused(), false);
assert_eq!(tween.direction(), TweeningDirection::Forward);
assert_eq!(tween.progress(), 0.);
}
#[test]
......@@ -434,8 +660,13 @@ mod tests {
end: Color::BLUE,
},
);
assert_eq!(animator.is_paused(), false);
assert_eq!(animator.direction(), TweeningDirection::Forward);
assert_eq!(animator.progress(), 0.);
let tracks = animator.tracks();
assert_eq!(tracks.tracks.len(), 1);
let seq = &tracks.tracks[0];
assert_eq!(seq.tweens.len(), 1);
let tween = &seq.tweens[0];
assert_eq!(tween.is_paused(), false);
assert_eq!(tween.direction(), TweeningDirection::Forward);
assert_eq!(tween.progress(), 0.);
}
}
use bevy::{asset::Asset, ecs::component::Component, prelude::*};
use crate::{Animator, AnimatorState, AssetAnimator, TweeningType};
use crate::{Animator, AnimatorState, AssetAnimator};
/// Plugin to add systems related to tweening
#[derive(Debug, Clone, Copy)]
......@@ -21,53 +21,12 @@ pub fn component_animator_system<T: Component>(
mut query: Query<(&mut T, &mut Animator<T>)>,
) {
for (ref mut target, ref mut animator) in query.iter_mut() {
if animator.state == AnimatorState::Playing {
animator.timer.tick(time.delta());
if animator.state == AnimatorState::Paused {
continue;
}
if animator.paused {
if animator.timer.just_finished() {
match animator.tweening_type {
TweeningType::Once { duration } => {
animator.timer.set_duration(duration);
}
TweeningType::Loop { duration, .. } => {
animator.timer.set_duration(duration);
}
TweeningType::PingPong { duration, .. } => {
animator.timer.set_duration(duration);
}
}
animator.timer.reset();
animator.paused = false;
}
} else {
if animator.timer.duration().as_secs_f32() != 0. {
let progress = animator.progress();
let factor = animator.ease_function.sample(progress);
animator.apply(target, factor);
}
if animator.timer.finished() {
match animator.tweening_type {
TweeningType::Once { .. } => {
//commands.entity(entity).remove::<Animator>();
}
TweeningType::Loop { pause, .. } => {
if let Some(pause) = pause {
animator.timer.set_duration(pause);
animator.paused = true;
}
animator.timer.reset();
}
TweeningType::PingPong { pause, .. } => {
if let Some(pause) = pause {
animator.timer.set_duration(pause);
animator.paused = true;
}
animator.timer.reset();
animator.direction = !animator.direction;
}
}
}
// Play all tracks in parallel
for seq in &mut animator.tracks_mut().tracks {
seq.tick(time.delta(), target);
}
}
}
......@@ -78,54 +37,13 @@ pub fn asset_animator_system<T: Asset>(
mut query: Query<&mut AssetAnimator<T>>,
) {
for ref mut animator in query.iter_mut() {
if animator.state == AnimatorState::Playing {
animator.timer.tick(time.delta());
if animator.state == AnimatorState::Paused {
continue;
}
if animator.paused {
if animator.timer.just_finished() {
match animator.tweening_type {
TweeningType::Once { duration } => {
animator.timer.set_duration(duration);
}
TweeningType::Loop { duration, .. } => {
animator.timer.set_duration(duration);
}
TweeningType::PingPong { duration, .. } => {
animator.timer.set_duration(duration);
}
}
animator.timer.reset();
animator.paused = false;
}
} else {
if animator.timer.duration().as_secs_f32() != 0. {
let progress = animator.progress();
let factor = animator.ease_function.sample(progress);
if let Some(target) = assets.get_mut(animator.handle()) {
animator.apply(target, factor);
}
}
if animator.timer.finished() {
match animator.tweening_type {
TweeningType::Once { .. } => {
//commands.entity(entity).remove::<Animator>();
}
TweeningType::Loop { pause, .. } => {
if let Some(pause) = pause {
animator.timer.set_duration(pause);
animator.paused = true;
}
animator.timer.reset();
}
TweeningType::PingPong { pause, .. } => {
if let Some(pause) = pause {
animator.timer.set_duration(pause);
animator.paused = true;
}
animator.timer.reset();
animator.direction = !animator.direction;
}
}
if let Some(target) = assets.get_mut(animator.handle()) {
// Play all tracks in parallel
for seq in &mut animator.tracks_mut().tracks {
seq.tick(time.delta(), target);
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment