DOTween完全に理解するその5 Sequence編

前回：[DOTween完全に理解するその4 DOPath編](https://qiita.com/BEATnonanka/items/50cacac803f88f5074dd)

#今回解説するアニメーション
今回はDOTween最重要と言っても過言ではない**Sequence**について解説します。
Tweenを繋げて1つのアニメーションとして連続実行させることができ、
複雑なアニメーションを作ることができます。

**開発環境**
Unity:2019.4.0f1
DOTween:v1.2.335


# Append

![sequence11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203331/d3ff0694-79ae-2580-62ef-c7a0149bf50e.gif)

```c#
var sequence = DOTween.Sequence(); //Sequence生成
    //Tweenをつなげる
    sequence.Append(transform.DOMoveX(7, 1))  
            .Append(transform.DOMoveX(-7, 1)) 
            .Append(transform.DOMoveX(0, 1)); 
```
Sequenceの**末尾**にTweenを追加します。
前のTweenが終わり次第次のアニメーションが実行されます。
内部的にはTweenの終了を待っているわけではなく、
**前のTweenが終わる時間までDelayしている感じです**

# AppendInterval / AppendCollback
![sequence12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203331/85eed718-3583-9f3b-eb27-b7a20d51a6fb.gif)

```c#
//TweenをつなげつつAppendIntervalとAppendCallbackを使う
sequence.Append(transform.DOMoveX(7, 1))
        .AppendInterval(0.25f)
        .AppendCallback(() =>
        {
            targetRenderer.material.color = Color.yellow;
        })
        .Append(transform.DOMoveX(-7, 1))
        .AppendInterval(0.25f)
        .AppendCallback(() =>
        {
            targetRenderer.material.color = Color.cyan;
        })
        .Append(transform.DOMoveX(0, 1));
```
Appendと同様に**末尾に**待機時間やコールバックを追加します。

#Join

![sequence21.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203331/9a119a98-f56b-d450-7456-527c81f8af60.gif)

```c#

//Joinで並列動作するTweenを追加
sequence.Append(transform.DOMoveX(7, 1))
        .Join(transform.DORotate(new Vector3(0, 180), 1.5f, RotateMode.WorldAxisAdd)) //0 ~ 1.5
        .AppendInterval(0.25f) //1.5 ~ 1.75
        .AppendCallback(() =>
        {
            targetRenderer.material.color = Color.yellow;
        })
        .Append(transform.DOMoveX(-7, 1)) 
        .Join(transform.DORotate(new Vector3(0, 180), 1.5f, RotateMode.WorldAxisAdd)) //1.75 ~ 3.25
        .AppendInterval(0.25f) //3.75 ~ 4
        .AppendCallback(() =>
        {
            targetRenderer.material.color = Color.cyan;
        })
        .Append(transform.DOMoveX(0, 1))
        .Join(transform.DORotate(new Vector3(0, 180), 1.5f, RotateMode.WorldAxisAdd)); //4 ~ 5.5
```
直前のTweenと**並行**して動作するようにTween追加を行います。
Joinの後Appendした場合**もっとも時間のかかったTweenの後に実行されます。**

|API|アニメーション時間|開始時間〜終了時間|
|---|---|---|
|Append|5秒|0〜5秒|
|Join|10秒|0〜10秒|
|Append|5秒|10~15秒


#Insert / InsertCallback

![sequence31.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203331/561ea075-320d-0702-49d2-27987422a47b.gif)

```c#
//Method2と同じ動きをInsertで行う
sequence.Append(transform.DOMoveX(7, 1.5f))  //0 ~ 1.5
        .AppendInterval(0.25f)                                 //1.5 ~ 1.75
        .Append(transform.DOMoveX(-7, 1.5f)) //1.75 ~ 3.25
        .AppendInterval(0.25f)                                 //3.25 ~ 3.5
        .Append(transform.DOMoveX(0, 1.5f)); //3.5 ~ 5

sequence.Insert(0, transform.DORotate(new Vector3(0, 0, 1800), 5.5f, RotateMode.WorldAxisAdd));

sequence.InsertCallback(1.75f, () =>
{
    targetRenderer.material.color = Color.yellow;
});
sequence.InsertCallback(3.5f, () =>
{
    targetRenderer.material.color = Color.cyan;
});
```
時間を指定して、Tweenerやコールバックを**挿入**します。
AppendやJoinとは異なり**前後のTweenerなどに関係なく並列で動作します。**

#Prepend / PrependCallback / PrependInterval
![sequence41.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203331/bf8de8cc-e5f9-13e3-02ca-ea1c0cf260bf.gif)


```c#
var sequence = DOTween.Sequence();
sequence.Append(transform.DOMoveX(7, 1))
        .Append(transform.DOMoveX(-7, 1))
        .Append(transform.DOMoveX(0, 1));

//前に追加する
sequence.PrependInterval(0.5f);
sequence.PrependCallback(() => { targetRenderer.material.color = Color.red; });
sequence.Prepend(transform.DORotate(new Vector3(0, 0, 180), 1, RotateMode.WorldAxisAdd));
```
Sequenceの**先頭**にTweenerやコールバックを追加します。
先頭に追加されていくので、**最後に追加したものから最初に実行されます。**

# Sequence同士をSequenceで繋ぐ
![sequence51.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203331/9f0d0cd0-2772-cda4-dcf5-dd0fbe2ad9a1.gif)

```c#
//SequenceにSequenceに追加する
var move = transform.DOMoveX(6, 0.5f)
                    .SetRelative(true)
                    .SetLoops(2, LoopType.Incremental);
var rotate1 = transform.DORotate(Vector3.forward * 90, 0.5f, RotateMode.WorldAxisAdd);
var sequenceA = DOTween.Sequence()
                       .Append(move)
                       .Join(rotate1);

var scale = transform.DOScale(1, 0.5f);
var rotate2 = transform.DORotate(Vector3.forward * 90, 0.5f, RotateMode.WorldAxisAdd)
                       .SetRelative(true);
var sequenceB = DOTween.Sequence()
                       .Append(scale)
                       .Insert(0.5f, rotate2)
                       .SetLoops(2, LoopType.Yoyo);

_tween = DOTween.Sequence()
                .Append(sequenceA)
                .Join(sequenceB);

_tween = DOTween.Sequence()
                .Append(_tween)
                .Append(transform.DORotate(Vector3.forward * 180, 1f, RotateMode.WorldAxisAdd));
```
SequenceもTweenerなので、Sequence同士をSequenceで繋ぐこともできます。
またSetLoops()をしたTweenerをSequenceに含めることもできますし
Sequence自体にSetLoops()をすることもできます。

#Sequenceを使用する際の注意点
## 無限ループが使えない
例えば以下のような**Sequence内のTweenerが無限ループする設定は無視されます。**
Sequenceが無限ループする場合は問題ありません。

```c#
var move = transform.DOMoveX(1, 0.5f)
                    .SetRelative(true)
                    .SetLoops(-1, LoopType.Incremental);
var sequence = DOTween.Sequence()
                      .Append(move);
```
##無効化される設定
**SetAutoKill(true)やSetSpeedBased(true)などの設定は、
Sequence追加時にfalseに設定されてしまいます。**

普通に考えればSeuqnceが終わっていない以上
PlayBackwordなどで実行済みのTweenを実行する必要が出てくるので
その際にKillされていたら困る為、設定が無効化されるのでしょう。

SpeedBaseもSequence自体は全て時間ベースで追加や挿入が行われる為
設定が無効化されるのだと思われます。


## Sequenceに追加する前に設定を終わらせる
Tweenに対する設定はSequenceに追加する前に終わっている必要があります。
以下のように**Sequence追加後のTweenへの設定は無視されます。**
Sequence追加前に行った設定は正しく反映されます。(もちろん無効化されるもの以外)

```c#
var move = transform.DOMoveX(1, 0.5f)
                    .SetRelative(true);
var sequence = DOTween.Sequence()
                      .Append(move);
move.SetDelay(3f);
move.SetLoops(2, LoopType.Incremental);
move.From(0);
```

#まとめ
Sequenceは作りが単純な分できることがかなり多い為、様々な場面で活躍します。
UnityはデフォルトではAnimator/Animationを使ってアニメーションを作ると思います。
ただ、Animatorの場合は固定化されたアニメーションを作るのには向いていますが
状況によって微妙に変化するアニメーションなど
コードから動きを細かく制御したい場合はSequenceが有効です。
Inspectorにアニメーション用のパラメータを出しておけば調整も簡単です。
あとこれは全てのTweenに該当しますが**TimeScaleで個別に速度を変えられる点も**強力だなぁと最近思いました。

最後に最近作ったどこで使うかわからないアニメーションを置いておきます。ご査収ください。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">どこで使うかわからないけどアニメーション作るの楽しい <a href="https://t.co/KO2NrDwToc">pic.twitter.com/KO2NrDwToc</a></p>&mdash; BEAT (@BEATnonanka) <a href="https://twitter.com/BEATnonanka/status/1283001862908592133?ref_src=twsrc%5Etfw">July 14, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>