この記事は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版は内部のソースコードが見れたりしますが、基本的には無料版で問題ありません。
Importが完了したら初期セットアップを行います
Open DOTween Utility Panel
を開き
Setup DOTween...
を選択し、Apply
でセットアップは完了です
基本編
まず動かしてみる
3秒かけて(5,0,0)へ移動するというコードを書いてみます。
UpdateやCoroutineを使って書こうとすると大変ですが、DOTweenを使えば1行で書くことができます。
このコードから拡張していろいろな機能を紹介していきます
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を入れるとずっと繰り返す挙動になります
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 | 移動後、その位置から再び移動を開始します |
遅延 SetDelay
SetDelay
を使うことで動作が開始するのを遅延させることができます
void Start()
{
//3秒待ってから(5,0,0)へ1秒で移動する
this.transform.DOMove(new Vector3(5f,0f,0f), 1f).SetDelay(3f);
}
Ease
SetEase
を使うと始点と終点をどのように繋ぐか設定することができます
void Start()
{
//(5,0,0)へ1秒でリニア移動する
this.transform.DOMove(new Vector3(5f,0f,0f), 1f).SetEase(Ease.Linear);
}
Easeには様々な種類があります。
実際使う際はいろいろなパラメータを変えながら、作りたい動きを探すのがいいでしょう。
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
//(5,0,0)の位置に4秒で2回ジャンプして移動する
this.transform.DOJump(new Vector3(5f, 0f, 0f), jumpPower: 3f, numJumps: 2, duration: 4f);}
jumpPower
の値を調整するとジャンプの高さが変わります
回転 DORotate
//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
}
Fast
とFastBeyond360
の比較すると図のようになります。
どちらもYが0度から350度へ回しています
this.transform.DORotate(Vector3.up * 350f, 1f, mode: this.rotateMode)
WorldAxisAdd
とLocalAxisAdd
は現在の角度から何度回転させるか
という操作になります
回転させるという意味ではこちらの方が直感的かもしれません
色を変える DOColor
[SerializeField]
Renderer rendererComponent;
void Start()
{
//1秒で赤色に変える
this.rendererComponent.material.DOColor(Color.red, 1f);
}
MaterialだけでなくImageにも使うことができます
[SerializeField]
UnityEngine.UI.Image image;
void Start()
{
//1秒で赤色に変化し元の色に戻るのをずっと繰り返す
this.image.DOColor(Color.red, 1f).SetLoops(-1,LoopType.Yoyo);
}
アルファを変化させる DOFade
[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
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);
});
実行開始時のコールバック 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という機能を使うと連続的な挙動を簡単に実装できます
//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を使うと移動しながらスケールを変えるといった動作が可能になります
//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の途中で待機を入れることができます
//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には数多くの拡張が存在します。
ぜひいろんな使い方を探ってみてください