Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
44
Help us understand the problem. What are the problem?
@broken55

Unity DOTween 入門

この記事はUnity #2 Advent Calendar 2020の5日目の記事です

はじめに

  • DOTweenというライブラリを使うとオブジェクトをアニメーションさせたり、マテリアルの色を徐々に変化させたい場合に非常に便利です
  • アニメーションクリップを作ることなく動きを作成できるので、プログラマだけでアニメーションを実装することができます
    • デザイナーにアニメーションを作ってもらうまでのモックとして利用することもできます
  • 今回は基本的な使い方、よく使う拡張、コールバック、SequenceをDOTween入門として紹介します

参考

DOTween Asset Store
https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676?locale=ja-JP

公式ドキュメント
http://dotween.demigiant.com/

セットアップ

Asset Storeでdotweenと検索して入手します
PRO版は内部のソースコードが見れたりしますが、基本的には無料版で問題ありません。
image.png

Importが完了したら初期セットアップを行います
Open DOTween Utility Panelを開き
image.png

Setup DOTween...を選択し、Applyでセットアップは完了です
image.png
image.png

基本編

まず動かしてみる

3秒かけて(5,0,0)へ移動するというコードを書いてみます。
UpdateやCoroutineを使って書こうとすると大変ですが、DOTweenを使えば1行で書くことができます。
このコードから拡張していろいろな機能を紹介していきます
move.gif

using DG.Tweening;  //DOTweenを使うときはこのusingを入れる
using UnityEngine;

public class MoveTest : MonoBehaviour
{
    void Start()
    {
        // 3秒かけて(5,0,0)へ移動する
        this.transform.DOMove(new Vector3(5f, 0f, 0f), 3f);
    }
}

繰り返す SetLoops

SetLoopsを使うと、動作を繰り返すことができます
繰り返す回数(loops)と繰り返し方法(loopType)を設定します
loopsに-1を入れるとずっと繰り返す挙動になります

loop.gif

void Start()
{
    //(5,0,0)へ1秒で移動するのを3回繰り返す
    this.transform.DOMove(new Vector3(5f, 0f, 0f), 1f).SetLoops(3,LoopType.Restart);
}

なお、Loop Typeパラメータ別の挙動は下記のようになっています

Loop Type 説明
Yoyo 移動後、最初の位置に戻るように動きます
Restart 移動後、最初の位置から再び移動を開始します
Increment 移動後、その位置から再び移動を開始します

loop2.gif

遅延 SetDelay

SetDelayを使うことで動作が開始するのを遅延させることができます

void Start()
{
    //3秒待ってから(5,0,0)へ1秒で移動する
    this.transform.DOMove(new Vector3(5f,0f,0f), 1f).SetDelay(3f);
}

Ease

SetEaseを使うと始点と終点をどのように繋ぐか設定することができます

ease.gif

void Start()
{
    //(5,0,0)へ1秒でリニア移動する
    this.transform.DOMove(new Vector3(5f,0f,0f), 1f).SetEase(Ease.Linear);
}

Easeには様々な種類があります。
実際使う際はいろいろなパラメータを変えながら、作りたい動きを探すのがいいでしょう。
image.png
https://github.com/Nightonke/WoWoViewPager/blob/master/Pictures/ease.png

なおSetEaseを使わない場合はOutQuadがデフォルトで設定されるようです。

全部一緒に使う

これらのパラメータは一度に設定して使用することができます

void Start()
{
    //2秒待ってから(5,0,0)へ3秒で移動するのを4回(2往復) OutBounceで行う
    this.transform.DOMove(new Vector3(5f, 0f, 0f), 3f).SetDelay(2f).SetLoops(4, LoopType.Yoyo).SetEase(Ease.OutBounce);
}

読みやすくするためにドットで改行して書くことも可能です

void Start()
{
    //2秒待ってから(5,0,0)へ3秒で移動するのを4回(2往復) OutBounceで行う
    this.transform.DOMove(new Vector3(5f, 0f, 0f), 3f)
                    .SetDelay(2f)
                    .SetLoops(4, LoopType.Yoyo)
                    .SetEase(Ease.OutBounce);
}

止める Kill

DOTweenの実行を止める方法を4つ紹介します。
シチュエーションに合った止め方を選択してください。
Killするとオブジェクトは移動中であってもその場で停止します。

Tween tween;

void Start()
{
    this.tween = this.transform.DOMove(new Vector3(5f, 0f, 0f), 2f).SetLoops(-1, LoopType.Yoyo);
}

void Update()
{
    if(Input.GetKeyDown(KeyCode.A))
    {
        //返り値を保存しておいて止める方法
        this.tween.Kill();
    }

    if(Input.GetKeyDown(KeyCode.S))
    {
        //参照元を指定して止める方法
        this.transform.DOKill();
    }

    if(Input.GetKeyDown(KeyCode.D))
    {
        //Objectを指定して止める方法
        DOTween.Kill(this.transform);
    }

    if(Input.GetKeyDown(KeyCode.F))
    {
        //全ての実行を止める方法
        DOTween.KillAll();
    }
}

いろいろな拡張

移動させるDOMove以外の拡張を紹介します

ローカル座標での移動 DOLocalMove

//ローカル座標の(5,0,0)へ3秒で移動する
this.transform.DOLocalMove(new Vector3(5f, 0f, 0f), 3f);

X,Y,Zの移動量を指定して移動する

//現在の座標からX+5の座標へ3秒で移動する
this.transform.DOMoveX(5f, 3f);

//現在の座標からY+5の座標へ3秒で移動する
this.transform.DOMoveY(5f, 3f);

//現在の座標からZ+5の座標へ3秒で移動する
this.transform.DOMoveZ(5f, 3f);

ジャンプして移動する DOJump

jump.gif

//(5,0,0)の位置に4秒で2回ジャンプして移動する
this.transform.DOJump(new Vector3(5f, 0f, 0f), jumpPower: 3f, numJumps: 2, duration: 4f);}

jumpPowerの値を調整するとジャンプの高さが変わります

回転 DORotate

rotate.gif

//1秒でRotationが(0,180,0)になるように回転する
this.transform.DORotate(Vector3.up * 180f, 1f)

第3引数に回転方法を設定できます

public enum RotateMode
{
    //
    // 概要:
    //     Fastest way that never rotates beyond 360°
    Fast = 0,
    //
    // 概要:
    //     Fastest way that rotates beyond 360°
    FastBeyond360 = 1,
    //
    // 概要:
    //     Adds the given rotation to the transform using world axis and an advanced precision
    //     mode (like when using transform.Rotate(Space.World)).
    //     In this mode the end value is is always considered relative
    WorldAxisAdd = 2,
    //
    // 概要:
    //     Adds the given rotation to the transform's local axis (like when rotating an
    //     object with the "local" switch enabled in Unity's editor or using transform.Rotate(Space.Self)).
    //     In this mode the end value is is always considered relative
    LocalAxisAdd = 3
}

FastFastBeyond360の比較すると図のようになります。
どちらもYが0度から350度へ回しています
fast.gif

this.transform.DORotate(Vector3.up * 350f, 1f, mode: this.rotateMode)

WorldAxisAddLocalAxisAddは現在の角度から何度回転させるかという操作になります
回転させるという意味ではこちらの方が直感的かもしれません

色を変える DOColor

color.gif

[SerializeField]
Renderer rendererComponent;

void Start()
{
    //1秒で赤色に変える
    this.rendererComponent.material.DOColor(Color.red, 1f);
}

MaterialだけでなくImageにも使うことができます

color2.gif

[SerializeField]
UnityEngine.UI.Image image;

void Start()
{
    //1秒で赤色に変化し元の色に戻るのをずっと繰り返す
    this.image.DOColor(Color.red, 1f).SetLoops(-1,LoopType.Yoyo);
}

アルファを変化させる DOFade

fade.gif

[SerializeField]
UnityEngine.UI.Image image;

void Start()
{
    //1秒でImageのアルファを0にする
    this.image.DOFade(endValue: 0f, duration: 1f);
}

DOFadeはImageだけでなくCanvasGroupやMaterialにもつかうことができます

//1秒でCanvasGroupのアルファを0にする
this.canvasGroup.DOFade(endValue: 0f, duration: 1f);

//1秒でMaterialのアルファを0にする
this.rendererComponent.material.DOFade(endValue: 0f, duration: 1f);

音量のフェードを行う DOFade

また、AudioSourceもDOFadeで操作することができます

[SerializeField]
AudioSource audioSource;

void Start()
{
    //1秒でAudioSourceのVolumeを0にする
    this.audioSource.DOFade(endValue: 0f, duration: 1f);
}

指定したポイントを通過する DOPath

path.gif

Vector3[] path =
{
    new Vector3(0f,0f,10f),
    new Vector3(5f,0f,10f),
    new Vector3(5f,0f,0f),
    new Vector3(0f,0f,0f)
};

//指定したPathを10秒で通り、進行方向を向く
this.transform.DOPath(path, 10f).SetLookAt(0.01f);

DOPathにはVector3の配列でPathを渡すことができます
またSetLookAtで進行方向に向けることができます
また、DOPathの実行中はシーンビュー上にPathが表示されます

コールバックを使う

DOTweenは実行時に各種タイミングでコールバックを呼び出すことができます
代表的なものを紹介していきます

完了時のコールバック OnComplete

//(5,0,0)に2秒で移動し、移動が完了したらログを出す
this.transform.DOMove(new Vector3(5f, 0f, 0f), 2f).OnComplete(() =>
{
    Debug.Log("OnComplete!");
});

ラムダ式を使わず、このように書くこともできます

void Start()
{
    //(5,0,0)に2秒で移動し、移動が完了したらログを出す
    this.transform.DOMove(new Vector3(5f, 0f, 0f), 2f).OnComplete(CallbackFunction);
}

void CallbackFunction()
{
    Debug.Log("CallbackFunction");
}

OnComplete内でさらに新しい動作を登録することもできます

//(5,0,0)に2秒で移動し、移動が完了したらY軸180度に1秒で回転する
this.transform.DOMove(new Vector3(5f, 0f, 0f), 2f).OnComplete(() =>
{
    this.transform.DORotate(Vector3.up * 180f, 1f);
});

complate.gif

実行開始時のコールバック OnStart

DOTweenの実行時にコールバックが呼び出されます。

//(5,0,0)に2秒で移動し、移動開始時にログを出す
this.transform.DOMove(new Vector3(5f, 0f, 0f), 2f).OnStart(() =>
{
    Debug.Log("OnStart");
});

なお、SetDelayなどを使って遅延させた場合は遅延終了後にOnStartが呼ばれます

Debug.Log(Time.realtimeSinceStartup);

//(5,0,0)に2秒で移動し、移動開始時にログを出す
this.transform.DOMove(new Vector3(5f, 0f, 0f), 2f).OnStart(() =>
{
    Debug.Log("OnStart"+ Time.realtimeSinceStartup);
}).SetDelay(2f);
1.700801
OnStart3.848413

実行中のコールバック OnUpdate

実行中に毎フレーム呼び出されるコールバックです

//(5,0,0)に1秒で移動し、移動中にログを出す
this.transform.DOMove(new Vector3(5f, 0f, 0f), 1f).OnUpdate(() =>
{
    Debug.Log($"[{Time.frameCount}] OnUpdate {this.transform.position}");
});
[1] OnUpdate (0.2, 0.0, 0.0)
[2] OnUpdate (0.4, 0.0, 0.0)
...
[60] OnUpdate (5.0, 0.0, 0.0)

停止時のコールバック OnKill

Killが呼び出されたときのコールバックです

void Start()
{
    //(5,0,0)に10秒で移動し、Killされたらログを出す
    this.transform.DOMove(new Vector3(5f, 0f, 0f), 1f).OnKill(() =>
    {
        Debug.Log("OnKill"+transform.position);
    });
}

private void Update()
{
    if(Input.GetKeyDown(KeyCode.A))
    {
        this.transform.DOKill();
    }
}

なお移動完了時もOnKillは呼び出されます
下記のようにOnKillとOnComplete両方を登録した場合
移動完了時に両方のコールバックが呼び出されます。
途中でKillした場合はOnKillのみが呼び出されます

//(5,0,0)に10秒で移動し、Killされたらログを出す
this.transform.DOMove(new Vector3(5f, 0f, 0f), 1f)
    .OnKill(() =>
    {
        Debug.Log("OnKill"+this.transform.position);
    })
    .OnComplete(()=>
    {
        Debug.Log("OnComplete" + this.transform.position);
    });

Sequence

DoTweenを連続で使用する場合、OnCompketeを使って繋げるのもいいのですが
Sequenceという機能を使うと連続的な挙動を簡単に実装できます

seq1.gif

//Sequenceのインスタンスを作成
var sequence = DOTween.Sequence();

//Appendで動作を追加していく
sequence.Append(this.transform.DOMoveX(5f, 2f));
sequence.Append(this.transform.DOMoveY(2f, 1f));

//Playで実行
sequence.Play();

同時に実行する Join

Joinを使うと移動しながらスケールを変えるといった動作が可能になります

seq2.gif

//Sequenceのインスタンスを作成
var sequence = DOTween.Sequence();

//Appendで動作を追加していく
sequence.Append(this.transform.DOMoveX(5f, 2f));
sequence.Append(this.transform.DOMoveY(2f, 1f));
//Joinはひとつ前の動作と同時に実行される
sequence.Join(this.transform.DOScale(Vector3.one * 2f, 1f));

//Playで実行
sequence.Play();

待機する AppendInterval

AppendIntervalを使うことでSequenceの途中で待機を入れることができます
seq3.gif

//Sequenceのインスタンスを作成
var sequence = DOTween.Sequence();

//Appendで動作を追加していく
sequence.Append(this.transform.DOMoveX(5f, 2f));
//AppendIntervalで途中に待機を入れられる
sequence.AppendInterval(3f);
sequence.Append(this.transform.DOMoveY(2f, 1f));

//Playで実行
sequence.Play();

止める

SequenceもKillを使って止めます

//メンバ変数でSequenceのインスタンスを作成
Sequence sequence = DOTween.Sequence();

void Start()
{
    //Appendで動作を追加していく
    sequence.Append(this.transform.DOMoveX(5f, 2f));
    sequence.Append(this.transform.DOMoveY(2f, 1f));

    //Playで実行
    sequence.Play();
}

void Update()
{
    if(Input.GetKeyDown(KeyCode.A))
    {
        this.sequence.Kill();
    }
}

コールバック

Sequenceに対してもコールバックを設定することができます

//Sequenceのインスタンスを作成
var sequence = DOTween.Sequence();

//Appendで動作を追加していく
sequence.Append(this.transform.DOMoveX(5f, 2f));
sequence.Append(this.transform.DOMoveY(2f, 1f));

//Playで実行
sequence.Play()
    .OnStart(() =>
    {
        //開始時に呼ばれる
        Debug.Log("OnStart");
    })
    .OnUpdate(()=>
    {
        //実行中に毎フレーム呼ばれる
        Debug.Log("OnUpdate");
    })
    .OnComplete(() =>
    {
        //完了時に呼ばれる
        Debug.Log("OnComplete");
    })
    .OnKill(()=>
    {
        //Kill時に呼ばれる
        Debug.Log("OnKill");
    });

まとめ

DOTweenのいろいろな使用方法を見てきました
今回紹介した拡張だけでなくDOTweenには数多くの拡張が存在します。
ぜひいろんな使い方を探ってみてください

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
44
Help us understand the problem. What are the problem?