sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

Unity ダイアログをポップアップするときのエフェクト(拡大・透過)

Unity キャンバス内に Scroll View を使ったダイアログをポップアップ」の記事の続きです。

前回の記事で、ポップアップするダイアログをシンプルに作成しました。なので、応用としてエフェクトを加えてみます。エフェクトがなくても、おもしろいゲームはたくさんあるかと思います。エフェクトの要/不要の調整は難しいと思いますが、とりあえずやってみよう。

f:id:shikaku_sh:20200909112924g:plain
エフェクトを追加

こんな感じになりました。0.n 秒で終わるエフェクトです。パッと表示して、操作の邪魔をしないこと、背景つきのポップアップでしたが、画面遷移も伝わりやすくなるかと思います。

レトロなゲームであるほど、エフェクトはピンポイントに利用されました。また、繰り返し操作する Windows のフォルダー表示の UI 演出なんかを見ると、とてもさりげないエフェクトですが、小気味いい効果音のように機能していると思います。


拡大(スケール)エフェクトの追加

Unity のエフェクトは、よく UniRx が利用されていると思いましたが、とりあえず利用しませんでした。コードが UniRx 独自のものになってしまうので、これくらい基本的な内容なら普通に書くほうが(間口が広くて)いいかな、と思いました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class ScaleEffect : MonoBehaviour
{
    private GameObject _Target = null;
    private float _Current = 0.0F;
    private float _Progress = 0.0F;

    [SerializeField]
    public bool _IsEnabled = true;

    [Header("スケールの線形")]
    [SerializeField]
    private Vector3 _From = Vector3.one;

    [SerializeField]
    private Vector3 _To = Vector3.one;

    [Header("実行時間")]
    [SerializeField]
    private float _Duration = 1.0F;

    [SerializeField]
    private AnimationCurve _Curve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(1, 1));

    [Header("イベント")]
    public UnityEvent Begin;

    public UnityEvent End;

    void Start()
    {
        // _IsEnabled = true;

        _Target = gameObject;
        _Current = 0.0F;
        _Progress = 0.0F;

        Begin?.Invoke();
    }

    void Update()
    {
        if (!_IsEnabled) return; // ガード節

        if (_Progress <= 1.00F) // 進行度の割合
        {
            _Current += Time.deltaTime;
            _Progress = _Current / _Duration;

            if (_Progress >= 1.00F)
            {
                _Progress = 1.00F;
                _IsEnabled = false;

                End?.Invoke();
            }
        }

        var curvePoint = _Curve.Evaluate(_Progress);

        _Target.transform.localScale = Vector3.Lerp(_From, _To, curvePoint);
    }
}

こんな感じになったので、これをポップアップさせる Prefab にコンポーネントとして追加します。

f:id:shikaku_sh:20200909113030p:plain:w600
詳細


透過度エフェクトの追加

コンポーネント単位で透過度を操作するには Canvas Group を作って、要素をまとめる必要があります。Canvas Group は、子要素の再描画の基本単位にもなるので、ポップアップの要素をまとめるのは、理にかなっているかと。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class FadeEffect : MonoBehaviour
{
    private GameObject _Target = null;
    private CanvasGroup _CanvasGroup = null;

    private float _Current = 0.0F;
    private float _Progress = 0.0F;

    [SerializeField]
    public bool _IsEnabled = true;

    [Header("透過度の線形")]
    [SerializeField, Range(0, 1)]
    private float _From = 0;

    [SerializeField, Range(0, 1)]
    private float _To = 1;

    [Header("実行時間")]
    [SerializeField]
    private float _Duration = 1.0F;

    [SerializeField]
    private AnimationCurve _Curve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(1, 1));

    [Header("イベント")]
    public UnityEvent Begin;

    public UnityEvent End;


    void Start()
    {
        // _IsEnabled = true;

        _Target = gameObject;
        _CanvasGroup = _Target.GetComponent<CanvasGroup>();

        _Current = 0.0F;
        _Progress = 0.0F;

        Begin?.Invoke();
    }

    void Update()
    {
        if (!_IsEnabled) return; // ガード節

        if (_Progress <= 1.00F) // 進行度の割合
        {
            _Current += Time.deltaTime;
            _Progress = _Current / _Duration;

            if (_Progress >= 1.00F)
            {
                _Progress = 1.00F;
                _IsEnabled = false;

                End?.Invoke();
            }
        }

        var curvePoint = _Curve.Evaluate(_Progress);

        _CanvasGroup.alpha = curvePoint;
    }
}

こんな感じで、Canvas Group のアルファ値を操作するのがポイントになります。

f:id:shikaku_sh:20200909113055p:plain:w600
詳細

実は、スケールのコードと(今のところ)ほぼ一緒です。2つのエフェクトを1つのコードにまとめると整理できるかもしれませんが、オブジェクト指向疎結合の観点からはマイナスかも。

ただ、重複するコードが余計になっていて気になるのも事実。なんらかで重複を防ぐこともできるでしょうが、リファクタリングにおける3度目の法則の範囲かと思いますのでパス。時間は有限。

「美しいコード」、「すばらしいエンジニアリングのプラクティス」といった道徳的理由だけでリファクタリングするのも考え物です。経済的に不利益となる恐れがあります。(詳細は、「既存のコードを安全に改善するリファクタリング(第二版)」)

ちなみに、今回のポップアップは最終的にこんな構成要素に。

f:id:shikaku_sh:20200909113603p:plain
コンポーネントの構成例


サンプル

GitHub に「unity-popup-practice」を公開しています。

f:id:shikaku_sh:20200908114936p:plain

参考

Effective C# 6.0/7.0

Effective C# 6.0/7.0