Unityでイージングをいじいじ with DOTween


この記事はUT-virtual Advent Calendar 2018の5日目の記事です.僕はUnityで簡単にイージングを実装できるDOTweenについて寄稿します.

イージングとはなんぞや?

そもそもイージング(Easing)とはなんなのでしょうか?イージングとは,移動等のアニメーションにおいて,その動きを加速・減速することにより,変化に緩急をつけることです.これを用いると,スタートからゴールへの移動について

  • スタートとゴールでは速度がほぼ0だが間では高速
  • ゴールで勢い余って通り過ぎ,振動してゴールに収束

などといった表現が可能になります.

例えばこちらの2つの動画を見比べてください.


前者がイージング無し,後者がイージング有りの動画です.後者の方が,前者よりもオブジェクトがぬるぬる動いているのが見て取れると思います.

イージングの具体的な様子を掴むために,ここではEasing Function早見表を引用しておきます.経過時間に対してどのくらい変位するかを表すイージング関数を定めることで,イージングは表現されます.先に述べた2つの例は,それぞれeaseInOutExpoeaseOutElasticに対応したものです.

もちろん自分でイージング関数を設計して実装するのも可能ですが,Easing Function早見表で取り上げられているイージング関数はWebデザイン等の文脈でどれも一般的なものです.この先人の知恵を有難く使うことで,手軽にクールなアニメーションを作成することが可能です.

DOTweenの使い方

DOTweenとは,イージングをUnityで簡単に実装できるようになるアセットです.AssetStoreから無料版をダウンロードできます1.以下ではDOTweenをインポート済みのUnityプロジェクト内で,どのようにDOTweenを使えばいいのかを説明していきます.

関数

DOTweenをスクリプト内で使用するには,専用の名前空間を宣言する必要があります.すなわち,スクリプトの最初で

using DG.Tweening;

と宣言する必要があります.以下のコード例ではこの宣言を省略していますが,忘れずに書きましょう.

関数を用いてアニメーションを実装するのですが,その使い方には,ラムダ式を用いる方法とそれを省略した方法の2つがあります.ラムダ式を使うならば,

DOTween.To(()=> val, x=> val = x, to, duration);

とします.ここでvalはアニメーションをつけたい値,toはアニメーションの終点の値(valの型に合わせて,intとかVector3とか…),durationはアニメーションの持続時間を表すfloatの値です.このDOTween.To()関数を使えば,移動やスケール変換など,どのような状況でもアニメーションをつけることが可能です.

一方,この書き方を省略した書き方もあります.例えばあるtransformdurationだけ時間を使って,valまでx軸方向に動かしたいときは,DOMoveX()関数を使って

transform.DOMoveX(val, duration);

としたり,スケールをdurationだけ時間を使ってvalに変えたいときは,DOScale()関数を使って

transform.DOScale(val, duration);

としたりする,といった具合です.ラムダ式で書くDOTween.To()関数と比べて一般性が失われる一方で,可読性が高いという特徴があります.

イージングの設定

次にイージングの設定方法を説明します.

transform.DOMoveX(0, 2.0f)
         .SetEase(Ease.OutQuint);

のように,関数の後ろに.SetEase()をつけることで,イージングを設定することが可能です.この引数には,Easeクラスに含まれるイージング関数の名前を指定します.Easing Function早見表から,自分の実現したいアニメーションに対応するイージング関数を探し,それを指定しましょう.

実践例

実際にゲームのどこでイージングが使えるか解説するために,今回はUnity1週間ゲームジャムに筆者が投稿したゲーム:10Countsを例として挙げます2

これは上から落ちてくる長方形を,左右に移動し避け続けて,高いレベルを目指すゲームです.このゲームの様々なところでイージングを用いています.これを1つずつ見ていきましょう.

移動

自機の正方形の移動ですが,ここにはEase.InOutElasticを指定してスチャっとした左右の移動アニメーションを実現しています.

具体的には次の通りに実装しています.

PlayerController.cs
public int field; // 自機の場所を離散的に示す,0-9の値

void Update () {
    if(Input.GetKeyDown(KeyCode.LeftArrow) && field > 0){
        field--;
        this.GetComponent<Transform>().DOMove(CalculateField(field), 0.2f)
            .SetEase(Ease.InOutElastic);
    }
    if(Input.GetKeyDown(KeyCode.RightArrow) && field < 9){
        field++;
        this.GetComponent<Transform>().DOMove(CalculateField(field), 0.2f)
            .SetEase(Ease.InOutElastic);
    }
}

Vector3 CalculateField(int num){
    // numから画面上の実際の位置を計算する関数
}

また,上から落ちてくる長方形の落ち方には,黄色にはEase.InElastic,青色にはEase.Linear,緑色にはEase.InExpoをそれぞれ適用しています.

具体的には次のようなことを記述したスクリプトを長方形Prefabにつけておき,生成しています.

YellowBulletDrop.cs
void Start () {
    this.GetComponent<Transform>().DOMoveY(-5.0f, 3.0f)
        .SetEase(Ease.InElastic);
}
BlueBulletDrop.cs
void Start () {
    this.GetComponent<Transform>().DOMoveY(-5.5f, 1.5f)
        .SetEase(Ease.Linear);
}
GreenBulletDrop.cs
void Start () {
    this.GetComponent<Transform>().DOMoveY(-5.5f, 1.5f)
        .SetEase(Ease.InExpo);
}

DOMoveY()関数の,終点を決める第一引数や持続時間を決める第二引数の値は,実際に実行しながら調節しました.

DOTweenではオブジェクトの移動だけではなく,画像の色や音などについてもイージングさせることが可能です.今回は,ゲームスタートから10秒経過する度に,背景色にEase.InOutElasticを適用してレベルアップを表現しています.

CameraLightController.cs
public void Flash(){
    this.GetComponent<Camera>().DOColor(Color.white, 1.0f)
        .SetLoops(2, LoopType.Yoyo)
        .SetEase(Ease.InOutElastic);
}

特にSetLoops(2, LoopType.Yoyo)とすることで,1回背景色を白くした後,ヨーヨーのように逆再生で元の色に戻るという動きを実現しました.

Sequenceと回転・スケール変換

自機に長方形が当たったときの,回転しながら小さくなっていくアニメーションもDOTweenで作成しています.

ここではSequenceというDOTweenの機能を使いました.

PlayerController.cs
public Sequence sequence;

private void OnTriggerEnter2D(Collider2D collision){
    if (collision.gameObject.tag == "Bullet"){
        sequence = DOTween.Sequence()
                          .Append(this.GetComponent<Transform>().DOScale(0.6f, 1.0f).SetEase(Ease.InExpo))
                          .Join(this.GetComponent<Rigidbody2D>().DORotate(360.0f, 1.0f).SetEase(Ease.OutExpo));
        sequence.Play();
        audioSource.PlayOneShot(destroySound); // 衝突時のサウンド再生
        Destroy(collision.gameObject);
    }
}

Sequenceを使うと,複数のアニメーションを同時に再生したり,あるアニメーションの後に数秒待って別のアニメーションを再生したり,といったことが可能です.ここではAppend()関数で空のSequenceにスケール変換のアニメーションを追加し,その後にJoin()関数を用いて,回転のアニメーションを同時再生するようにしています.

おわりに

この記事では,DOTweenの使い方の基本中の基本を扱いました.Unityプロジェクトは筆者のGitHubリポジトリで公開しているので,是非参考にしてください.ここで紹介した他にも,ゴールの値ではなくスタートの値が指定できるようになるFrom()や,終了時のコールバックを定めるOnComplete()OnKill()など,様々な機能があります.是非公式のドキュメントを見ながら,自分で遊んでみてください.

参考文献

DOTweenの使い方については,公式サイトが充実していてわかりやすいです.特にドキュメントには使用可能な全ての関数が載っているので,これを見ながら遊ぶのがいいと思います.

イージング関数の種類は,本文中でも引用しましたがEasing Function早見表を見るのがお勧めです.各イージング関数の形だけではなく,それによってどのようなアニメーションが実現できるかまで直感的に理解できます.


  1. 無料版と有料版の大きな違いの1つは,有料版だとTextMeshProにもアニメーションを付けられることです.はあ課金しよかな… 

  2. Unity1週間ゲームジャムは,Unity製ゲーム投稿サイトunityroomにおいて不定期に開催される,共通のお題に対して約1週間でゲームを作るイベントです.今回のお題は「10」でした.是非遊んで評価お願いします!