Solution 1:
Assuming that it suffices for you to have the updated transitionTarget
take effect at the moment when the animation-cycle starts, there is a simple solution to your problem
utilizing the ChangeEndValue
method.
Moreover I suggest a simplification:
Instead of rewinding the tween manually, I suggest that you use
SetLoops(-1, LoopType.Yoyo)
The same amount of control can be achieved by utilizing the OnStepCompleted
callback.
public class EnemySpaceship : MonoBehaviour {
private Tweener transitionTweener;
public float transitionTarget = 0f;
private Vector3 Endvalue => new Vector3(0.0f, transitionTarget, 0.0f);
void Start()
{
transitionTweener = transform.DOMove(Endvalue, 3f, false)
.SetOptions(AxisConstraint.Y)
.SetEase(Ease.Linear)
.SetLoops(-1, LoopType.Yoyo)
.SetAutoKill(false);
transitionTweener.OnStepComplete(OnStepCompleted);
}
private void OnStepCompleted() {
if (transitionTweener.CompletedLoops() % 2 == 0) {
Debug.Log("->> Transition played and completed!");
transitionTweener.ChangeEndValue(Endvalue);
} else {
Debug.Log("<<- Transition played backward and completed!");
}
}
}
Solution 2:
If you do need the animation to use the updated target value instantly while the animation is still playing, then we need more clarifications.
Consider the following cases for the time of the change of the transitionTarget
value:
- during transition from the start location to the
transitionTarget
.
- the new
transitionTarget
value is yet to be reached
- the new
transitionTarget
value has already been surpassed
- during transition back from the transitionTarget to the start location.
I will ignore case 2. entirely, because the spaceship will have to return to the start location anyways, and that location which never changes - so in this solution like in the first, the change will only take effect, as soon as the loop is complete when the start location has been reached.
regarding 1., I suppose we can generally assume, that the movement of the spaceship should be continuous, so we will avoid any sort of "teleportation".
Unfortunately the ChangeEndValue
method rewinds the tween.
So if we want to use this method† for case 1.1, we will have to manually "scrub" the tween to the correct position, which can be achieved with the Goto
method.
However, it appears that the Goto
method does not work properly with infinite Loops (which should be reported as issue). The solution will just execute a single loop, still using LoopType.Yoyo
(the number of loops has to be 2 to go forth and back once), and restart the tween afterwards (which is a bit closer to the sample code from the question).
In case of 1.2 we just scrub forward to the position in time, where we move back to the start-position, while we are at the same position, which is 2 * duration - transitionTweener.position
so without further ado, the solution:
public class EnemySpaceship : MonoBehaviour {
private Tweener transitionTweener;
public float transitionTarget = 0f;
private float lastValue;
private Vector3 startValue;
private Vector3 Endvalue => new Vector3(0.0f, transitionTarget, 0.0f);
private const float duration = 3f;
void Start() {
lastValue = transitionTarget;
startValue = transform.position;
transitionTweener = transform.DOMove(Endvalue, duration)
.SetOptions(AxisConstraint.Y)
.SetEase(Ease.Linear)
.SetLoops(2, LoopType.Yoyo)
.SetAutoKill(false);
transitionTweener.OnComplete(RestartTween);
}
private void RestartTween() {
transitionTweener.ChangeEndValue(Endvalue);
transitionTweener.Restart();
}
private void Update() {
if (lastValue != transitionTarget && transitionTweener.CompletedLoops() == 0) {
var t = duration * (transform.position.y - startValue.y) / (transitionTarget - startValue.y);
if (t > duration) {
transitionTweener.Goto(2 * duration - transitionTweener.position, true);
} else {
transitionTweener.ChangeEndValue(Endvalue);
transitionTweener.Goto(t, true);
}
lastValue = transitionTarget;
}
}
}
Note:
To comply with your spec, that the tween should update when the transitionTarget
is changed from within the inspector, the solution compares lastValue
with transitionTarget
every frame in the update method.
To avoid this in the real application, you could probably do the following:
- rename the update method to something like
UpdateTarget
, so you trigger it manually.
- use a property to encapsulate
transitionTarget
and use the properties setter to invoke UpdateTarget
- additionally invoke
UpdateTarget
when the tween is completed: transitionTweener.OnComplete(RestartTween);
†An alternative option not considered in this Answer would be to recreate the tweens in the event of a change of transitionTarget
.