概要
DoTween を途中から再生する方法が見つからなかったので作ってみました。
具体的には、Tween や Sequence を n 秒スキップする拡張メソッドを作り、それを利用して途中から再生できる拡張メソッドを作りました。
上の gif は、10秒で反時計回りに一周する sequence を用意して Cube に適応し、Button を押すと 2.5 秒スキップするようにしたものです。
実装方法は、次のフレームで目的の再生位置になるように timeScale を調整し、次のフレームで timeScale を元に戻すというものです。
Skip() にマイナスの値を入れた場合は、n 秒前の再生時間になるよう Restart() と Skip() を実行します 。
途中から再生する正式な方法や改良案などがありましたら、コメントをお願いします。
注意事項
スキップ前後のTime.deltaTime
がほぼ同じという前提で実装しているので、FPSが不安定なほど目的の再生位置からズレます。
1フレーム進める必要があるので、停止中の Tween/Sequence でも1フレームだけ再生します。
コード
お手持ちの DoTweenEx クラスに追加してご利用ください。
DoTweenEx.cs
using DG.Tweening;
using UnityEngine;
namespace Extensions
{
public static class DoTweenEx
{
/// <summary>
/// [Caution] The more unstable the FPS, the more back and forth from the specified time.
/// Since it is necessary to play 1 frame, let it play for 1 frame even if it is paused.
/// </summary>
public static Tween Skip(this Tween tween, float second, bool ignoreTimeScale = true)
{
if (second == 0f) return tween;
var isPlayed = tween.IsPlaying();
if (second < 0f)
{
var elapsedSecond = tween.Elapsed(true);
second = elapsedSecond + second;
tween.Restart();
if (second <= 0f)
{
if (!isPlayed) tween.Pause();
return tween;
}
}
var timeScale = tween.timeScale;
tween.timeScale = (second + (isPlayed ? Time.deltaTime : 0f)) / Time.deltaTime / (ignoreTimeScale ? 1f : timeScale);
if (!isPlayed) tween.Play();
DOVirtual.DelayedCall(float.Epsilon, () =>
{
if (tween == null) return;
tween.timeScale = timeScale;
if (!isPlayed) tween.Pause();
});
return tween;
}
/// <summary>
/// [Caution] The more unstable the FPS, the more back and forth from the specified time.
/// Since it is necessary to play 1 frame, let it play for 1 frame even if it is paused.
/// </summary>
public static Sequence Skip(this Sequence sequence, float second, bool ignoreTimeScale = true)
{
if (second == 0f) return sequence;
var isPlayed = sequence.IsPlaying();
if (second < 0f)
{
var elapsedSecond = sequence.Elapsed(true);
second = elapsedSecond + second;
sequence.Restart();
if (second <= 0f)
{
if (!isPlayed) sequence.Pause();
return sequence;
}
}
var timeScale = sequence.timeScale;
sequence.timeScale = (second + (isPlayed ? Time.deltaTime : 0f)) / Time.deltaTime / (ignoreTimeScale ? 1f : timeScale);
if (!isPlayed) sequence.Play();
DOVirtual.DelayedCall(float.Epsilon, () =>
{
if (sequence == null) return;
sequence.timeScale = timeScale;
if (!isPlayed) sequence.Pause();
});
return sequence;
}
public static Tween Restart(this Tween tween, float skipSecond, bool ignoreTimeScale = true)
{
tween.Restart();
return tween.Skip(skipSecond, ignoreTimeScale);
}
public static Sequence Restart(this Sequence tween, float skipSecond, bool ignoreTimeScale = true)
{
tween.Restart();
return tween.Skip(skipSecond, ignoreTimeScale);
}
}
}