djeedai / bevy_tweening

Tweening animation plugin for the Bevy game engine.
Other
412 stars 65 forks source link

Adding new tweens to existing ones #115

Open musjj opened 9 months ago

musjj commented 9 months ago

Let's say I have an entity that is set up with a tween targeting the entity's transform.rotation. The tween loops infinitely throughout the game.

But I also want to have a system that can insert a new tween that targets said entity's transform.scale, depending on the state of the game. The tween loops only once. I also want to be able to replay that tween, depending on the game state.

The problem is that this requires you to put more than one Animator<Transform> component on the entity, which is currently not possible in Bevy. Is there a good solution for this use-case?

SecretPocketCat commented 9 months ago

You could use tracks, but you'd have to recreate the looping tween which would be tedious. A simple-ish approach would be to add a wrapper that can have it's own animator which can do the rotation.

guyguy2001 commented 6 months ago

A similar need I have (I'm not sure if this should go here or in a new issue) is to tell a tween to run after the current tween. So for example, when my player is hit, I want them to flash red, and afterwards start animating their alpha to indicate an invulnerability state. (This might not be the correct way to approach the problem, I'm not sure yet).

To do this I need one of the following:

I also need a way to know when the current animation will end - but I think I can get that via the existing total_duration, which seems great

The second option is probably the best one, but I'm too new to writing APIs in rust to have a clear idea. Also, I could see a world in which it is just a bad idea to allow enqueuing more animations from the outside.

djeedai commented 6 months ago

@musjj sorry I completely missed this issue.

The problem is that this requires you to put more than one Animator component on the entity, which is currently not possible in Bevy. Is there a good solution for this use-case?

Yes indeed. So you're stuck with a single animator, unless you can reparent your scaled object to an invisible rotating one? If not, then the way I'd approach it in this case given the two animations are widely different is to put the logic into a custom Lens, where you always apply the rotation, and conditionally apply the scaling when needed. You will need to use some form of Arc to share the lens (or some of it's fields) with the animator. And it's a bit awkward if the scaling doesn't start in sync with the rotation nor has the same duration, but you can figure some delayed t value inside the lens from the first time the scaling is activated I think. Definitely more complex than it should, but I don't see a good solution at least until we have dynamic queries in Bevy ECS.

djeedai commented 6 months ago

@guyguy2001

So for example, when my player is hit, I want them to flash red, and afterwards start animating their alpha to indicate an invulnerability state.

I'm not sure I understand. It seems that you want to animate first the color and then the alpha of the same material or sprite? If so, you can just use Tracks and enqueue two Tweens one after the other.

If, on the other hand, you're trying to execute a tweenable on one Animator and synchronize it with another tween on a different Animator, then yes there's no good built-in solution for this. One workaround I can think of is if you know the duration of each individual tweenable, you can insert delays to indirectly synchronize them based on time.

musjj commented 1 month ago

I think a simple solution would be to allow Animator to target an entity other than itself. For example:

Animator::new(tween).with_entity(entity)

This way you can just spawn multiple animator entities targeting the entity you want. This is similar to the approach used by bevy_tween, except that it traverses up the parent hierarchy until it finds a target marker.