duck-tween icon indicating copy to clipboard operation
duck-tween copied to clipboard

FastForward ignores IsReversed, & The api's usage and use cases are not clear

Open kkjamie opened this issue 5 years ago • 0 comments

Functional Problem 1 (IsReverse is not respected)

The code

public override void FastForward()
{
	// Set the animation state to its final form
	CurrentTime = Duration;
	Refresh(Progress);
	if (IsPlaying)
	{
		AnimationDriver.Remove(Update);
	}
	base.FastForward();
}

In a TimedAnimation: FastForward is achieved by setting CurrentTime to Duration of course if you have reversed it the theoretical "End" is actually Progress == 0 and CurrentTime == 0. You can also see that Refresh() is called regardless of it being played or not.

Use cases

It can occur in multiple use cases but a common one might be reversing, then fastforwarding, in order to put an animation to it's intial state.

Solutions

  • It's an easy fix to make it respect the IsReversed Property
  • Perhaps a new API for Rewind to hit the first frame?
  • Expose Refresh(float progress) so we can put our own time in, then you can hit any frame of the animation 50% 72% or whatever you want

Functional Problem 2 (FastForward does not hit the final frame in some situations)

In a TimedAnimation it always calls Refresh(1) no matter what state it's in (Playing, Completed, Not-started). In an AnimationCollection however it's behaviour is actually It fast-forwards any uncompleted child animation. Theres 3 cases:

  • If the animation has never been started: it will fast-forward each child.
  • If the animation is playing: it will fast-forward all uncompleted child (meaning each one will be on it's last frame)
  • If the animation was completed: it will just complete instantly because all children as "completed".

This is due to an implementation detail.

Solutions

  • it's quite an easy fix to force it to "reset" when FastForward is called
  • An in project work around is to call Play before FastForward. It brings up bigger API design questions (see below)

API Design & Philosophy

Right now it's not quite clear if we need to call .Play() before .FastForward(). Before reading the implementations in depth whenever I used it I was never sure if I needed to set it playing before FastForwarding. It feels like it should be needed.

In both problems 1 and 2 there is no clarity about whether or not the FastForward will hit the final frame.

I believe originally it was designed as a play-time fast forward, but due to implementation it works (in most cases) without starting, and that's turned out to be very useful. We can create an animation which is a virtual timeline, and then "hit" the final frame, and now we are seeing it would be useful to hit the first or indeed any frame.

Our Options

Theres a few options for the future of this:

  1. Make FastForward a no-op for animations that are not playing, with a throw. This would be very explicit and it would become obvious that the API is designed that way. That would mean a lot of
// Get it to the end...
tween.Play();
tween.FastForward();
  1. Fix it up where needed and allow it to FastForward any time. This would still be ambigious in my opinion.

Other enhancements

Regardless of our choice above there are some new APIs that would benefit us:

  1. Add public .Refresh(float progress) to force a refresh. We could allow this without needing to play. - I thought about calling it Goto(0.25)- so you could get an animation to start part way through, but this would not work at all for collections... so let's accept that it won't change the progress or time elapsed. If we wanted that we could implement it in a different way. Update them with large delta times, or iteratively do it.

  2. Add .Rewind to do the opposite of .FastForward - This would need to have the same rules as FastForward regarding whether or not we can call it when not playing and also respect for IsReversed

kkjamie avatar Apr 05 '19 13:04 kkjamie