【Unity】モーダルダイアログを実装する(2019.4版)

タイトルには2019.4版と書きましたが特に新機能を使っているわけではないです。新しい環境でもできるよーって感じです。Unity は uGUI というUI作成機能があるのでかなり簡単にダイアログが実装できます。ほとんどコードも書かないで大丈夫です。そこでアプリでよくあるモーダルダイアログの Unity 2019.4 環境で実装してみたので作成手順を紹介したいと思います。

確認環境

  • Unity 2019.4.4f1
  • Windows10
  • VisualStudio2019

補足:

UnityEditor上のみで動作を確認しています。

ダイアログの仕様

仕様は概ね以下の通りです。

  • OKボタン, Cancelボタンを配置
  • ダイアログ表示中は背景が半透明にマスクされる
  • ダイアログ外をタッチするとCancelできる

見た目は大体以下のような感じです。

f:id:Takachan:20200725171900p:plain

ダイアログ外をタッチするとキャンセルできるというのがメインな感じです。

コンポーネント構成

Canvas以下にuGUIのコンポーネントを以下の通り配置することになります。これ以降で配置も含めて手順を説明しますが最終的にこのような形になります。

+ Canvas // UIのルート
  + DialogContainer   // ダイアログを配置する親要素
    + Image_Background   // 背景をマスクするImageコンポーネント
    + Image_DialogBody   // ダイアログ本体のImageコンポーネント
      + Button_OK   // OKボタン
      + Button_Cancel   // Cancelボタン

スクリプトの作成

ボタンが押されたときの動作を行うためのスクリプトが必要なので以下を定義して DialogContainer に追加します。

// OkCancelDialog.cs

using System;
using UnityEngine;

public class OkCancelDialog : MonoBehaviour
{
    public enum DialogResult
    {
        OK,
        Cancel,
    }
    
    // ダイアログが操作されたときに発生するイベント
    public Action<DialogResult> FixDialog { get; set; }
    
    // OKボタンが押されたとき
    public void OnOk()
    {
        this.FixDialog?.Invoke(DialogResult.OK);
        Destroy(this.gameObject);
    }
    
    // Cancelボタンが押されたとき
    public void OnCancel()
    {
        // イベント通知先があれば通知してダイアログを破棄してしまう
        this.FixDialog?.Invoke(DialogResult.Cancel);
        Destroy(this.gameObject);
    }
}

各コンポーネントの設定

DialogContainer の設定

DialogContainer はUI直下に配置します。作成するときにUI上で CreateEmpty を指定することで RectTransform だけを持つゲームオブジェクトとして作成します。よくImageを半透明にして配置するなどの手順がありますが半透明に上書きが発生するとパフォーマンスが悪くなるようなのでやめましょう。

f:id:Takachan:20200725175935p:plain

またこのオブジェクトの RectTransform はアンカーを全方向で Stretch に設定し画面いっぱいに引き伸ばすように設定します。

f:id:Takachan:20200725175548p:plain

画面いっぱいに広がるように余白は上下左右をゼロに設定します。

f:id:Takachan:20200725175713p:plain

そして上記で作成したスクリプトを追加しておきます。

f:id:Takachan:20200725180204p:plain

最後に EventTrigger を追加し、クリックすると先ほど作成したスクリプトの OnCancel メソッドを呼び出すように設定します。

f:id:Takachan:20200725181117p:plain

画面がタッチされたらイベントが発生するようにしたいので Event Trigger コンポーネントの「Add New Event Type」ボンタンを押して PointerClick を選択します。

f:id:Takachan:20200725181315p:plain

以下の図の赤丸の「+」ボタンを押し、クリックされたときの動作を追加します。ヒエラルキーからコンポーネントを下図のようにドラッグして配置し、右側のドロップダウンダイアログから「OkCancelDialog」>「OnCancel ()」の順で呼び出す処理を設定します。

f:id:Takachan:20200725181958p:plain

Image_Background の設定

こちらも DialogContainer の子要素に Image コンポーネントとしてオブジェクトを追加します。

f:id:Takachan:20200725180325p:plain

画像は省略しますが追加後に DialogContainer 同じようにアンカーを Stretch に設定して画面いっぱいに引き伸ばしておきます。そして色を半透明の黒に指定します。

この階層に追加することでUIの後ろの要素にダッチを通過させない、タッチするとこのゲームオブジェクトからイベントが発生するようにできます。

f:id:Takachan:20200725180432p:plain

Image_DialogBody の設定

こちらも DialogContainer の子要素に Image コンポーネントとしてオブジェクトを追加します(画像は省略)ヒエラルキー上の表示は以下のようになります。

f:id:Takachan:20200725180817p:plain

画面上の表示したい位置と大きさに配置し画像を設定します。Scene ウインドウの表示は以下のようになります。

f:id:Takachan:20200725180905p:plain

ボタンの追加

Image_DialogBody の子要素にOKボタンとCancelボタンを2つ配置します。ヒエラルキー上の配置は以下のようになります。

f:id:Takachan:20200725182106p:plain

SceneView 上での見た目はこんな感じです。

f:id:Takachan:20200725182452p:plain

そしてボタンコンポーネントにに配置されている「OnClick ()」に押したときの処理を追加します。追加手順は DialogContainer と同様です。

OKボタンの設定内容

f:id:Takachan:20200725182759p:plain

Cancelボタンの設定内容

f:id:Takachan:20200725182811p:plain

ダイアログのプレハブ化

上記の設定を全て行ったらダイアログをプレハブ化しておきます。以下のようにヒエラルキー上のコンポーネントをプロジェクトにドラッグするとヒエラルキー上のオブジェクトが青くなるのでこれでプレハブ化ができます。

f:id:Takachan:20200725183017p:plain

プレハブ化したらヒエラルキー上からオブジェクトを削除します。

動作確認

テスト用の処理を追加

まず以下のようなコンポーネントを作成します。

// OkCancelDialogTest.cs

using UnityEngine;

public class OkCancelDialogTest : MonoBehaviour
{
    // ダイアログを追加する親のCanvas
    [SerializeField] private Canvas parent = default;
    // 表示するダイアログ
    [SerializeField] private OkCancelDialog dialog = default;

    public void ShowDialog()
    {
        // 生成してCanvasの子要素に設定
        var _dialog = Instantiate(dialog);
        _dialog.transform.SetParent(parent.transform, false);
        // ボタンが押されたときのイベント処理
        _dialog.FixDialog = result => Debug.Log(result);
    }
}

次に画面上にテスト用のボタンを配置して上記スクリプトを追加します。

f:id:Takachan:20200725183939p:plain

スクリプトを追加したらプロジェクトとヒエラルキーからそれぞれ以下のようにオブジェクトをインスペクターにドラッグ&ドロップします。

f:id:Takachan:20200725183853p:plain

次にボタンが押されたときの処理を Button の OnClick() に追加します。

f:id:Takachan:20200725184201p:plain

実行してみる

これで実行すると以下のような動きになります。

f:id:Takachan:20200725184847g:plain

ボタンを押したときと画面外を押したときの動きが反映されていると思います。

少しアニメーションを追加すると以下のようになります。こっちも基本は同じです。

f:id:Takachan:20200725190404g:plain