見出し画像

コルーチンやUniTaskを使ったゲージアニメーションについて【Advent Calendar 12/7】

はじめに

この記事は Colorful Palette アドベントカレンダー 12/7 の記事です。
はじめまして! 株式会社Colorful Paletteでクライアントエンジニアをやっています。 「ばーやん」といいます。

こういう記事を書くのは初めてなので暖かく見守っていただけると嬉しいです…

普段はアウトゲームの実装を行っていて、UI実装やUIアニメーションの組み込みなどを担当しています。
そんな業務の中でよく使う技術を紹介していきたいと思います。

使用バージョン

Unity : 2020.3.32f1
UniTask : v1
DOTween: v1.1.575

コルーチンを使ったアニメーション処理

UIアニメーションを行うときによく使う処理としてCoroutine(コルーチン)を使用します。 コルーチンって何?って方に説明をするため、Unityさんのドキュメントを見に行くと…

コルーチンとは、実行を停止して Unity へ制御を戻し、その次のフレームで停止したところから続行することができる関数です。

と書かれています。

つまりコルーチンを使えば特定の状態で中断と再開を繰り返すことができるので、一定時間に経験値などのバーを少しずつ上昇させる演出を実装することに適しているのです!

というわけで、実際に一定時間でゲージが最大になるプログラムを書いてみました。

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

/// 経験値バーなどのゲージ
public class Gauge : MonoBehaviour
{
		// 今回はUnityのSliderを使います。
    [SerializeField]
    private Slider slider = default;
    
    // アニメーション経過時間
    float elapsedTime = 0f;
    
    // アニメーション全体の時間
    float duration = 3;
    
    // 開始時の量
    float from = 0;
    
    // 終了時の量
    float to = 1;
    
    /// <summary>
    /// 開始
    /// </summary>
    private void Start()
    {
        StartCoroutine(PlayGaugeAnimation());
    }

    /// <summary>
    /// ゲージのアニメーションを行う
    /// </summary>
    public IEnumerator PlayGaugeAnimation()
    {
        // 少し待機する
        yield return new WaitForSeconds(0.5f);

        // 指定された時間分ゲージが増える演出を行う
        while(elapsedTime < duration)
        {
            yield return null;
            
            elapsedTime += Time.deltaTime;
            float normalize = elapsedTime / duration;
            
            // 3秒間かけてtoの値まで更新する
            float value = Mathf.Lerp(from, to, normalize);
            SetupValue(value);
        }
    }

    /// <summary>
    /// ゲージの現在の量を設定(0 ~ 1)
    /// </summary>
    public void SetupValue(float value)
    {
        slider.value = value;
    }
}

少しだけ説明をしていくと

繰り返し処理を行いたい関数の戻り値にIEnumeratorを指定してあげます。 その関数内で yield return new WaitForSeconds や yield return null を記入してあげるとその位置で処理が一時的に中断され、一定時間後に処理を再開することができます。

yield return null は1フレームだけ中断することが出来る処理です。

なので、

//指定された時間分ゲージが増える演出を行う
while(elapsedTime < duration)
{
    yield return null;

    elapsedTime += Time.deltaTime;

		〜〜〜
}

と書いてあげると指定した時間が経過するまでwhile内に書かれた処理が毎フレーム行われて、少しずつゲージが増えるようになります。

実行してみた結果はこちらです。

だんだんゲージが増えてますね!
そして、PlayGaugeAnimationの中を少し変更して…

/// <summary>
///ゲージのアニメーションを行う
/// </summary>
public IEnumerator PlayGaugeAnimation()
{
		//少し待機する
		yield return new WaitForSeconds(0.5f);
		
		//指定された時間分ゲージが増える演出を行う
		while(elapsedTime < duration)
		{
        yield return null;

        elapsedTime += Time.deltaTime;
        float normalize = elapsedTime / duration;
		
				// 3秒間かけてtoの値まで更新する
				float value = Mathf.Lerp(from, to, normalize);
		    SetupValue(value);
		}
		
		//少し待機する
		yield return new WaitForSeconds(0.5f);
		
		elapsedTime = 0f;
		
		//指定された時間分ゲージが増える演出を行う
		while(elapsedTime < duration)
		{
		    yield return null;
		
		    elapsedTime += Time.deltaTime;
		    float normalize = elapsedTime / duration;
		
				// 3秒間かけてtoの値まで更新する
				float value = Mathf.Lerp(from, to, normalize);
        SetupSecondValue(value);
    }
}

2本のゲージを順番に処理してあげることもできます。

UniTaskを使ったアニメーション処理

ここまでコルーチンについて説明をしていたのですが、UniTaskとDOTweenというものを使って同じ処理を実装することができます。

(UniTaskとDOTweenについての説明は今回スキップします)

using System;
using DG.Tweening;
using UniRx.Async;
using UnityEngine;
using UnityEngine.UI;

/// 経験値バーなどのゲージ
public class Gauge: MonoBehaviour
{
    [SerializeField]
    private Slider slider = default;

    [SerializeField]
    private Slider secondSlider = default;
		
		//アニメーション全体の時間
		float duration = 3;
				
		//終了時の量
		float to = 1;
		
		/// <summary>
    /// 開始
		/// </summary>
		private voidStart()
		{
        PlayGaugeAnimation();
    }

		/// <summary>
    /// ゲージのアニメーションを行う
		/// </summary>
		private async void PlayGaugeAnimation()
    {
				//少し待機する
				await UniTask.Delay(TimeSpan.FromSeconds(0.5f));

				//指定した時間を使ってゲージの値を更新する
				DOTween.To(() => slider.value, value => slider.value = value, to, duration);

				//アニメーション時間分待機
				await UniTask.Delay(TimeSpan.FromSeconds(duration));

				//2本目のゲージの値を更新する
				DOTween.To(() => secondSlider.value, value => secondSlider.value = value, to, duration);

        await UniTask.Delay(TimeSpan.FromSeconds(duration));
    }
}

DOTweenのおかげでだいぶスッキリしましたね。

コルーチンとの違いは yield return new WaitForSeconds(0.5f) → await UniTask.Delay(TimeSpan.FromSeconds(0.5f)) がメインになります。

やっていることは同じで指定した時間、待機を行っています。

ゲージが増える処理に関してはDOTweenの機能の一つであるToを使ってカスタマイズ処理を行っています。 DOTweenは基本的にDOMoveやDORotateなどを使ってオブジェクトの更新を行います。 様々な処理が出来るので気になった方は調べてみてください。

そして、実際に増やす処理はこちらになります。 DOTween.To(() => slider.value, value => slider.value = value, to, duration);

説明を行うとduration時間分toの値になるまでslider.valueを変更する処理になります。 これだけだと待機はできないので、await UniTask.Delay(TimeSpan.FromSeconds(duration))
の記入もしてあげます。

そして、実行してみたものがこちらになります。

DOTweenのおかげで緩急のついたアニメーションになりました。 この処理に加えてイージング処理も追加できるので、後半だけ遅くするアニメーションなども作成することができます。

おわりに

今回の記事では、コルーチンやUniTaskを使った待機処理で表現するUIアニメーションの説明を行いました。

知ってる方にとっては基本的な技術だったり少し古い技術だなって感じたと思います。 UniTaskとDOTweenのアニメーション処理に関しても新しいバージョンではもっと簡単に待機処理を行えるようになっているかと思いますので興味がある方は調べてみてください!

明日は「いち」さんによる「URPのBokeh DoFを読み解く」になります!

クライアントメンバーはお昼になるとみんなでランチに行くのですが、彼の机に行くと難しい数式がメモ帳に書かれているんですよね… そんな数式を使ったDoFついての記事です! お楽しみに!