【Unity2D】メッセージウィンドウの部品を作成してみた

Unity

Unity2Dでゲームを作成している者です。

ゲーム中に会話イベントを起こしたり、何らかの警告メッセージを表示するためのメッセージウィンドウ部品を作ってみました。

Unity2Dであれば、結構汎用的に使えるかなと思ったので、作成したコードについて紹介をさせていただこうと思います。

今回作成したメッセージウィンドウの仕様は以下になります。

  • メッセージウィンドウが表示されているとき、ゲームは止める(キャラ操作などできなくする)
  • クリックでメッセージを進められるようにする
  • 自動でメッセージを進める機能も盛り込む
  • メッセージの合間にイベントを挟み込めるようにする(BGMを変更するなど)

メッセージが出ている間はゲームを停止させる仕様があるので、ポーズボタンとかにも活用できるかなと思います。

作成したコードなどを記載していきます。

メッセージウィンドウのUI部品を作成する

メッセージウィンドウが出ている間はゲームを止めるという仕様について、Time.timeScale=0で物体の時間を止めることはもちろん、OnPointerClickなどのイベントも発生させないようにしたかったです。

イベントを無効化させるのが面倒だったので、最前面で画面全体を覆う透明な画像を作成し、そのraycastTargetをTrueにすることでイベントの発生を防ぐことにしました。

以下のようなイメージです。raycastTargetをTrueにすると画像が邪魔でゲーム画面が触れなくなるという感じです。

早速作成をしていきます。

透明な画像オブジェクト(最前面)を作成する

まず最前面の透明画像を作成します。今回はUIのImageで作成をしました。

UIの上にFrontというレイヤーを作成し、そこに配置しました。またWidthとHeightをカメラと同じサイズに設定し、PosZを100にしました。

ゲーム画面が見えなくならないようにColorのアルファを0に指定します。

次に、この透明の画像オブジェクトの下にメッセージウィンドウになるオブジェクトを作成します。

メッセージウィンドウを作成する

作成した透明の画像オブジェクトと同じでUIのImageで作成します。

ウィンドウメッセージなので横長の四角にします。Width300、Height100に設定しました。

またImageは作成した透明の画像オブジェクトと同じでアルファを0に設定します。

更にウィンドウの上に表示するためのテキストオブジェクトを作成します。テキストを表示するオブジェクトはUIのTextを使用することにしました。

ウィンドウの大きさより若干小さくしたいのでWidth280、Height90くらいにしておきました。

ウィンドウを操作するためのスクリプトを作成する

簡易ですがUIが完成したので、次にウィンドウを表示するスクリプトを作成していきます。

早速ですが完成した物は以下になります。

透明の画像オブジェクト用のスクリプト FrontObj.cs

using UnityEngine.UI;
using UnityEngine;
using System;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using System.Threading.Tasks;
public class FrontObj : MonoBehaviour, IPointerClickHandler
{
    public Image img;
    public WindowDlg dlg;
    private List<Action> act;
    private Boolean waitFlg;
    void Start()
    {
        img = this.GetComponent<Image>();
        img.raycastTarget = false;
        waitFlg = false;
    }

    void Update () {
    }

    public void setMessages(List<string> _messages){
        dlg.setMessages(_messages);
    }

    public void setMessages(string _message){
        List<string> _messages = new List<string>();
        _messages.Add(_message);
        dlg.setMessages(_messages);
    }

    public void clearMessage(){
        dlg.clearMessage();
        if (act.Count == 0){
            Time.timeScale = 1f;
            img.raycastTarget = false;
            dlg.GetComponent<Image>().color = new Color(0, 0, 0, 0);
        }
    }

    public void inputWait(Boolean _waitFlg){
        waitFlg = _waitFlg;
    }

    public async void showDialog(List<Action> _act){
        Time.timeScale = 0f;
        img.raycastTarget = true;
        dlg.GetComponent<Image>().color = new Color(0, 0, 0, 0.7f);
        act = _act;
        act[0].Invoke();
        act.RemoveAt(0);
    }

    public void OnPointerClick(PointerEventData data)
    {
        if (img.raycastTarget){
            if (act.Count > 0 && waitFlg)
            {
                act[0].Invoke();
                act.RemoveAt(0);
            }
            if (act.Count == 0){
                dlg.clearMessage();
            }
        }
    }
}

setMessagesというメソッドでウィンドウに表示したい文字列を指定します。指定すると後述するメッセージウィンドウ用のWindowDlgクラスに値を渡して、テキストに文字を表示させます。

clearMessageというメソッドはメッセージウィンドウを強制的に削除します。やることがなくなったらclearMessageを呼び出せるようにしています。

inputWait関数でユーザの入力待ちをするかどうかを切り替えられるようにしています。inputWaitがFalseだとメッセージが自動で消えます。

メッセージウィンドウ用のスクリプト WindowDlg.cs

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

public class WindowDlg : MonoBehaviour
{
    private List<string> messages = new List<string>();
    public Text Dlgtext;
    private const int MAX_LINE = 3;
    private List<string> dispMassage = new List<string>();

    public void setMessages(List<string> _messages)
    {
        messages.AddRange(_messages);
        while (messages.Count > 0)
        {
            addMessage();
        }
    }

    private void addMessage()
    {
        if (dispMassage.Count == MAX_LINE)
        {
            dispMassage.RemoveAt(0);
            dispMassage.Add(messages[0]);
        }
        else
        {
            dispMassage.Add(messages[0]);
        }
        Dlgtext.text = string.Join("\n", dispMassage.ToArray());
        messages.RemoveAt(0);
    }

    public void clearMessage()
    {
        messages = new List<string>();
        dispMassage = new List<string>();
        Dlgtext.text = "";
    }
}

これは前述したFrontObj.csから値を受け取ってテキストに表示するだけのクラスです。

オブジェクトにスクリプトを登録する

スクリプトをドラッグ&ドロップしてオブジェクトに添付します。

FrontObj.csは透明なオブジェクトに付与

WindowDlg.csはメッセージウィンドウに付与

FrontObj.csのpublicなオブジェクトは以下のように指定しておきます。

Img:自分(透明なオブジェクト)の画像

Dlg:メッセージウィンドウのオブジェクト

WindowDlg.csのpublicなオブジェクトは以下のように指定しておきます。

Dlgtext:テキストを表示するオブジェクト

動かし方

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using System.Threading.Tasks;

public class GameMaster : MonoBehaviour
{
        public FrontObj frontObj;

        void Start(){
                List<Action> actList = new List<Action>();
                Action act = async () => {
                        frontObj.inputWait(false);
                        frontObj.setMessages("テスト用のメッセージだぞ");
                        await Task.Delay(1000);
                        frontObj.setMessages("テスト用のメッセージだぞ2");
                        await Task.Delay(1000);
                        frontObj.clearMessage();
                };
                actList.Add(act);
                frontObj.showDialog(actList);
        }
}

動かすためのテストコードは上記になります。

FrontObjには、前述した透明なオブジェクトを設定します。

以下のようにメッセージが表示されて自動で消えるようになります。その間ゲームオブジェクトの時間は止めています。

テキストの色やウィンドウの色をしっかり調整すればゲーム内で普通に使えそうです。

今回のブログ記事用に作成したプロジェクトを丸ごといかにアップロードしてみました。実際に動かしてみたい人はどうぞ。Unityバージョン2020.3.12f1を使用しています。

https://unitygame.slavesystems.com/game/メッセージサンプル.zip

コメント

  1. 匿名 より:

    応援してます

タイトルとURLをコピーしました