こんにちは、Unityエンジニアのオオバです。
DOTweenはUnityでアニメーションをスクリプトから実装する時に超絶便利なアセットです。ゲーム中の演出やUIアニメーションを実装する時に大活躍します。
演出はテンポが重要なのですが、ロジック面ではイベント管理が重要です。
例えばアニメーションの
- 開始タイミング
- 更新タイミング
- 終了タイミング
- 中断タイミング
など、様々なタイミングで異なる処理を挟む必要が出てきます。これらを OnStart(開始タイミング)
や OnComplete(終了タイミング)
を使って実装します。
実はDOTweenのコールバック関数は実行順の把握が非常に重要です。主要なコールバック関数は8種類もあり 実行順を正しく理解していないと不具合に繋がります。
ぜひこの記事を参考に実行順を理解していきましょう。
DOTweenを使ってこれからスクリプトでアニメーションを実装していきたい方はこちらの記事を参考にしてみてください。
👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!
DOTweenコールバック関数の使い方
そもそもDOTweenのコールバック関数の使い方について解説します。いろんなコールバックが実装されていますが、よく使うOnComplete(Tween完了時のコールバック)を例に解説していきます。次のコードを見てみましょう。
transform.DOLocalMove(new Vector3(1f, 0, 0), 0.5f)
.OnComplete(()=>Debug.Log("Tween完了"));
Tweenに対してOnComplete関数を実行します。使い方は簡単で引数に関数を渡して使うのです。
また次のコードのように一度Tweenを変数に格納してOnCompleteを指定することも可能です。
var tween = transform.DOLocalMove(new Vector3(1f, 0, 0), 0.5f);
tween.OnComplete(()=>Debug.Log("Tween完了"));
こんな感じDOTweenのコールバックを使用していきます。
UniTaskを使うと更に便利に使えるDOTween
DOTweenはUniTaskと連携して使えます。UniTaskとはUnityで非同期処理を実装する時によく使われるアセットです。
例えばアニメーションの完了を待機して次の処理を実行するという何かを待機する処理はまさに非同期処理です。
↑上の動画はUniTaskとDOTweenを使ったサンプルです。3人のTweenを待機した後全員でジャンプするように実装しています。詳しくは↓こちらの記事を参考にしてみてください。
またUniTaskを使うとDOTweenのコールバックは次のように書き直せます。
await transform.DOLocalMove(new Vector3(1f, 0, 0), 0.5f).ToUniTask();
Debug.Log("Tween完了");
await
と ToUniTask
を使うことでOnCompleteを使うことなく実装できます。上から順に処理を書くことができるため可読性の高いコードになります。
しかしUniTaskは中断処理に気をつけないといけません。UniTaskとDOTweenのレン刑事も同じです。正しく中断処理を実装しないと不具合につながるので、次の記事を参考にUniTaskの中断処理を確認してみてください。
8種類のDOTweenコールバック
ではDOTweenのコールバック実行順位ついて具体的に解説していきます。
ちなみにDOTweenのコールバック関数は主要なところでいうと8種類あります。表にまとめてみました。
コールバック関数 | 内容 |
---|---|
OnStart | Tweenの開始タイミング |
OnPlay | Tweenの再生タイミング |
OnUpdate | Tweenの更新タイミング |
OnPause | Tweenポーズタイミング |
OnStepComplete | Tweenのリピートタイミング |
OnComplete | Tweenの終了タイミング |
OnKill | Tweenの終了タイミング |
OnRewind | Tweenのリスタートタイミング |
これらがどういう順番で実行されるのか解説していきます。
DOTweenコールバックの基本的な実行順序
DOTweenのコールバック順を調査するために次のような検証コードを用意しました。
void Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
}
上記のソースコードを実行すると次のような出力結果になります。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnComplete
- OnKill
OnStart
から始まり OnKill
で終わります。これがDOTweenコールバックの基本的な実行順です。
さてここから様々なパターンでTweenの再生や停止させて挙動を確認していきます。
オオバが執筆したDOTweenの教科書では動画でよりわかりやすくDOTweenのコールバックを理解できるようになっています。
DOTweenを深く学んでいきたい方はぜひチェックしてみてください。
※ちなみに↑上記の動画は有料ページのチラ見せです。
DOTweenの中断(Kill)と完了(Complete)
まずはTweenの中断と完了におけるDOTweenコールバックの実行順をチェックしていきます。
結論から言うと 呼ばれる順番は基本的に同じ ですが 呼ばれるコールバックと呼ばれないコールバックが登場する ため注意が必要です。
次の4つのパターンで確認していきます。
①中断 Kill(false)
②中断 Kill(true)
③完了 Complete(false)
④完了 Complete(true)
DOTweenの中断Kill(false)
Tween中断による挙動を確認していきます。Tweenの中断は Kill
メソッドを使います。Killメソッドにはbool型の引数を渡せるのがポイントです。 引数によって実行されるコールバックが変わる ため注意してください。
具体的には次のコードを実行していきます。
1秒かけて動作するDOTweenを実行して0.5秒のタイミングで中断させます。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
yield return new WaitForSeconds(0.5f);
// 中断メソッドにfalseを代入
tween.Kill(false);
}
すると、次のような結果になります。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnKill
基本形から変わったのは、OnStepComplete
と OnComplete
が呼ばれなくなりました。
DOTweenの中断Kill(true)
次はTweenの中断メソッドKillの引数にtrueを代入して変化を確認してみましょう。具体的には次のコードを実行します。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
yield return new WaitForSeconds(0.5f);
// 中断メソッドにtrueを代入
tween.Kill(true);
}
すると結果はこうなります。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnComplete
- OnKill
ポイントは OnComplete
の有無です。今回の Kill(true)
ではOnKill、OnComplete共に実行される点に注意しましょう。
DOTweenの完了Complete(false)
次にTweenの途中で完了メソッドCompleteを実行してみます。先のKillとCompleteの違いはTweenの進捗具合です。Killは実行タイミングで中断しますがCompleteは最後まで実行された状態にジャンプします。
またCompleteはKill同様bool型の引数が代入できるため「false」を設定して結果を確認していきましょう。具体的には次のコードを実行していきます。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
yield return new WaitForSeconds(0.5f);
// 完了メソッドにfalseを代入
tween.Complete(false);
}
結果は以下のようになりました。
- OnStart
- OnPlay
- OnUpdate
- OnComplete
- OnKill
Kill(true)
と同じ結果です。
DOTweenの完了Complete(true)
次に完了メソッドCompleteの引数にtrueをセットして挙動を確認していきます。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
yield return new WaitForSeconds(0.5f);
// 完了メソッドにtrueを代入
tween.Complete(true);
}
すると結果は次のようになります。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnComplete
- OnKill
Complete(false)
との違いは OnStepCompleteの有無 です。 Complete(true)
を実行するとOnStepCompleteも呼ばれる点に注意しましょう。
DOTweenのポーズ(Pause)と再開(Play or Restart)、
次にTweenのポーズと再開によるコールバック挙動の違いを確認していきます。Tweenのポーズってあまり使われないと思われがちですが、ゲーム開発中はゲームの一時停止機能を実装する時に必要となります。
再びTweenを再開する時にPlayまたはRestartでアニメーションを再生するのですが、それぞれコールバックの挙動が変わってくるため注意しましょう。
DOTweenの途中でポーズ(Pause)
まずはTweenの途中でポーズしてみましょう。具体的には次のコードを実行してみます。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
yield return new WaitForSeconds(0.5f);
// ポーズ
tween.Pause();
}
すると以下の結果になります。
- OnStart
- OnPlay
- OnUpdate
- OnPause
ポーズをすると OnPause が実行されます。当然ですがTweenが中断、完了していないため、OnComplete、OnKillは呼ばれません。
DOTweenの途中でポーズ(Pause)後に再開(Play)
ではTweenをポーズした後Playメソッドで再開してみましょう。次のコードを実行してみます。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
yield return new WaitForSeconds(0.5f);
// ポーズ
tween.Pause();
yield return new WaitForSeconds(0.5f);
// 再開
tween.Play();
}
すると次のような結果になります。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnPause
- OnPlay
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnComplete
- OnKill
OnPause
が呼ばれた後、再開時に OnPlay が実行されます。注意点として 再開時にOnStartは呼ばれないこと 。OnStartは初回しか実行されない点に注意しましょう。逆にOnPlayは何度も呼ばれます。
DOTweenの途中でポーズ(Pause)後に再開(Restart)
ポーズ後の再開メソッドをPlayからRestartに変えて挙動を確認していきます。具体的には次のソースコードを実行します。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
yield return new WaitForSeconds(0.5f);
// ポーズ
tween.Pause();
yield return new WaitForSeconds(0.5f);
// 再開
tween.Restart();
}
すると結果は以下のようになりました。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnPause
- OnUpdate(※1度だけ実行)
- OnRewind
- OnPlay
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnComplete
- OnKill
Restartを使った再開はOnPlayではなく OnRewind が実行されます。Pause後に 1度だけOnUpdateが呼ばれる謎仕様 に注意です。
DOTweenの途中で最初から再生(Restart)
Tween再生後Restartで最初から再生させてみます。具体的には次のソースコードです。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x));
yield return new WaitForSeconds(0.5f);
// リスタート
tween.Restart();
}
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnRewind
- OnPause
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnComplete
- OnKill
ポイントは ポーズしていないにも関わらずOnPauseが呼ばれること です。かなり引っかかりやすいため注意しましょう。
DOTweenのループに関するコールバック挙動
次にループしたTweenでコールバック挙動を確認していきます。Tweenのループはよく使います。例えばゲームタイトル画面内の「TAP TO START」はTweenをループさせて点滅アニメーションさせることが多いです。
またタップするとTweenを中断して何か別の処理を実行させるケースはよくあります。こういう実装をするときこそ適切にコールバック順を知っておく必要があるため本章で理解を深めていきましょう。
有限回数ループ設定したDOTween
SetLoopsメソッドを使用してループするTweenで挙動を確認していきます。具体的には次のコードを実行します。
void Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x))
// 3回ループ設定
.SetLoops(3);
}
結果は以下のようになります。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnStepComplete(1回目)
- OnUpdate(毎フレーム実行)
- OnStepComplete(2回目)
- OnUpdate(毎フレーム実行)
- OnStepComplete(3回目)
- OnComplete
- OnKill
ポイントはループ実行時に OnStepComplete が呼ばれる点です。逆にOnStepCompleteとOnUpdate以外呼ばれないことに注意してください。
OnStepCompleteにLoopTypeは影響を受けない
SetLoopsメソッドの第1引数でループ回数を設定し、第2引数にはLoopTypeを設定します。
- LoopType.Restart
- LoopType.Yoyo
- LoopType.Incremental
3種類のLoopTypeどれを設定しても コールバックには影響しない ということを覚えておきましょう。
無限ループ設定のDOTween
SetLoopsの引数に負の整数を設定して無限ループのTweenで挙動を確認していきます。
void Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x))
// 無限ループ設定
.SetLoops(-1);
}
次のような結論になります。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnUpdate(毎フレーム実行)...永遠に続く
無限ループに設定しているため、OnStepCompleteとOnUpdateが永遠に繰り返されます。
無限ループDOTween中にKill(true or false)
次に無限ループDOTween中の中断処理を確認していきましょう。具体的には次のソースコードのようにKillメソッドを実行していきます。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x))
// 無限ループ設定
.SetLoops(-1);
yield return new WaitForSeconds(2f);
// 無限ループDOTweenを中断
tween.Kill(true); // or tween.Kill(false);
}
次のような結果になりました。
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnUpdate(毎フレーム実行)
- OnKill
ポイントはKillメソッドの引数にtrueを入れてもfalseを入れても OnComplete は呼ばれないということです。つまり 無限ループTweenではOnCompleteは実行されません。
無限ループのDOTween中にCompleteは意味がない
最後に無限ループDOTweenに対してTweenの完了メソッドCompleteを実行してみます。
IEnumerator Start()
{
var tween = transform.DOLocalMoveX (2f, 1f)
.OnStart (() => Debug.Log ("OnStart"))
.OnPlay (() => Debug.Log ("OnPlay"))
.OnKill (() => Debug.Log ("OnKill"))
.OnComplete (() => Debug.Log ("OnComplete"))
.OnPause (() => Debug.Log ("OnPause"))
.OnRewind (() => Debug.Log ("OnRewind"))
.OnStepComplete (() => Debug.Log ("OnStepComplete"))
.OnUpdate (() => Debug.LogWarning ("OnUpdate"))
.OnWaypointChange (x => Debug.Log ("OnWaypointChange" + x))
// 無限ループ設定
.SetLoops(-1);
yield return new WaitForSeconds(2f);
tween.Complete();
}
- OnStart
- OnPlay
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnUpdate(毎フレーム実行)
- OnStepComplete
- OnUpdate(毎フレーム実行)...永遠に続く
※Tweenは止まりません
結論、無限ループのDOTweenに対してCompleteを実行してもTweenは止まりません。なぜなら無限ループのため完了という概念が存在しないからです。無限ループTweenを止めたい場合はKillを実行するということを覚えておきましょう。
DOTweenコールバックのまとめ
本記事ではDOTweenに実装されているコールバック関数について解説してきました。内容を簡単にまとめます。
①DOTweenの主要コールバックは8種類
②OnStartの呼び出しは1度きり
③DOTween終了時には必ずOnKill
④終了メソッドの引数によってOnCompleteは呼ばれない
⑤無限ループ設定時はOnCompleteは呼べない
DOTweenに対してポーズや再開、ループといった設定をすることで呼ばれるコールバックと呼ばれないコールバックが出てきますし、呼び出し順も変わってきます。
このあたりしっかりと理解して使わないと思わぬ落とし穴にハマり貴重な時間を無駄にしてしまう可能性があります。ぜひ本記事を参考にDOTweenのコールバックの処理順を改めてチェックしてみてください。
DOTweenを使用することでUnity製のゲームにアニメーション、演出を簡単に追加することが可能です。
例えば巻き戻し演出。

【DOTween】巻き戻し(逆再生)と早送りする方法
DOTweenで巻き戻し(逆再生)と早送りの実装方法を紹介します。難しそうに感じるかもしれませんが意外と簡単に実装できるのでオススメです。
SetLookAtを使った向きの制御。
それぞれの記事を参考にチェックしてみてください。

筆者のXをフォローしよう
- Unity6000.0.32f1
- DOTween v1.2.765