#bevy_tween

529 messages · Page 1 of 1 (latest)

zealous bay
north lagoon
#

trying to add this tween

self.commands.entity(entity).insert((
            SpanTweenerBundle::new(Duration::from_millis(500)).tween_here(),
            EaseFunction::BackIn,
            ComponentTween::new(interpolate::Translation { start, end }),
        ));

to my player object that was created earlier

commands
        .spawn((
            Name::new("Player"),
            SceneBundle {
                scene: models.chara.clone(),
                transform: player_transform,
                ..default()
            },
...

nothing is happening when I insert the tween bundle in the first code block

#

I feel like im missing something basic, does this work out of the box with things other than sprites?

wooden zinc
#

Hmm yeah lets see.

#

This should works because the tween won't care about anything else if it found the transform.

north lagoon
#

here is an easier to read view

#

and I can use the inspector to see the components getting inserted correctly

wooden zinc
#

Hmm I'm seeing an Animator<Transform>. Is this running? It might be conflicting with the new tweens.

north lagoon
#

That is from previous use of bevy_tweening

#

let me make sure that doesn't get added

#

okay got rid of everything that could add that & am experiencing the same issue

wooden zinc
#

is move_player running every frame or atleast run in multiples frame consecutively?

north lagoon
#

it is run on update and the system functions should be gated to only execuate once per key press

#
impl Plugin for PlayerMovementPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Update, move_action);
    }
}

fn move_action(
    query: Query<&ActionState<PlayerAction>, With<PlayerInput>>,
    mut movement_system: MovementSystem,
) {
    let action_state = query.get_single();

    match action_state {
        Ok(action_state) => {
            if action_state.just_pressed(&PlayerAction::MoveUp) {
                movement_system.move_player(PlayerAction::MoveUp);
            } else if action_state.just_pressed(&PlayerAction::MoveDown) {
                movement_system.move_player(PlayerAction::MoveDown);
            } else if action_state.just_pressed(&PlayerAction::MoveLeft) {
                movement_system.move_player(PlayerAction::MoveLeft);
            } else if action_state.just_pressed(&PlayerAction::MoveRight) {
                movement_system.move_player(PlayerAction::MoveRight);
            }
        }
        Err(_) => todo!(),
    }
}
#

made a minimal example, still having the problem with a basic 3d mesh

wooden zinc
#

I'm sure that 3D does works because I just animate something with it yesterday. Maybe there's something else going on.

#

oh alright.

wooden zinc
north lagoon
#

HOLY SHIT

wooden zinc
#

does it works?

north lagoon
#

trying now

wooden zinc
#

love your detailed explanation btw :)

north lagoon
#

that was it !

#

gah, thank you for the sanity checking

#

okay excited to actually get into this plugin heh

wooden zinc
#

thanks for using my crate!

north lagoon
#

I am excited to try the parallel tweening abilites!

cunning heath
#

@wooden zinc is there a way to pause all running tween animations and resume at a later time? I wonder if there is a scheduled time to operate pause/resume functions, something like

fn system(time: Res<Time<TweenSchedule>>) {
  time.pause() // or unpause()
}
cunning heath
#

I'm experimenting with SkipTween in the following code (didn't work). I wonder if SkipTween is supposed to be put on entities with other components?

        match self {
            PlayState::Run => {
                let animations: Vec<Entity> = world
                    .query_filtered::<Entity, With<SkipTween>>()
                    .iter_mut(world)
                    .collect();
                for entity in animations {
                    world.entity_mut(entity).remove::<SkipTween>();
                }
            }
            PlayState::Pause => {
                let animations: Vec<Entity> = world
                    .query_filtered::<Entity, With<AnimationTarget>>()
                    .iter_mut(world)
                    .collect();
                for entity in animations {
                    world.entity_mut(entity).insert(SkipTween);
                }
            }
        }
wooden zinc
cunning heath
#

Do I have to use set_paused and SkipTimeRunner both?

wooden zinc
#

set_paused only pause the timer but do not disable tweens.

#

So depends if you want to edit tweening target’s value too.

Edit: Forgot to mention that leaving it them active also have performance overhead for doing nothing

cunning heath
#

Cool, tested and it worked! Thank you @wooden zinc

cunning heath
#

Quick question: Does Time<Virtual> pause tween animations?

wooden zinc
#

I’ve never tested this but all systems use Res<Time>.

cunning heath
#

According to the documentation, Res<Time> is backed up Time<Virtual>, so theoretically, I could pause all animations via that. I'll try that and report back what I found

cunning heath
#

Hmm... when I pause Time<Virtual> animations powered by bevy's AnimationPlayer are paused (including all shaders that rely on globals.time; but not those powered by bevy_tween crates. I have to pause TimeRunner and add SkipTimeRunner to pause those. That seems strange. Maybe I was doing something wrong...

wooden zinc
#

That’s weird. I’ve looked through animation_player and they used exactly the same resource

wooden zinc
#

@cunning heath Res<Time<Real>> is used in the ticking system… I have no idea why, when, or how I put that there but the system definitely doesnt suppose to use that

cunning heath
#

That is good to know. Are you planning to change that into Virtual in the next release?

wooden zinc
echo sundial
#

Does this crate work with 3d or just 2d?

wooden zinc
flat rover
#

Been a while since anyone needed help with tweens I suppose :P
Trying to figure out how to do a partially repeating tween. I have a sequence that fades in a text and then pulses the text and I want to only repeat the pulse.
So far I've tried spawning these effects as two separate animations and playing around with go/forward/backward but I haven't figured out the latter and the former is clunky because it's two animations running at the same time and modifying the same component.

wooden zinc
# flat rover Been a while since anyone needed help with tweens I suppose :P Trying to figure ...

You can use TweenEvent to extends functionality. By adding a custom event at the end of the animation then register a custom system that jump the animation back to that specific time-frame, you essentially create a partial repeating animation. Be sure to not use Repeat option so they don’t interfere with the custom repeat logic. Note that foward/backward/go does not modify the animation timing while it is running. It only change the timing parameter in which a tween start and end during creation. I decided to use those name so it is consistent, eg. “forward” instead of “delay” because I can’t find for other better names that’s consistant with the others.

#

Also you asked help at quite the right time😄I was a bit busy with school for the past few months so I had zero activity on the project but now I’m in school holiday so I can continue (but also might take awhile since i had another project in progress).

wooden zinc
#

Hmm is this crate still needed? The 0.15 animation might be actually good enough without the need for 3rd party crates.

#

Though I'm not really sure since I haven't try to work with them yet.

runic sleet
#

I've tried it out, and while there are great improvements it's still quite a few steps away from truly replacing tweening crates IMO.

#

I've taken another look around the ecosystem but IMO, bevy_tween is still the most ergonomic and powerful tweening library I've tried so far. I really hope that the upstream devs would take a serious look at it while developing their animation API further.

wooden zinc
#

Great to know. I'll continue to support later version then. I think I'd replace the interpolator trait with the shiny new animatable property and easingfunctions.

runic sleet
#

Is there a way to not reset the animation at the end of a non-looping animation? I want to translate an entity from one point to another, but the position is always reset to the beginning at the end of the animation.

wooden zinc
#

I believe that shouldn't happen?

runic sleet
#

It works now, must've been cosmic rays 😂

wooden zinc
cunning heath
#

@wooden zinc how does bevy_tween implement the animation on StandardMaterial? When I was manually updating the material color in bevy, it has significant performance impact on iOS (desktop seems fine). It seems to me bevy_tween's animation on StandardMaterial is reasonably fast. I'm curious about how you implemented it. Thanks

wooden zinc
cunning heath
#

I'm using

            .add_tween_systems((
                asset_dyn_tween_system::<StandardMaterial>(),
                asset_tween_system::<EmissiveColor>(),
            ))


impl Interpolator for EmissiveColor {
    type Item = StandardMaterial;

    fn interpolate(&self, item: &mut Self::Item, value: f32) {
        item.emissive = self.start.lerp(&self.end, value);
    }
}

My question is mainly about how frequently do you update the material's emissive color

wooden zinc
#

you might need to do a few spot benchmark. I’m pretty sure I’ve not done any special for different interpolators.

cunning heath
#

Do you update it in the Update schedule?

cunning heath
#

Feature request: Updating material's uniforms can be quite expensive, esp on mobile platforms. It would be great if bevy_tween offer a customization to allow it run skip frame instead of every frame.

runic sleet
#

Will this crate be updated for 0.15 soon?

wooden zinc
runic sleet
#

nice thanks 🥳

wooden zinc
cunning heath
#

How do I do that based on the following example

            commands.animation().insert(animate_death_color(
                target,
                Duration::from_secs_f32(duration),
            ));



fn animate_death_color(
    target: TargetAsset<StandardMaterial>,
    duration: Duration,
) -> impl FnOnce(&mut AnimationCommands, &mut Duration) {
    sequence((
        tween(
            duration.mul_f32(0.4),
            EaseFunction::Linear,
            target.with(EmissiveColor {
                start: LinearRgba::new(0.0, 0.0, 0.0, 0.0),
                end: LinearRgba::rgb(10.0, 1.0, 1.0),
            }),
        ),
        tween(
            duration.mul_f32(0.6),
            EaseFunction::Linear,
            target.with(EmissiveColor {
                start: LinearRgba::rgb(10.0, 1.0, 1.0),
                end: LinearRgba::rgb(10.0, 10.0, 10.0),
            }),
        ),
        tween(
            duration.mul_f32(0.1),
            EaseFunction::Linear,
            target.with(EmissiveColor {
                start: LinearRgba::rgb(10.0, 10.0, 10.0),
                end: LinearRgba::new(0.0, 0.0, 0.0, 0.0),
            }),
        ),
    ))
}
wooden zinc
#

Bevy powered™️

#

In the future version the system registration will be a plugin instead. Seems like this is a useful case, I’ll add a simple configuration option for the plugin too.

cunning heath
#

Are you suggesting add inthe run condition here?

 app.add_plugins(DefaultTweenPlugins)
            .add_tween_systems((
                asset_dyn_tween_system::<StandardMaterial>(),
                asset_tween_system::<EmissiveColor>(),
            ))
wooden zinc
#

Yep

cunning heath
#

Like this?

  .add_tween_systems((
                asset_dyn_tween_system::<StandardMaterial>().run_if(skip_frame_check_here()),
                asset_tween_system::<EmissiveColor>().run_if(skip_frame_check_here()),
            ))
wooden zinc
#

Yeah, that looks good.

cunning heath
#

Nice. Let me try that. Thanks @wooden zinc

cunning heath
#

@wooden zinc tested and it worked. However, this approach comes with some limitations: If the animation runs with duration of let's say 1.0 sec and I configured it to run every 0.1 sec. Then it depends on when the animation starts. I could end up lose the last frame update if the animation start at 0.05 sec mark. So the end value of the animation may end up not matching the actual end value specified. This is less an issue for long duration animation but is more likely to happen for short duration animations

wooden zinc
#

Well thats unfortunate

#

I dont like that

#

Im gonna have a long and hard think in the toilet

cunning heath
#

lol, sorry about that. I guess the tween_system is looking up the animation config and update the object. When animation config finishes, it's removed from the game, so the next time the tween_system runs, it can't find the animation so end up not doing the update right? I wonder if it is possible to remove the animation config after tween_system successfully update the object

wooden zinc
#

Yeah, that would require some changes internally. In the mean time, one possible solution is you could configure the run condition to execute if it detects any animation ending which would resolve the issue. It may not be as performance if there are many multiple animations ending near each other.

cunning heath
#

That might work. Does bevy_tween emit/trigger any event when an animation finishes? It would be easier if that is the case; Otherwise I have to maintain the animation status inside the game logic

wooden zinc
#

Yep, though not bevy_tween directly. TimeRunnerEnded is emitted every time the timing is at the end of the timeline. The animation config is still available until the next frame.

wooden zinc
#

@cunning heath Another solution: Instead of animating on the value directly, animate an intermediate component to store the latest value. You then can update the material whenever you want. Might have more granularity and simpler to reason about. Wouldn't require internal change too

cunning heath
#

@wooden zinc that is a good idea! Is it like creating a ProxyAnimation object and use bevy_tween to animate that proxy directly without slowing down. Then I can create a custom update system at a slower pace to update the actual animated object (material) and then do the cleanup after it's finished?

wooden zinc
cunning heath
#

Yeah, I mean adding a Proxy component in the animating entity. I'm thinking about having

#[derive(Component)]
struct Proxy {
  value: f32,
  end: f32,
  done: bool,
}

and let bevy_tween to update the value in it. Then have the following system

fn update_ui_once_every_10_frames(...) {
  if proxy.done { return }
  if value == end {
    proxy.done = true;    
  }
  // update object with current value
}

to update the entity. Without removal, I feel I need to maintain a done state to mark the last update.

BTW, you mention removing component at scale may cause performance issues? How bad could that be? I'm using a lot of that in my game. I wonder if I need to change those into a bool based component.

wooden zinc
# cunning heath Yeah, I mean adding a `Proxy` component in the animating entity. I'm thinking ab...

Removing and inserting components frequently can cause table fragmentation and reduce cache locality. There's no definitive way to tell if it has a big impact other than benchmarking. If it do has a significant impact then you can consider trying out sparseset and benchmark again. I think the done state is fine though there's other way to check like utilizing change detection but I'm not sure what are the performance profile differences.

cunning heath
#

Instead of checking done status based on the end value, I think it might be better to rely TimeRunner.is_completed() to check if the tween completes. Besides that, everything works pretty well. @wooden zinc

wooden zinc
#

well i'd be damned

#

this is getting fun

wooden zinc
# wooden zinc
poll_question_text

Do you guys use TweenEvent for events that do not implemented clone? We’re considering adding a clone bound to TweenEventTakingPlugin to add support for observer or deprecating the thing entirely if unused. If the latter is the case, then we might remove uneccessary Option inside TweenEvent.

victor_answer_votes

1

total_votes

1

victor_answer_id

2

victor_answer_text

Nah

quartz star
#

@wooden zinc Sup, trying out your crate :). Is there a reason why AnimationTarget can't implement Default? Unfortunately, when setting it as a required component, i have to do this: AnimationTarget(|| AnimationTarget). Which feels kinda silly lol. But something with 0 fields implementing default also sounds silly, but at least helpful in some cases.

wooden zinc
quartz star
#

thats what i figured lol

wooden zinc
#

Are there any specific reason you want to use this required component? You're usually supposed to used it in conjunction with the .animation() builder, not exactly required by any other components.

quartz star
#

In an example i have seen, u spawn it with the other components for that entity. As far as i understand, u really just set that component once on something u want to animate, right? It doesnt need to change after that. I have something that I want to be animatable, so im adding it as a required component so i avoid the error of forgetting to add it.

wooden zinc
quartz star
wooden zinc
#

Yep, it should be simple as that.

quartz star
#

OK cool, ty 👍

quartz star
#

@wooden zinc Im trying to get asset tweening to work. Right now im giving it the entity that has the MeshMaterial2d handle. Not sure how it works internally to get to the asset.

#

I threw a print statement in the interpolate method but its not showing up, is that a sign that its not even running?

#
//adding it like this
app.add_tween_systems(bevy_tween::tween::apply_asset_tween_system::<CardMaterialTween>);

//interpolator
#[derive(Debug, Default, Clone, PartialEq)]
pub struct CardMaterialTween {
    pub start: Vec2,
    pub end: Vec2,
}
impl CardMaterialTween {
    pub fn new(start: Vec2, end: Vec2) -> CardMaterialTween {
        CardMaterialTween { start, end }
    }
}
impl Interpolator for CardMaterialTween {
    type Item = CardMaterial;

    fn interpolate(&self, item: &mut Self::Item, value: f32) {
        println!("oapjdopasjdop");
        let result = self.start.lerp(self.end, value);
        item.rotate(result.x, result.y);
    }
}
wooden zinc
quartz star
wooden zinc
quartz star
wooden zinc
#

.into_target() on the handle also works.

quartz star
#

@wooden zinc If i add 2 animations with 2 separate commands to the same entity, should both work fine without any weird effects?

#

Two completely different animations (i.e Translation and Asset)

wooden zinc
#

If you use insert_tween_here there’s a chance some previous component my get replaced

#

If you use insert, new children will be made.

quartz star
#

I see, i think i can switch to insert and avoid the weirdness im seeing. Thanks 🙂

wooden zinc
# quartz star I see, i think i can switch to insert and avoid the weirdness im seeing. Thanks ...

hmm i realized i was not clear enough in the above respond

the latter method may still interfere with other animations, so to be more precise:

  1. .insert_tween_here method. This inserts everything into the entity you’re inserting to. It’s not supported for multiple animation running simultaneously because the working components may get replaced by the previous call.
  2. .insert (directly on the target entity). This can be used to run multiple tweens simultaneously but not multiple animation. Because TimeRunner will be replaced in the target entity when called multiple times. The tween children will actually still get created so it may seems like the animation is “merging”.
  3. .insert but as a new children of the target entity (target_entity.with_children(|c| { c.animation(); })). Animations will truly be separated from each other at this level because each TimeRunner gets to be its own entity.
  4. commands.animation() this will never causes any interference to other animations due to it spawning a new TimeRunner entity each time.
quartz star
wooden zinc
#

does anyone need animation blending?

wooden zinc
#

For example, if you have a UI transition between menu and if the user switch between them quick enough then animations will be blended together.

runic sleet
wooden zinc
#

Exploring macro design for creating state

let sprite_commands = commands.spawn(…);

let target = sprite_commands.id().into_target();
let target = tween_state!(target:
    tween::translation = Transfrom::default(),
    tween::color = Color::WHITE,
);
sprite_commands.animation()
    .insert(sequence((
        target.translation_to(Vec3::new(...), ...)
        target.color_to(Color::BLACK, ...),
    )));

and

define_tween_state! {
    MySpriteState:
    tween::translation,
    tween::sprite_color,
}
let sprite_commands = commands.spawn_empty();
let mut target = MySpriteState::target(sprite_commands.id().into());
sprite_commands
    .insert((
        Sprite { ... },
        Translation::default(),
    ).fill_state(&mut target))
    .insert(sequence((
        // ... same as above
    )));
quartz locust
#

I went over most of the crate and I'm impressed. There's one thing I can't wrap my head around - how should I respond to tweens being done?
From what I understand, listening to TimeRunnerEnded should cover all done tweens, and they carry the entity so I can get its EntityCommands easily. I could put a marker component on it to indicate what should be done with it when the tween is done. But what if there's more than one ongoing tween?
Is there a way to assign a "TweenDoneEvent" by tween?

wooden zinc
quartz locust
#

Listen
Installing this crate was probably the smartest desicion I've made since I began messing with bevy about a year ago
I tried it on a piece of code and it made it so clean and pretty that I almost shed a tear

wooden zinc
#

that's amazing 😂

quartz locust
#

Is there a quick way to get to all the tweens that currently have this entity as a target other than querying over all the tweens or making a marker component that includes the entity in it?

wooden zinc
#

Fyi, if you want/need to do that now, it’s simple as add an observer to index fresh Tween<TargetComponent,I> component and then you can get its target entity.

quartz locust
#

I'm not very happy with my code, sharing it here in case other people would want to use it:


#[derive(Debug, Clone, PartialEq, Eq, Hash, Component)]
pub struct TweenTarget(pub TargetComponent);

pub struct TweenTargetPlugin;

impl Plugin for TweenTargetPlugin {
    fn build(&self, app: &mut App) {
        app.add_observer(
            |trigger: Trigger<OnAdd, ComponentTween<Transform>>,
             mut commands: Commands,
             query: Query<&ComponentTween<Transform>>| {
                let mut tween_commands = commands.entity(trigger.entity());
                let tween = query.get(trigger.entity()).unwrap();
                tween_commands.insert(TweenTarget(tween.target.clone()));
            },
        );
    }
}
wooden zinc
#

Hmm I was thinking more of a HashMap<TargetComponent, HashSet<Entity>>
where the key is target and value is set of tweens kind of index.

#

@quartz locust does this works better for you?

quartz locust
#

(thinking)

#

I'm experimenting right now, will let you know if something better comes to mind

#

impl Plugin for TweenDestroyerPlugin {
    fn build(&self, app: &mut App) {
        app.add_observer(
            |trigger: Trigger<OnRemove, AnimationTarget>,
             mut commands: Commands,
             mut query: Query<(&mut ComponentTween<Transform>, Entity)>| {
                let entity = trigger.entity();
                for (mut tween, tween_entity) in &mut query {
                    match &mut tween.target {
                        TargetComponent::Entity(target) if *target == entity => {
                            commands.entity(tween_entity).despawn_recursive();
                        }
                        TargetComponent::Entities(targets) => {
                            targets.retain(|&target| target != entity);
                            if targets.is_empty() {
                                commands.entity(tween_entity).despawn_recursive();
                            }
                        }
                        _ => {}
                    }
                }
            },
        );
    }
}

@wooden zinc what do you think of this

wooden zinc
#

what an epic plugin name

quartz locust
#

Not ideal, but I don't think there's a way to escape giving ComponentTween a type
I guess the best thing I could do is make the plugin generic

quartz locust
quartz locust
wooden zinc
#

So you are trying to clear any animation when animation target component is removed right?

quartz locust
#

A better approach would be to destroy if after removing it from the entities we're left with none

#

Hold on

#

I added that

wooden zinc
wooden zinc
# quartz locust ```pub struct TweenDestroyerPlugin; impl Plugin for TweenDestroyerPlugin { ...

I'm still not quite sure what is the end goal 😅 but I have some idea for specifically the code.

If you were talking about improving the code performance, this code would have worst-case O(n) because of for loop here.
If we index the tweens and targets into HashMap<TargetComponent, HashSet<Entity>> with On<Add, ...>first, the time-complexity would be O(1) when we are going to remove those tweens.

If you just want to reduce the amount of times you have to register the plugin because of the generics, you can improve it by wrapping the component_tween_system inside a custom plugin that would also register your TweenDestroyerPlugin. So you only have to specify your components once instead of twice.

You can't escape generics.

brittle orchid
#

I have some code to despawn tween without dealing with generics. It's not the best since it's a regular system and not observer, but observer would despawn the tween before applying it. Maybe you'll find it helpful @quartz locust

UPDATE: I made some changes to the code, I don't know what I was doing with the original lol

// marker component, either attach to the tween or animation target to despawn on finish
#[derive(Component)]
pub struct DespawnOnFinish;

pub fn despawn_tween_on_finish(
  mut time_runner_ended_reader: EventReader<TimeRunnerEnded>,
  mut commands: Commands,
  tween_query: Query<(Option<&Parent>), (With<DespawnOnFinish>, With<TimeRunner>)>
) {
  for event in time_runner_ended_reader.read() {
    let tween_entity = event.time_runner;
    if let Ok((possible_parent)) = tween_query.get(tween_entity) {
      // remove from parent hirerarchy if any
      if let Some(parent) = possible_parent {
        commands.entity(parent.get()).remove_children(&[tween_entity]);
      }
      commands.entity(tween_entity).despawn_recursive();
    }
  }
}
#

and just configure it to run after the apply tween to animate the last frame.

app.add_systems( PostUpdate,
  despawn_tween_on_finish
    .after(TweenSystemSet::ApplyTween)
);
quartz locust
#

Sorry for the delay (haven't got much time for hobbies lately)
I ended up going with:


use crate::{plugin_for_implementors_of_trait, prelude::*};

plugin_for_implementors_of_trait!(TweenDestroyerPlugin, Sendable);

impl<T: Sendable> Plugin for TweenDestroyerPlugin<T> {
    fn build(&self, app: &mut App) {
        app.add_observer(
            |trigger: Trigger<OnRemove, AnimationTarget>,
             mut commands: Commands,
             mut query: Query<(&mut ComponentTween<T>, Entity)>| {
                let entity = trigger.entity();
                for (mut tween, tween_entity) in &mut query {
                    match &mut tween.target {
                        TargetComponent::Entity(target) if *target == entity => {
                            commands.entity(tween_entity).despawn_recursive();
                        }
                        TargetComponent::Entities(targets) => {
                            targets.retain(|&target| target != entity);
                            if targets.is_empty() {
                                commands.entity(tween_entity).despawn_recursive();
                            }
                        }
                        _ => {}
                    }
                }
            },
        );
    }
}

Which works with tweens of types I register on another file:


plugin_for_implementors_of_trait!(SendableGenericPlugins, Sendable);

pub struct GenericPlugins;

impl Plugin for GenericPlugins {
    fn build(&self, app: &mut App) {
        app.add_plugins((
            SendableGenericPlugins::<Translation>::default(),
            SendableGenericPlugins::<Scale>::default(),
        ));
    }
}

impl<T: Sendable> Plugin for SendableGenericPlugins<T> {
    fn build(&self, app: &mut App) {
        app.add_plugins(TweenDestroyerPlugin::<T>::default());
    }
}
quartz locust
#

I have a new goal: being able to tag animators with "my tweens kill all previous tweens of this type on the entities they affect" or "if my tweens entities have an active tween of this type, do not activate this tween"

Here how I listen tp them:

    triggering_entity: Entity,
    tween_policies_query: Query<&TweenSpawnPolicy>,
    tweens_query: Query<(&ComponentTween<T>, &Parent, Entity)>,
    mut commands: Commands,
) {
    if let Ok((tween, parent, tween_entity)) = tweens_query.get(triggering_entity) {
        if let Ok(tween_policy) = tween_policies_query.get(parent.get()) {
            ... //I never get to that part
        }
    }
}

Here's how I insert it:

    orb_query: Query<(&Transform, Entity), With<Orb>>,
    mut commands: Commands,
) {
      for (orb_transform, orb_entity) in &orb_query {
          if let Some(orb_animation_target) = commands
              .get_entity(orb_entity)
              .map(|e| e.id().into_target())
          {
              let collection_duration = Duration::from_secs_f32(ORB_COLLECTION_DURATION);
              let orb_z = orb_transform.translation.z;
              let mut orb_transform_state = orb_animation_target.transform_state(*orb_transform);
              commands
                  .spawn((
                      TimeMultiplierSubscriber(TimeMultiplierId::GameTimeMultiplier),
                      TweenSpawnPolicy::DestroyOthersOfTypeOnAddition,
                  ))
                  .animation()
                  .insert(…);
          }
      }
}

Could it be that I don't understand the tween hierarchy well?

#

I could make the repo public and send a link here if it helps

quartz locust
brittle orchid
#

If you are refering to the event TimeRunnerEnded, it's a built-in event to the crate. It is sent automatically when the TimeRunner timer is finished. I'm not sure about the animator, I mostly just use time runner + multiple tweens like this

(TimeRunner, DespawnOnFinish)
    - child 1: ( EaseKind, TimeSpan, TargetComponent, etc.)
    - more tween children

The tween children run on their own time span and are despawned along with the TimeRunner when it is finished. Having a TimerRunner on each tween is also valid too.

wooden zinc
#

This is the recommended method most of the time. You can also see it in one of the examples

#

But it do not provide any more granularity other than despawning the whole animation which seems like it is the thing you were trying to do (?)

brittle orchid
#

Yeah, it's for cleaning up finished animation or despawning entities after some duration.

quartz locust
#

The thing is
I have an animation over a sequence of tweens with an even that shoots once they're all done
However
Done TimeRunners despawn the animation recursively, resulting in the event at the end of the sequence never firing

Although
Listening to done TimeRunners in an event listener instead of an observer might do the trick as it won't execute instantly

quartz locust
#

Yup
That did it haha

wooden zinc
#

Had some rewrite pushed to branch overhaul-v2. Barely had any documented but you can test/review if you guys want.

wooden zinc
#

The new design features:
Core:

  • [Done, can be tested but input might be changed] Arbitrary system parameters for an Alter system. (New trait and system to replace Interpolate)
  • [Done, can be tested] Arbitrary curve that implements the curve trait
  • [WIP, it's always blending atm] Contradicting tweens solving methods
  • [todo] Dynamic tween that supports animated property
  • [Done, can be tested] Detect and warn if a certain system required for generic tweening is not registered for a type.
    Framework:
  • [Done, can be tested] Better traits and methods. Building currying function should no longer be required for building animation with state.
  • [Done, can be tested] BuildAnimation trait instead of Fn(…) to provide more flexibility. (struct and enums can be provided which should allow for further bulk operation like setting ease for the whole sequence)

Bunch of features from the previous is still missing.

hallow bronze
hallow bronze
wooden zinc
# hallow bronze Does this support animating resources yet?

This is how you would currently implement one, I think

use bevy_tween::tween_core::{AlterPlugin, AlterResource};
struct AlterMyResource;
impl AlterSingle for AlterMyResource {
    type Value = f32;
    type Item = MyResource;
    fn alter_single(...) {...}
}
fn main() {
    App::new().add_plugins(AlterPlugin::<AlterResource<AlterMyResource>>::default());
}
hallow bronze
wooden zinc
#

Oh I just updated that recently to AlterPlugin

hallow bronze
#

Like in the last 30 minutes?

wooden zinc
#

Alterer sounds weird in my head and I want to reduce that 😅

#

yeah

hallow bronze
#

I'll refresh

hallow bronze
#

Back from my errands and trying to implent this, I'm clearly doing something wrong:

<bevy_tween_core::alter::AlterResource<bevy_game::input::InputData> as bevy_tween_core::alter::Alter>::alter_system could not access system parameter Res<TweensTargetFinalValue<AlterResource<InputData>>>

Is this a resource I must add manually somewhere?

wooden zinc
#

I didn't mean to do that but my muscle memory got ahead of me 😅

hallow bronze
# wooden zinc Oh that were from a wip commit I've pushed. You might wanna stick to just this c...

Ah okay. I'll switch to that. Does the rest of this look fine?

    app
        // snip
        .add_plugins(DefaultTweenCorePlugins)
        .add_plugins(AlterPlugin::<AlterResource<InputData>>::default())
        .insert_resource(InputData::new())
        // snip
    ;
//------------------------------------------------

#[derive(Resource, Debug)]
pub struct InputData {
    pub mouse_pos: Vec2,
    pub mouse_down: bool,
    pub radius: f32,
}

impl AlterSingle for InputData {
    type Value = f32;
    type Item = InputData;

    fn alter_single(item: &mut Self::Item, value: &Self::Value) {
        item.radius = *value;
    }
}

//-----------------------------------------------
wooden zinc
#

Yeah that's seems good

#

If there were any problem, the debug plugin should notify you of missing plugin and the compiler would complain of missing trait.

#

Hopefully

#

I wonder if how expensive it would be to automatically add plugin on the fly. It’s possible but could be costly because this had to be checked every time on add. Though one could find it useful for quick iteration.

hallow bronze
#

Alright it compiles in that commit

#

Soo how do I set the tween? Is it the same as with the main branch?

wooden zinc
#

Mostly, they’re still components but just wrapped behind a generic one

#

These are in bevy_tween::tween_core::argument

#

It remove component requirement from the inner type and I can do interesting stuff with generic component hook

hallow bronze
#

Ah but hey, resources don't have components

wooden zinc
#

What do you want to set?

hallow bronze
#

Tween the radius

wooden zinc
#

Youve said set the tween, whats the thing you’re trying to modify? The timespan, the curve, or something else?

hallow bronze
#

Oh the timespan

wooden zinc
#

That will be bevy_tween::time_runner::TimeSpan and it is still a component

hallow bronze
#

But I can't add components to a resource?

#

Sorry if I'm missing something obvious

wooden zinc
#

Oh alright then. Well the bevy_tween’s design philosophy is all animation parameters is components.

#

When you’re animating resource

#

There’s a component somewhere telling the system on how to animate that resource

hallow bronze
#

So you're creating an entity for each animatable resource?

wooden zinc
#

Not exactly, it’s an entity per animation.

#

You can try to use bevy-inspector-egui to check what is the exact hierarchy looks like when dealing with one.

#

Alternatively also check out bevy_tween_core test that build the entity animation manually component by component.

wooden zinc
hallow bronze
#

Aha, okay

#

So I define a struct for the field I want to tween:

pub struct AlterInputDataRadius;

impl AlterSingle for AlterInputDataRadius {
    type Value = f32;
    type Item = InputData;

    fn alter_single(item: &mut Self::Item, value: &Self::Value) {
        item.radius = *value;
    }
}

And add that as a plugin:

        .add_plugins(AlterPlugin::<AlterResource<AlterInputDataRadius>>::default())

I don't see any generated entities with the inspector though.

wooden zinc
#

Have you spawn any animation?

hallow bronze
#

I'll reread the source files you referenced, I found them a bit opaque at my skill level

#
pub struct Blend {
    pub weigth: f32,
    pub additive: bool,
}
``` found a typo : p
wooden zinc
wooden zinc
#

To put it simply…

hallow bronze
#

Yes, starting animations (tweening to target values) for individual fields on a resource is my goal

wooden zinc
#

I completely forgot to make a tweenbuilder method for spawning resource target in the first place. Thank you for being patient 😂

hallow bronze
#

Ah 😄 no worries, glad to be of assistance

wooden zinc
#

It should still be possible with the raw method

hallow bronze
#

Don't let me stop you from getting some sleep haha

#

I'm in no hurry to get tweens working yet, just a fun detour for now

wooden zinc
#
commands.spawn(
    TimeRunner
    TimeSpan,
    Target(()),
    Alterer(AlterReseouce(AlterInputDataRadius))),
    Curve(EasingCurve::new(…)),
);

Something like this should get it working

wooden zinc
#

😂It’s not like im procrastinating or something…

hallow bronze
wooden zinc
#

okay fine youve got me. here come my bed

hallow bronze
#

Sweet coding dreams bevy

wooden zinc
#

After trying for awhile, I’m not sure if I should implement value collection system? (random technical abstract name I made for blending). It requires quite a bit of indexing code, hashmaps, and clones that adds some amount of overhead.

#

Maybe the library is already have many battery included that maybe this doesn’t matter?

#

Though this is optional and there is fine grain control down to entity level to choose which tween to be indexed. Once indexed, all alterer of the same type is guaranteed to be executed once per target and so they can be parallelized. You can debug on which tween is doing what to what. You can choose whether you want to blend, warn, or anything else custom for contradicting tweens

quartz locust
wooden zinc
#

You can tell them to do that, yes.

quartz locust
#

(nods)

#

Wait, so you're not going to impleemnt that but we'd be able to do that ourselves

#

I mean, the users

wooden zinc
#

There'll be some default method to choose, that includes the blending that bevy_animation implements.

quartz locust
#

Cool, I didn't know they do that

#

Btw, do you know if they have a built-in frame animator?

#

I ended up making a funny one like:

    type Item = Sprite;

    fn interpolate(&self, item: &mut Self::Item, value: f32) {
        let current_handle_index = (self.frame_count as f32 * value).floor() as usize;
        if let Some(sprite) = self.sprites.get(current_handle_index) {
            item.image = sprite.clone();
        } else {
            print_error(
                format!(
                    "FrameAnimator: {:?} has no frame at index: {:?}",
                    self.name, current_handle_index
                ),
                vec![LogCategory::RequestNotFulfilled],
            );
        }
    }
}

pub fn frame_animation_maker(animator: FrameAnimator) -> impl Fn(&mut Sprite) -> FrameAnimator {
    move |current| {
        if let Some(first_frame) = animator.head() {
            current.image = first_frame;
        }
        animator.clone()
    }
}
wooden zinc
#

I think bevy_animation supports generic curve (dynamic) but I didn't play with them much to know the exact details but I think that should be possible.

quartz locust
#

I think like

wooden zinc
#

In the next version you'll be able to build this kind of interpolator that interact with other components directly.

quartz locust
#

The main difficulty I'm having with Bevy is that sometimes there might be a function somewhere that does exactly what I'm looking for and I'm just unaware of its existance

wooden zinc
#

Yeah, it's kind of system inside a trait.

#

Though, I probably should mention that this wouldn't be finish for awhile since I ended up enjoying procrastinating this project by making EDM music 😅.

quartz locust
#

Yea, I get that haha

#

I think the crate works well as is

raven wigeon
#

Is there an example anywhere of sequences of arbitrary length? I'm trying to have a sprite follow a path, but the length of the path can vary. I've tried mapping the path points to a vec of translation_to tweens and then passing that into sequence, but haven't had much luck

wooden zinc
#

Are the path straight or instead spline-like?

#

Also maybe the “follow” example can be repurposed from cursor to path which would also work. This doesn’t require any kind of sequence .

raven wigeon
#

It’s a turn based strategy game, where sprites move over a square grid following an a-star algorithm. So depending on how far away a target the player chooses, the path could have a varying number of waypoints

#

I wasn’t planning on smoothing those waypoints out into a spline

#

basically something like (doesn't compile)

commands
    .entity(*figure_entity)
    .animation()
    .insert(sequence(
        path.into_iter().map(|point| {
            tween(Duration::from_secs_f32(0.3), EaseKind::Linear, translation_to(point.extend(1.0)))
        }).collect()
    ));
#

basically, is there a way to put an iterator, loop, or vec of tweens into a sequence

wooden zinc
#

You can create your own function/combinator which lets you access the inner animation commands and build animation imperatively.

#
fn path_animation(path: SomeType) -> impl FnOnce(&mut AnimationCommands, &mut Duration) {
    move |a, pos| {
        path.into_iter().map(|point| {
            tween(...)(a, pos)
        })
    }
}

commands
    .entity(*figure_entity)
    .animation()
    .insert(path_animation(path));
#

Something like this should suits your needs

#

Using the closure directly is also perfectly fine.

#

(I think that's pretty cool)

quartz locust
#

I have that one

    duration: Duration,
    interpolation: I,
    tween: T,
    spawn_policy: TweenSpawnPolicy,
) -> impl FnOnce(&mut AnimationCommands, &mut Duration)
where
    I: Bundle,
    T: Bundle,
{
    move |a, pos| {
        let start = *pos;
        let end = start + duration;
        a.spawn((
            TimeSpan::try_from(start..end).unwrap(),
            interpolation,
            tween,
            spawn_policy,
        ));
        *pos = end;
    }
}

Though if I ever need more components added to the tween I might just make a generic tween_with_component that takes a <C: Component>

stoic parcel
#
    let mut entt_cmd = cmd.spawn((
        AnimationTarget,
        Sprite {
            custom_size: Some(Vec2::new(100., 100.)),
            ..default()
        },
        Transform::from_xyz(-400., -400., 0.),
    ));
    let target = entt_cmd.id().into_target();
    entt_cmd.animation().insert_tween_here(
        Duration::from_secs_f32(1.),
        EaseKind::Linear,
        target.with(translation_to(Vec3::new(400., 400., 0.))),
    );

i don't know what i did wrong, but i don't think it moves

wooden zinc
#

Wait

#

translation_to requires a mutable state but you're not declaring any.

#

have you try bare translation first?

stoic parcel
#

what do you mean by "mutable state"? i don't think I've read them in the docs

#

bare translation doesn't work

wooden zinc
stoic parcel
#

huh

#

I've read that but somehow completely missed the point

wooden zinc
stoic parcel
#

i can finally touch my code again

stoic parcel
#

thanks!

#

somehow reading that the first time one caught the sprite_color_to and not the .state part

stoic parcel
#

now im curious why Interpolator::interpolate is &self and not &mut self

quartz locust
#

Is there a build-in way to do something like:

    move |a, pos| {
        let start = *pos;
        let end = start + duration;
        a.spawn(TimeSpan::try_from(Duration::from_secs(0)..duration).unwrap());
        *pos = end;
    }
}
#

Maybe I should've called it just delay()

wooden zinc
#

like forward()?

wooden zinc
#

Though I think they're not being utilized much here.

quartz locust
# wooden zinc what is this supposed to do?

I have some built in systems around tweens that I want to use for regular timers
So I thought I should just make an animation() that does nothing but send an event when it's done
So I needed something that would tell it to fire an event only after a predefined amount of time (that can change as an animator in my code can subscribe to a time-scaler that might change during the game

wooden zinc
#

yeah there’s no builtin for this

quartz locust
#

Is there a smoother way to achieve that?

wooden zinc
#

I think its fine. It’s just like a very minimal tween. Though, some memory will be wasted but just very slightly that shouldn’t be a problem.

#

(Unless you have like a million of these)

quartz locust
wooden zinc
#

Welp, better get to profiling then!

quartz locust
#

Something is funny

#
        Some(next_color) => parallel((
            transform_change_tween,
            tween(
                animation_duration,
                EaseKind::ExponentialIn,
                color_state.with(sprite_color_to(next_color)),
            ),
        )),
        None => transform_change_tween,
    };```
#

Is there no way to make a conditional animation?

wooden zinc
#

oh that's a rust type issue

#

you gotta implement an enum

quartz locust
#

Like, I want to animate the color too only under certain conditions

#

hmmmm

#

Like, an enum where one variant wraps a parallel and another wraps a regular tween

#

right?

wooden zinc
#

wait

#

hmm

quartz locust
#

Worst case I'll just spawn a redundant tween for the color

wooden zinc
#

This is probably ain't possible since you need to implement FnOnce on the enum

#

but that's nightly

quartz locust
#

mmm ok

#

I'll just spawn a redundant color one

#

Thanks

wooden zinc
#

@quartz locust Instead of returning a closure you could try passing arguments manually instead

#

Might work if you have AnimationCommands available

#
|a, p| {
    match ... {
        ... => tween1(a, p),
        ... => tween2(a, p),
    }
}
wooden zinc
#

Thinking of making bevy_tween a subcrate of bevy_timerunner instead.

#

This would make a lot more sense for how things are represented currently.

#

.animation() builder is not really a special thing to animation but more about bevy_timerunner’s thing

#

There’s quite a bit more breaking changes too so I guess Id get a chance to change the crates name to prevent further confusion with bevy_tweening

#

Then if stuff is properly in place then maybe I could starts adding audio support

runic sleet
#

I think combining the two crates into a single workspace/monorepo sounds nice. Makes contribution easier.

runic stream
#

Does using runner.set_tick(0.0) is a correct way to replay an animation?

runic stream
#

What's the pattern for having multiple tween and being able trigger them?

wooden zinc
wooden zinc
heady hare
#

I've been really enjoying this crate, thank you! ❤️

quartz locust
#

I just made another tiny crate with this crate haha

weary flame
#

hello ! first time using animation/tween crate.
i tried to do this:

            let mut transform_target = AnimationTarget.into_target().transform_state(transform);
            ec.animation().insert(parallel((
                tween(
                    Duration::from_secs(1),
                    EaseKind::Linear,
                    transform_target.translation_by(Vec3::Y * 20.),
                ),
                tween(
                    Duration::from_secs(1),
                    EaseKind::CubicIn,
                    transform_target.scale_by(Vec3::splat(0.5)),
                ),
                tween(
                    Duration::from_secs(1),
                    EaseKind::CubicIn,
                    AnimationTarget
                        .into_target()
                        .with_closure(|c: &mut TextColor, v: f32| {
                            c.0.set_alpha(255. - v * 255.);
                        }),
                ),
            )));

the first two works as per the example, but the custom modification doesn't work, the closure is never called.
i tried using a state, but i couldn't use .with_closure and only .with.
thanks

#

i am obligated to impl a Interpolator trait ?

weary flame
#

i tried implementing the interpolator trait on a struct and using it but still not working

brittle orchid
#

I think this is because of the alpha of Color in bevy is ranged from 0.0 to 1.0 (instead of 0.0 to 255.0)?

wooden zinc
weary flame
weary flame
wooden zinc
#

Interpolator is implemented for Fn(…) so you can use closure for quick prototyping if needed. You’d implement interpolator for custom struct if you wanted more rigid code.

#

Anyways, closure not being called is usually because a system is not registered correctly, could you show me the tween systems youve registered?

weary flame
#

yep it works now...

#

using the component dyn

#

i was using the component_tween_system before with the interpolator trait, i thought the dyn ones where for other advanced things lol

#

thanks a lot

#

what does registering a component internally does ? it can't be detected from the closure or target type ?

wooden zinc
#

When you’re using closure, the type it represents in the ECS world is different from a rigid type because it’s a Box<dyn Fn(…)> wrapped inside Box<dyn Interpolator>. component_tween_system will look for interpolator type directly and different interpolator for the same component may do different thing and so will require multiple registrations.

#

component_dyn_tween_system is only required to be registered once for the same component since it do not look for a rigid type but things that implements Interpolator.

weary flame
#

hello, i want to implement a arcing to my projectile (simple archer troops 2d arrows), and i guess i can do it with this crate ? a tween from the shooter to the target ?
i think i saw in the example how to dynamically change the end state (if the target of the projectile move)
but how can i do a arcing animation ?
i use a certain distance per seconds to move the arrows, can it be used ?
or im better of doing it manually myself ?
thanks

quartz locust
weary flame
#

ill try thanks

midnight notch
#

Hello. I'm using bevy_tween to move a player camera horizontally across terrain that has some amount of height variation, and I would like to keep the height of the camera adhered to the terrain height. Rather than creating a very complicated tween, I figured I could simplify things by just using a tween for the horizontal movement, and then setting the camera height to the terrain height every frame after the tween is applied:

app.add_systems(
  PostUpdate,
  terrain_correction_system.after(TweenSystemSet::ApplyTween),
);

fn terrain_correction_system(
    mut positioned: Query<&mut Transform, With<Camera>>,
    terrain: Query<(&TerrainTile)>,
) {
    for mut transform in &mut positioned {
        if let Some(terrain_tile) =
            find_terrain(transform.translation, &terrain)
        {
            transform.translation.y = calculate_terrain_height(terrain_tile);
        }
    }
}

This works about half the time I run it, however the other half of the time there is a rapid jitter visually between the height I've set and the height the tween is setting, meaning there is some system ordering inconsistency. Does anybody know if there is some way to configure the system ordering so that the overridden height is always the transform that is rendered?

wooden zinc
#

Otherwise you can order your system after TweenSystemSet::ApplyTween which by default is in PostUpdate

midnight notch
# wooden zinc Otherwise you can order your system after TweenSystemSet::ApplyTween which by d...

This is what I'm currently doing (terrain_correction_system.after(TweenSystemSet::ApplyTween)), and for some reason it is still inconsistent. I was wondering if there is some core bevy animation system or timerunner system that I need to order against as well.

I may have to dig into making a custom interpolator if I can't figure out the inconsistency, but its not straightforward as I do need the vertical animation sometimes. The above is a bit simplified, in actuality there is a 3d grid of blocks and the above logic is for other objects in a given space that affect height (ramps, half-blocks, etc).

wooden zinc
midnight notch
wooden zinc
#

Alright Ill try to dig into the code after this sleep.

midnight notch
#

Oh no pressure, only if you feel like it, I can certainly keep poking at it myself. Was just hoping there was some gotcha I was missing around the tween being "applied" versus the transform being modified or something. thank you regardless! It might be easier to look at one of the bevy_tween crate examples instead of my code, so I'll modify one of the examples to see if the same thing happens.

wooden zinc
#

All good, I’m certainly curious too. Just that I planned the first thing to do in the morning is to take a look at that jitter 😄

wooden zinc
#

Oh wait I get it now. Those 2 ran from the same code.

midnight notch
#

Yeah, same code, about fifty/fifty.

wooden zinc
#

It’s currently correction_system.in_set(TweenSystemSet::ApplyTween). What happens of you change it to after?

midnight notch
#

Whoops. My bad. The example actually appears to work fine with my typo fixed.

#

It is correct as after in my original code though. That being said, I did actually find a fix for my specific problem: I created an interpolator for an "intermediary" transform that I selectively copy to the main transform under certain conditions. works pretty well!

#

I think its possible the original problem was user error too, it might have been that the jitter I assumed was systems fighting over the animation was actually something else.

#

Thank you for your time!

wooden zinc
#

I was guessing if maybe the jitter was the terrain is just really rough or something. Anyways, glad your problem is fixed.

quartz locust
#

Sharing a TextColor interpolator (this is an exact copy of the ones in the crate, just thought it might be useful for other people too. Don't forget to add the plugin).

use bevy_tween::interpolate::Interpolator;

#[derive(Debug, Default, Clone, PartialEq, Reflect)]
pub struct TextColorInterpolator {
    pub start: Color,
    pub end: Color,
}

pub struct TextColorInterpolatorPlugin;

impl Plugin for TextColorInterpolatorPlugin {
    fn build(&self, app: &mut App) {
        app.add_tween_systems(component_tween_system::<TextColorInterpolator>());
    }
}

impl Interpolator for TextColorInterpolator {
    type Item = TextColor;

    fn interpolate(&self, item: &mut Self::Item, value: f32) {
        item.0 = self.start.mix(&self.end, value)
    }
}

pub fn text_color_to(to: Color) -> impl Fn(&mut Color) -> TextColorInterpolator {
    move |state| {
        let start = *state;
        let end = to;
        *state = to;
        TextColorInterpolator { start, end }
    }
}
stark shard
#

Hello, I have a hard time understanding what a TimeRunner is and what it does?
and why I need one?

in the examples, we use TimeRunner and TimeSpan, what is the difference, what do both do?

i have this for example:

commands
    .entity(entity)
    .insert(AnimationTarget)
    .with_children(|commands| {
        commands
            .spawn(TimeRunner::new(Duration::from_secs(1)))
            .with_children(|commands| {
                commands.spawn((
                    TimeSpan::try_from(..Duration::from_secs(1)).unwrap(),
                    EaseKind::QuadraticInOut,
                    ComponentTween::new_target(
                        TargetComponent::marker(),
                        CardOffsetTween {
                            start: Vec3::ZERO,
                            end: Vec3::new(100.0, 0.0, 0.0),
                        },
                    ),
                ));
            });
    });

When I change the TimeRunner, nothing seems to change. but when i change the TimeSpan it takes longer to complete the animation.

wooden zinc
# stark shard Hello, I have a hard time understanding what a `TimeRunner` is and what it does?...

TimeRunner is what supplying the current time to its children. Setting the time in TimeRunner just means the amount of time for the animation to run for and it will stop supplying time at the end of the specified time. If TimeRunner is configured to loop then that time effects the looping duration. TimeSpan is like a parameter which each tween is at. If a tween has a TimeSpan with range like 4..5 (second 4 to second 5), the tween will not run for 4 seconds and it will only runs for 1 seconds.

stark shard
stark shard
#

one more question, more on how to do something.

what i want to do is, tween an entity towards its target position, easy.
however the target position might change before the tween itself was completed.

currently i simply despawn the current tween and insert a new one with the new target location.
as you can see in the gif, this results in quite a choppy movement.

i'd instead like to check if a animation is still running, if it is i just need to change the target location, and if it's done restart the tween with a new target location.

is the idea in theory the right way and how would i achieve this?

wooden zinc
stark shard
silent silo
#

any chance we could get a commands.entity(e).animation().try_insert()? or is there a simple equivalent?

i have observers that add tweens on component removal, but the removal might've been a despawn, causing the tween insertion to panic. with my custom components i just use try_insert, but can't here

wooden zinc
quartz locust
#

I'm trying to have the camera follow my player like in the follow example:
https://github.com/Multirious/bevy_tween/blob/main/examples/demo/follow.rs

Here's what my code looks like:
https://gist.github.com/Rabbival/62303c41f2f7db096dfe4bb5652590ab

GitHub

Flexible tweening plugin library for Bevy. Contribute to Multirious/bevy_tween development by creating an account on GitHub.

Gist

GitHub Gist: instantly share code, notes, and snippets.

wooden zinc
#

What happens if you use other type of ease?

quartz locust
#

The same

#

(Let me know if there's a specific kind of easing you'd like me to try out)

wooden zinc
#

Linear first I guess. Then Exponential is the curve that's always handy

quartz locust
#

It has to be one that starts off quickly or the camera would always be way behind the player (since I create a new one every time the player moves)

wooden zinc
#

Is it jiterring?

quartz locust
#

yea

wooden zinc
#

I actually can't see it

quartz locust
#

It's not as visible now but it jitters

wooden zinc
#

Maybe it's some lag?

quartz locust
#

Yea, I don't think it's a crate problem
I have no idea why it happens though

#

It's like only the first frame is problematic

wooden zinc
#

Check the animation duration when it's first running

#

Using this method the animation will start at like 0% to 100% and while the player is moving they'll always be around 50% to 100%

#

Maybe it looks like jitter because at 0% to 50% the player will move significantly faster

#

But that problem shouldn't be visible to linear curve

#

What about system ordering?

quartz locust
wooden zinc
#

Maybe the camera is rendering in the same schedule as the tweening?

quartz locust
#

Could be
Don't know if there's anything I can do about that though haha

wooden zinc
#

It's possible to put system ordering on third party

#

I think it will goes something along like "camera_rendering.after(tween_system)"

quartz locust
#

Interesting
I'll look it up
Thanks!

untold raft
#

hey there - trying to get a number to animate up. anyone see problems with this? it appears to not be doing anything to the Text component when I add an animator

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TextNumberLens {
    pub start: usize,
    pub end: usize,
}

impl Lens<Text> for TextNumberLens {
    fn lerp(&mut self, target: &mut dyn Targetable<Text>, ratio: f32) {
        target.0 = ....   .to_string();
    }
}
...
Animator::new(Tween::new(
    EaseFunction::QuadraticInOut,
    Duration::from_secs(1),
    TextNumberLens {
        start: 0,
        end: added_score,
    },
))
...

Any ideas on what I'm missing?

ah - missing the system. whoops

quartz locust
#

Ok so this has taken my entire day

#

This PR adds the ability to have multiple tweens of the same type affect the same entity at the same time by reporting deltas!

#

It does add a small breaking change (the signature of interpolate function changes to get the last interpolation value for the delta calculation)

#

Feel free to check the delta tweens example to see it in practice

#

Would love to get reviews for it and hear your thoughts!

#

Now, I need to step up from this chair and go do something else haha

quartz locust
weary flame
#

hello! i think im having a bug ?

        commands.entity(entity).animation().insert_tween_here(
            Duration::from_millis(150),
            EaseKind::QuadraticOut,
            entity.into_target().with_closure(
                move |component: &mut UiWorldPosition, _ease_time: f32, progress: f32| {
                    component.0 = current_pos.lerp(target_pos, progress);
                },
            ),
        );

the first call to the closure, progress is already at 1.. the following calls then goes from the start normally. this make the target flicked for one frame to the target, then back to the start

#

also, is inserting a new animation on a running animation to overwrite the previous one ok ?
in the first tick (closure call) of the new animation, the progress is still the old animation progress. following calls are ok. this is probably the same problem

quartz locust
#

I did not see that

#

Please open a github issue or ping me next time

#

Taking a look now

quartz locust
#

Taking one additional frame is fine, it depends on your system ordering

#

But I am curious about it starting at 1.0 by default, taking a look in the repo now

#

@weary flame are you sure about the parameters of your closure?
I just looked at the crate's code and it looks fine

#

I assume you were looking at the follow or interpolator examples, but it seems that there the order of the parameters is the current interpolation value as the first f32 (the second parameter)

weary flame
#

hey, thanks for answering ! I'll try to do a reproduction when I get back

#

the animation is added in a Picking observer

weary flame
#

oh my god i was using the previous progress... sorry

quartz locust
quartz locust
weary flame
#

hello ! i was wondering:

  • how to handle multiples different animation on the same entity ? should just always spawn animation fully in a child entity ? i think when i used .insert() it still conflicted, overwriting previous animation, so i use .insert() but on a new empty child entity.
  • bevy_tween_helpers cleanup plugins despawn the entity from what i understand in the readme ? how to simply despawn my child animation entities without removing the AnimationTarget ?

thanks !

quartz locust
#

I'm on a vacation, could you please remind me on Tuesday?

#

Oh wait

#

I got a notification on the channel but then saw that this is actually from a week ago?

#

Please ping me next time

#

@weary flame Tuesday, ok?
If you didn't solve that already

weary flame
#

hope you had good vacations :)
no problem, i have a few things in my list i had to do before looking into that, so i didn't yet resolve it, but im sure this is no magic, just wondering the best way of doing this.
thanks

quartz locust
weary flame
#

yeah see you on tuesday :) didn't want to ping you now has there's no urgency

quartz locust
#

So for the first question- you can use an existing delta tween or implement your own using the provided previous_value in the inteprolate functions. Here's an example that uses delta tweens to, for example, change the translation of an entity by two different tweens at once:
https://github.com/Multirious/bevy_tween/blob/main/examples/demo/delta_tweens.rs
You can see how that's implemented here (if you want to make your own):
https://github.com/Multirious/bevy_tween/blob/main/src/interpolate/transform.rs

#

I'll be back for the second question in a bit

quartz locust
weary flame
weary flame
#

thanks :)

quartz locust
weary flame
quartz locust
quartz locust
#

@weary flame please let me know if that answers your questions

weary flame
weary flame
#

thanks !!

quartz locust
quartz locust
weary flame
#

sorry im also not english haha. i guess its especially the specific terms, like for example "animation parent" which one is the animation ? as in my case for example there is 3 different entities in the hierarchy. now i know its the one with the timer runner, but that's hard to understand the different possibilities as new comer. i dont how we can improve this

half acorn
#

@quartz locust does bevy_tween allow tweening UiTransforms translation and scale?

quartz locust
quartz locust
#

Let me know if you tried to make intrpolators for them and if so how it went

#

If you think they'd be useful for other people we could add them to the helpers crate

half acorn
half acorn
#

@quartz locust the problem is that UiTransform translation operates on Val which is hard to interpolate pre-0.18

quartz locust
quartz locust
# half acorn <@1204086077942538304> the problem is that UiTransform translation operates on `...

So, given the current version of Bevy, the closest thing you can do is something like:

#[derive(Debug, Default, Clone, PartialEq, Reflect)]
pub struct UiTranslationInterpolator {
    pub start: Val2,
    pub end: Val2,
}
impl Interpolator for UiTranslationInterpolator {
    type Item = UiTransform;

    fn interpolate(&self, item: &mut Self::Item, value: f32, _previous_value: f32) {
        if let Val2 { x: Val::Px(start_x), y: Val::Px(start_y) } = self.start
            && let Val2 { x: Val::Px(end_x), y: Val::Px(end_y) } = self.end
        {
            let current_x = start_x.lerp(end_x, value);
            let current_y = start_y.lerp(end_y, value);
            item.translation = Val2 { x: Val::Px(current_x), y: Val::Px(current_y) }
        }

        //TODO: match other Val variants here
    }
}
quartz locust
weary flame
#

uitransform interpolators @half acorn

quartz locust
#

@ocean crane I saw your PR, it'll take us some time (I think, about a week and a half) to get to it
Multirious is busy for the next couple of days and there's a new fearture we want to merge before bumping the version (you can actually see an open PR related to it on the time_runner crate)

ocean crane
#

all good - i've got a locally patched version working for me - thanks for the heads up

quartz locust
#

Do you want to take a look at my PRs and tell me your thoughts?
I'm basically trying to make Fixed tweens for physics (specifically, in my case, avian) calculations
Avian already has a tween crate, but it doesn't have all the features that one has

quartz locust
#

nvm I just realized that ticking the timer runners on a special step doesn't matter if you don't also sample them (and the lookup curves) on that step, so I have a few more hours of work to do
Tomorrow, that is

surreal sandal
#

that .. seems wrong, somehow. i don't know if that means my code has always been broken or there's an issue in the new branch. i haven't dug into this much yet

#
let mut camera_commands = commands.entity(camera_entity);
let camera_target = camera_commands.id().into_target();
camera_commands.animation().insert_tween_here(
    Duration::from_secs_f64(MascotTasks::LOGIC_RATE),
    EaseKind::Linear,
    camera_target.with(interpolate::translation(
        camera_xform.translation,
        translation,
    )),
);
#

my tweens are pretty simple

quartz locust
#

We're working on a new update for the crate (and a very exciting one imo) that would come with the 0.18 upate

#

(We are planning on merging it soon though)

cunning heath
quartz locust
quartz locust
#

For those who need to move to 0.18 as soon as possible, you can use this dependency:
bevy_tween = { git = "https://github.com/Multirious/bevy_tween", branch = "generic_time_steps" }
We're not working on that branch, and it has bevy 0.18 and roughly the same api

cunning heath
#

Is the branch for 0.18 broken? Got this error while pointing at it

364 | use bevy_time_runner::TimeRunnerSystemsPlugin;
    |     ^^^^^^^^^^^^^^^^^^-----------------------
    |     |                 |
    |     |                 help: a similar name exists in the module: `TimeRunnerPlugin`
    |     no `TimeRunnerSystemsPlugin` in the root

error: cannot construct `TimeRunnerPlugin<_>` with struct literal syntax due to private fields
   --> /Users/wentao/.cargo/git/checkouts/bevy_tween-d2140e841646a492/2904aee/src/lib.rs:570:29
    |
570 |             app.add_plugins(bevy_time_runner::TimeRunnerPlugin {
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: ...and other private field `marker` that was not provided
help: you might have meant to use the `in_schedule` associated function
    |
570 -             app.add_plugins(bevy_time_runner::TimeRunnerPlugin {
571 -                 schedule: self.app_resource.default_schedule,
572 -                 enable_debug: self.enable_time_runner_debug,
573 -             });
570 +             app.add_plugins(bevy_time_runner::TimeRunnerPlugin::in_schedule(_));
    |
help: consider using the `Default` trait
    |
570 -             app.add_plugins(bevy_time_runner::TimeRunnerPlugin {
570 +             app.add_plugins(<bevy_time_runner::TimeRunnerPlugin as std::default::Default>::default());
    |
quartz locust
#

What branch is this? I'll fix it now

#

Can you route your game to the main branch?

#

(it's already updated to 0.18 and seems to build fine)

cunning heath
#

I tried both main and your branch yesterday. I'll try the main branch again later today

quartz locust
quartz locust
#

Alright, running an example on the main branch now
If it succeeds- I'll let you know, If it fails- I'll put whatever fixes necessary on a new branch, and make sure to keep it stable on time_runner crate changes.

#

Good news: it compiles, so you should probably cargo update
Bad news: things don't seem to move, but I'm on it

#

Ok done, allow me to clarify

#

In short: route your repo to the branch called "0.18-patch-until-we-merge-generic-time-contexts"

What was the problem on main: animations didn't run
Why: no TimeContext was provided

What does it mean, what we're doing for so long and why:

  • The update we're working on would allow you to run tweens with time contexts (for example, Fixed) on a schedule of your desire, additional to regular tweens
  • What that means is that TimeRunners aka animation parents need a time context to know what time to use (the new exposed api is pretty similar to the old one, it's not 100% done so I don't want to say too much)
  • Why are we even doing that - imagine wanting to run a Translation tween of a physics object, or a tween that applies velocities etc. Running it with regular time on PostUpdate wouldn't make sense and result in many unstable and hard-to-replicate errors. Well- not anymore (or should I say, soon enough not anymore). You would soon be able to make any tween you make run on different time scales, on whatever schedule intern you desire. But, it does take time to write, and we're both busy with other things.
    Please bear with us.
karmic flare
#

does the bevy_tween is updated to bevy 0.18?

quartz locust
karmic flare
#

Long name😄

quartz locust
karmic flare
#

because it doesn;t work for me

quartz locust
#

What error does it give

#

(Also please ping me if you need me)

karmic flare
#

I am afraid some guys hate that😀

quartz locust
karmic flare
#

As soon I will get home I will send thanks

quartz locust
#

Thanks please ping me when you do

#

It's already evening here so I might get to that tomorrow

karmic flare
#

@quartz locust no problem

karmic flare
#

@quartz locust did you get the error?

quartz locust
quartz locust
#

(checking)

#

I may have forgot to cargo update 🥲

#

anyway, this branch will be fixed in the next half an hour or so

#

@karmic flare can you please do cargo update and try again

karmic flare
#

I will

#

but to make the game run I remove all tween functionnality

#

I need to redo it

#

@quartz locust it works now

#

I try to run the crate

karmic flare
#

Thanks

quartz locust
wooden zinc
#

v0.12.0 is released! Thanks everyone for being patience. This release took way longer than it should've. There are quite a bit of changes. Please read the changelog carefully.

dim warren
#

hey there, Im having a bit of hard time trying to understand TimerRunner. I'm trying to manually trigger the animation again, but doesn't seem to get it to work. The animation only trigger once, which I assume it was trigger during insert. But it doesn't trigger again after even though it does goes through else condition.

...
if !has_runner {
    ent_com.animation().insert(sequence((
        tween(
            Duration::from_secs_f32(0.1),
            EaseKind::Linear,
            power_supply
                .target
                .into_target()
                .transform_state(*trans)
                .scale_to(Vec3::splat(1.5)),
        ),
        tween(
            Duration::from_secs_f32(0.5),
            EaseKind::ElasticOut,
            power_supply
                .target
                .into_target()
                .transform_state(*trans)
                .scale_to(Vec3::splat(1.)),
        ),
    )));
} else {
    ent_com
        .animation()
        .repeat(bevy_tween::prelude::Repeat::times(1));
}
...

Any Ideas on what I'm missing?

wooden zinc
quartz locust
#

So you basically create there a new animation that repeats once, but it's empty

#

You should spawn the animation again and kill the previous one if it exists
Might want to take a look at bevy_tween_helpers crate

dim warren