「Unity キャンバス内に Scroll View を使ったダイアログをポップアップ」の記事の続きです。
前回の記事で、ポップアップするダイアログをシンプルに作成しました。なので、応用としてエフェクトを加えてみます。エフェクトがなくても、おもしろいゲームはたくさんあるかと思います。エフェクトの要/不要の調整は難しいと思いますが、とりあえずやってみよう。
こんな感じになりました。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 にコンポーネントとして追加します。
透過度エフェクトの追加
コンポーネント単位で透過度を操作するには 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 のアルファ値を操作するのがポイントになります。
実は、スケールのコードと(今のところ)ほぼ一緒です。2つのエフェクトを1つのコードにまとめると整理できるかもしれませんが、オブジェクト指向や疎結合の観点からはマイナスかも。
ただ、重複するコードが余計になっていて気になるのも事実。なんらかで重複を防ぐこともできるでしょうが、リファクタリングにおける3度目の法則の範囲かと思いますのでパス。時間は有限。
「美しいコード」、「すばらしいエンジニアリングのプラクティス」といった道徳的理由だけでリファクタリングするのも考え物です。経済的に不利益となる恐れがあります。(詳細は、「既存のコードを安全に改善するリファクタリング(第二版)」)
ちなみに、今回のポップアップは最終的にこんな構成要素に。
サンプル
GitHub に「unity-popup-practice」を公開しています。
参考
- kido Tech Blog - uGUIのCanvas Groupを使って透過処理をしたり、操作を制限する
- imog - Unityでシンプルなポップアップを実装する
- ゲーム UI 演出 - ポップアップの見せ方_背面の種類
- ゲーム UI 演出 - UI演出はゲームの世界観を作る
- 作者:MartinFowler
- 発売日: 2019/12/06
- メディア: Kindle版
- 作者:BillWagner
- 発売日: 2018/09/05
- メディア: Kindle版