UnityでCSVを活用したメッセージウィンドウの作り方

ゲーム開発では、プレイヤーに重要な情報を伝えるためにメッセージウィンドウを頻繁に使います。
しかし、ゲーム内で表示するメッセージが増えてくると、それらを一つ一つ管理していくのは非常に大変な作業になります。
メッセージの内容を変更したい時や、新しいメッセージを追加したい時に、一つ一つのテキストを直接編集するのは時間がかかり、効率も良くありません。

こうした問題を解決する一つの方法が、CSVファイルを利用したメッセージ管理です。
CSVファイルを使えば、メッセージの編集や追加が簡単になり、開発がスムーズに進むようになります。

そこで今回は、UnityでCSVファイルを使ってメッセージを管理し、それをメッセージウィンドウで動的に表示する方法について詳しく解説します。
この方法を使えば、ゲーム内でのメッセージ管理がぐっと楽になります

またメッセージに限らずCSVはキャラクターのステータス管理などにも使えたりしますので、覚えておいて損はないと思います。

Unityスキルアップ、
始めるなら今

パズル、脱出、RPG...目標のゲームを完成させよう!

人気のUnity講座はこちら

Udemy講座

CSVについて

そもそもCSVについて知らない人のために簡単にどんなものか触れておきます。
CSVファイルとは、「comma separated values」の略称です。
値や項目をカンマ(,)で区切って書いたテキストファイル・データのこと指します。
具体的には以下のような内容のものになります。

key,message
welcome,ようこそゲームの世界へ!
quest_start,クエストを始めますか?
quest_complete,おめでとう!クエスト完了です!
thank_you,ご協力ありがとうございました。
goodbye,また会いましょう!

一行目は各列がそれぞれ何を示しているかを表すヘッダーと呼ばれるものです。
この場合は1行目にkeyが、2行目には実際に表示されるmessageが書かれています。
こういうデータをCSVといいます。
このフォーマットはExcelなどのスプレッドシートアプリケーションで開くことができ、データの編集や確認が簡単です。
ゲーム開発だけでなく、様々なデータ管理タスクにCSVが活用されています。

では続いて実際にUnityプロジェクトを立ち上げてCSVデータを表示させてみましょう。

メッセージ表示の準備

まず必要なものを色々と準備していきましょう今回必要なUIは以下のとおりです。

  • メッセージを表示するメッセージウィンドウ
  • メッセージ表示のトリガーとなるボタン

これだけになります。
では配置していきましょう。

UIの配置

まずメッセージウィンドウの背景になる画像を用意しておきましょう。
これは別の記事で紹介した9-sliceという方法で画像を分割しバックグラウンドとなるBGImageを配置しておきます。

BGImageを画面内に配置したらその子要素にTextを配置します。
続いてメッセージを表示させるためのボタンを配置しましょう。
今回は5種類のメッセージを表示させたいので5個のボタンを配置しておきます。
実際の画面はこんな感じになりました。
メッセージ表示用UI準備

続いてCSVファイルを配置したり、C#スクリプトを作成したりしていきます。

CSVファイルを扱う

早速CSVファイルをプロジェクトにインポートしましょう。

Resourcesフォルダ

ProjectウィンドウのAssets内にResourcesというフォルダを作ります。
Resourcesフォルダは、ゲーム実行時に動的にアクセス可能なアセットを格納する特別なフォルダです。
このフォルダ内に置かれたアセットは、スクリプトからResources.Load()メソッドを使って、ファイル名のみでロードできます。
Resourcesフォルダを使用することで、事前にシーンに配置しなくても、必要に応じてアセットを読み込み、使用することが可能になります。
但し注意も必要で、同じ名前で異なる形式のファイルが存在するとバグになる可能性があります。
必ずユニーク名前になるようにしておきましょう。
それでは今回は先程のCSVファイルを入れておきます。
sample.csvとしておきましょうか。

CSVファイルインポート

続いてCSVデータを読み込んだり、キーによってその内容を返す、MessageManagerというクラスを作っていきます。

CSV読込スクリプト作成

まず、Unityエディタ内のProjectビューでScriptsフォルダを作成しましょう。
これは、プロジェクト内のスクリプトを整理するためのものです。
次に、このScriptsフォルダ内で新しいC#スクリプトを作成し、そのスクリプトにMessageManagerという名前を付けます。
MessageManagerスクリプト作成

MessageManagerクラスは、直接ゲームオブジェクトにアタッチするタイプのクラスではありません。
そのため、このクラスはMonoBehaviourを継承する必要はありません
MessageManagerは単なるC#クラスとして定義しますが、UnityEngineの機能にアクセスしたい場合(例えばDebug.Logを使いたい場合)もあると思います。
その場合はクラスの先頭でUnityEngineネームスペースをusingディレクティブを使って明示的にインクルードしておきましょう。
初期のコードは以下の通りです。

using System.Collections.Generic;
using UnityEngine;

public class MessageManager{

}

続いてMessageManagerに実装するフィールドやメソッドを作っていきます。
まずCSVデータを読み込まないと何も始まりません。
LoadMessageというメソッドを作り、そこで実装していきます。
読み込んだデータをDictionaryで保持しておきたいのでそのフィールドも準備しておきましょう。

LoadMessageメソッド

具体的には以下のようになりました。

using System.Collections.Generic;
using UnityEngine;

public class MessageManager{
    public Dictionary<string, string> messages = new Dictionary<string, string>();
    
    public void LoadMessage(string fileName)
    {
        TextAsset textAsset = Resources.Load<TextAsset>(fileName);
        if(textAsset == null)
        {
            Debug.LogError("Faild to load the file");
            return;
        }

        string[] lines = textAsset.text.Split('\n');
        foreach(string line in lines)
        {
            if (!string.IsNullOrWhiteSpace(line))
            {
                string[] parts = line.Split(',');
                if(parts.Length == 2)
                {
                    messages[parts[0].Trim()] = parts[1].Trim();
                }
            }
        }
    }
}

処理の流れですが以下の通りになります。

・Resources.LoadメソッドでCSVファイルを読み込む。
※fileNameはResourcesフォルダ内のファイル名(拡張子無し)を指定
・TextAsset型の変数textAssetに代入する。
・textAssetのnullチェックを行う。
・textAssetを1行ずつに分割しstring型配列linesに格納する。
・linesを1行ずつlineに格納しforeachループで回す。
・IsNullOrWhiteSpaceメソッドでlineの中身をnullまたは空文字列かチェックする。
・lineをカンマ(,)で分割しstring型配列partsに格納する。
・partsの長さが2ならば辞書型messagesにキーと値を格納する。
・この時、キーと値はTrim関数で不要な余白を取り除く。

簡単ですがこんな感じの流れになっています。

続いてmessagesに格納した文字を取得するメソッドを作っていきましょう。

GetMessageメソッド

このメソッドはキーを受け取り、それに該当するmessages内の値を返すシンプルなメソッドです。

using System.Collections.Generic;
using UnityEngine;

public class MessageManager{
    /*略*/
    
    public string GetMessage(string key)
    {
        if (messages.ContainsKey(key))
        {
            return messages[key];
        }
        Debug.LogWarning($"Message key not found : {key}");
        return string.Empty;
    }
}

このように作ってみました。
messages.ContainsKey(key)でmessages内にキーが存在しているかをチェックし、存在している場合は値を返しています。
もしkeyが無ければ警告をログで表示し空の文字列を返しています。
nullではなく空の文字列を返している理由は、このメソッドの呼び出し元が常に文字列を受け取ることを想定しているためです。

CSVのヘッダー行をスキップ

これでOKと思いましたが、CSVデータは一行目がヘッダーの行なのでそれをスキップさせる仕組みをLoadMessageメソッドに入れましょう。
以下のように修正してみます。

using System.Collections.Generic;
using UnityEngine;

public class MessageManager{
    /*略*/
    
  public void LoadMessage(string fileName)
    {
        TextAsset textAsset = Resources.Load(fileName);
        if(textAsset == null)
        {
            Debug.LogError("Gaild to load the file");
            return;
        }

        string[] lines = textAsset.text.Split('\n');
        //追加 
        bool isHeaderLine = true;
        
        foreach(string line in lines)
        {
            //追加 
            if (isHeaderLine)
            {
                isHeaderLine = false;
                continue;
            }

            if (!string.IsNullOrWhiteSpace(line))
            {
                string[] parts = line.Split(',');
                if(parts.Length == 2)
                {
                    messages[parts[0].Trim()] = parts[1].Trim();
                }
            }
        }
    }
}

このような感じでbool値を使って一行目をスキップさせます。

次にこのクラスを呼び出しUIに表示する処理を書いていきましょう。

UIにデータを表示させる

ではUIにメッセージを表示させてみます。
MassageButtonというC#スクリプトを作成し、配置しているButtonにアタッチしておきましょう。

CSV読込のテスト

まずCSVデータが正常に読み込めるかテストしたいのでButtonをクリックしたらwelcomeがキーとなっているメッセージを表示させてみましょう。
コードは以下の通りです。

MessageManager Startメソッド

using UnityEngine;
using UnityEngine.UI;

public class MessageButton : MonoBehaviour
{
    public Text messageText;
    Button messageButton;
    MessageManager message;

    void Start()
    {
        messageButton = GetComponent<Button>();
        message = new MessageManager();
        message.LoadMessage("sample");
        messageButton.onClick.AddListener(()=> { messageText.text = message.GetMessage("welcome"); });
    }
}

流れは以下の通りです。

・必要なフィールドを準備する
・MessageTextをInspectorからアタッチする。
・自身のButtonコンポーネントを取得する。
・MessageManagerのインスタンスを生成する。
・sampleCSVを読み込む
・messageButtonがクリックされた処理を記述する。
※今回は固定でwelcomeがキーとなっているメッセージを表示させる。

では実際に起動し実験してみましょう。
ボタンのテスト

このようにボタンをクリックすると「ようこそゲームの世界へ!」と表示されました。

ボタン毎に表示メッセージを変える

今はすべてのボタンが同じkeyを呼び出していますので、これをそれぞれ変更してあげましょう。
MessageButtonにpublicでstrign keyを追加してあげます。
そしてMessageManagerのGetMessageメソッドにそのkeyを引数にして渡してあげます。

using UnityEngine;
using UnityEngine.UI;

public class MessageButton : MonoBehaviour
{
    public string key; //追加
    public Text messageText;
    Button messageButton;
    MessageManager message;

    void Start()
    {
        messageButton = GetComponent<Button>();
        message = new MessageManager();
        message.LoadMessage("sample");
        messageButton.onClick.AddListener(()=> { messageText.text = message.GetMessage(key); });
        
    }
}

このようにフィールドを増やし、それぞれのButtonのInspectorでkeyを設定してあげます。
ボタンにkeyを設定

それぞれにキーを設定したら再度テストしてみます。
ボタンにキーを振り分けてテスト

ボタン毎に表示されるメッセージが変わりました。
CSVからデータを取得しkeyとvalueのペアを作成してUIに反映させることが出来ました。
今回は非常にシンプルな使い方でしたが、冒頭でも触れたようにCSVは様々な使い道があると思いますので色々試してみてください。

また今回のCSVメッセージのプロジェクトはGithubに公開しておきます。
よかったら参考にしてみてください。
Githu CSV_Message_Sample

関連記事

最後までご覧頂いてありがとうございました。

セール中!
2dパズル

作って覚える7つの2Dパズル

段階的にUnityスキルをレベルアップ