【Unity】テキストファイルの読み取りと表示

はじめに


UnityでADVなんかを作るときに、キャラクターに言葉をしゃべらせると思います。しかしすべてのセリフをプログラムに直接書くとなると、それはもう莫大なものになってしまいます。そうなると、セリフだけを記したテキストファイルを直接読み込みたくなることでしょう…。
そこで、今回はテキストファイルから文字を読み込むプログラムを紹介します。


使用した環境とバージョン


今回はUnity ver.2021,3.4f1を使用しています。
プロジェクトは2Dのコアを使用しています。
また、プログラムを書く時にはVisual Studio 2022を使用しました。

達成目標


①テキストファイルを読み込んで、画面に表示する。
②文章をカンマ( , )で区切って、Enterを押すと区切った内容が順番に表示されるようにする。

準備


1.テキストを画面に配置


UIからText-TextMeshProを選択
画面いっぱいに広げます
表示位置を真ん中にしたら完了

2.フォントを用意


  (英数字のみでの支障がない場合は飛ばしても良い)

好きなフォントをダウンロードします。
(今回はZEN丸ゴシック Regularをお借りしました)
ダウンロードしたファイルを解凍し、ttf(またはotf)ファイルをUnityのAsset内に入れます。
(この時、名前が英数字以外のものが含まれていたら、消す英数字に変更しておきましょう。)
Unityを開いて、WindowからTextMeshPro→Font Asset Creatorを選択
Character SetをCustom Charactersに変更し、
一番上のSource Font Fileに先ほどダウンロードしてきたフォントのファイルを入れます。
Custom Character Listに表示する文字を すべて 書きます。
何を使うかわからん場合は、下記のリンクから日本語データをお借りすればいいと思います。
Generate Font Atlasをクリックしてしばらく待つと、
Unity用に変換されたものをダウンロードできるようになります。
SaveかSave as…のボタンをクリックするとダウンロードできます。
1で作成したテキストのFontAsset欄に入れれば使えるようになります。

準備終了です。

手順


①プログラムの作成

まず、読み取り機構を作ります。

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using System.IO;
using System.Text;
using UnityEngine.SceneManagement;
using Unity.VisualScripting;

public class TextController : MonoBehaviour
{

    int textNum = 0, count = 0;

    [SerializeField] TextMeshProUGUI Text;
    [SerializeField] TextAsset TextFile; 
    
    List<string[]> TextData = new List<string[]>();

    void Start()
    {
        StringReader reader = new StringReader(TextFile.text);

        while (reader.Peek() != -1) 
        {
            string line = reader.ReadLine();
            TextData.Add(line.Split(','));
        }

    }

    void Update()
    {

    }
}

< 簡単な解説 >
まずはStart外の説明です。

int textNum = 0, count = 0;

textNumは行、countは列(項目)を表しています。プログラムでの一番最初は0なので、一行目の番号も一項目目の番号も0になります。

[SerializeField] TextMeshProUGUI Text;
[SerializeField] TextAsset TextFile; 

SerializeFieldを使って、UIの方のテキスト(上) と テキストファイル(下) を受け取っています。

List<string[]> TextData = new List<string[]>();

文字列型の配列を作り、そこに文章の中身を入れます。

次にStart内の説明です。

StringReader reader = new StringReader(TextFile.text);

ファイルのデータをStringReanderにすることで、ReadLine() で一行ずつ読み込むことができます。(using System.IO;の記載が必要になります)

while (reader.Peek() != -1) 
{
     string line = reader.ReadLine();
     TextData.Add(line.Split(','));
}

StringReanderとして読み込んだファイルデータを、末尾に達するまで行を読み取ります。
文字列型の変数「line」に一行を代入。
Addを使ってTextDataにSplitの中の文字(カンマ( , ))で区切った文章を入れていきます。


ここまで出来ましたら、Updateにもプログラムを書いていきましょう。

void Update()
{
    string Times = TextData[textNum][count].ToString();

    if (Times != "ENDTEXT")
    {
        if (Times != "END")
        {
            if (Input.GetKeyDown(KeyCode.Return))
            {
                count++;
            }

            Text.text = Times; //Textに入れます。
        }
        else
        {
            if (Input.GetKeyDown(KeyCode.Return))
            {
                count = 0; //列(項目)をリセットする
                textNum++; //行を下(次)にする
            }
        }
    }
}

< 簡単な解説 >

string Times = TextData[textNum][count].ToString();

文字列型のTimesにTextDatas[行][列(項目)]を指定して文字列形式に変換して代入しています。

if (Times != "ENDTEXT")
    {
        if (Times != "END")
        {

今回の文書は、文章の終わりを表す文字として「END」、
文書自体の終わりを表す文字として「ENDTEXT」を使用しています。
(変更可能です。その場合はのちに作成する文書も対応させてください)

上のifは代入された文字列が「ENDTEXT」(文書の終わり)かどうかの判定を行っています。
下は代入された文字列が「END」(行の終わり)かどうかの判定を行っていま
す。

if (Input.GetKeyDown(KeyCode.Return))

Enterキーが押されたかの判定です。

count++;

代入された文字列が「END」ではない場合は、次に読み込むときにのために列(項目)を指す値を隣(次)にします。

 Text.text = Times;

代入された文字列が「ENDTEXT」ではない場合は、Timesに代入された文字列をUIのテキストに代入して表示します。

else
{
     if (Input.GetKeyDown(KeyCode.Return))
     {
           count = 0;
           textNum++;
     }
}

代入された文字列がENDだった場合は、Enterキーが押されたときに
列(項目)をリセットし、行を指す値を下(次)にします。


Trimを使用したり、「END」や「ENDTEXT」の部分を変更したりコマンド化するなど、使用する人が使いやすいように変更することもできます。

これでプログラムは完成です。

②テキストファイルを作成

Assetフォルダ以降の好きなフォルダ内で右クリックし、
新規作成→テキストドキュメントを選択します。

適当に名前を付け、カンマ( , )を文の区切りとして内容を書きます。

名前はtest.txtにしました

同じものを張っておくので使いたい方はどうぞ

文の最後には「END」、テキスト自体の終わりには「ENDTEXT」と書くことで、存在しないものを読み込んだりエラーになることを防ぎます。

前述した通り、「END」と「ENDTEXT」は変更可能です。

③配置

Unityに戻り、さきほど書いたプログラムをアタッチするために新しいオブジェクトを作成します。

新しく作成したオブジェクトに適当な名前を付け、プロジェクトをアタッチします。

下のTextとText FIleの欄にUIのテキストとテキストファイルを配置します。

これで完成になります。

結果


実行してみるとこんな感じです。

Enterキーを押すたびに表示されている文章が変わりますね。


今回作ったプログラムはこんな感じです。

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using System.IO;
using System.Text;
using UnityEngine.SceneManagement;
using Unity.VisualScripting;

public class TextController : MonoBehaviour
{

    int textNum = 0, count = 0;

    [SerializeField] TextMeshProUGUI Text;
    [SerializeField] TextAsset TextFile;

    List<string[]> TextData = new List<string[]>();

    void Start()
    {
        StringReader reader = new StringReader(TextFile.text);

        while (reader.Peek() != -1)
        {
            string line = reader.ReadLine();
            TextData.Add(line.Split(','));
        }

    }

    void Update()
    {
        string Times = TextData[textNum][count].ToString();

        if (Times != "ENDTEXT")
        {
            if (Times != "END")
            {
                if (Input.GetKeyDown(KeyCode.Return))
                {
                    count++;
                }

                Text.text = Times; //Textに入れます。
            }
            else
            {
                if (Input.GetKeyDown(KeyCode.Return))
                {
                    count = 0; //列(項目)をリセットする
                    textNum++; //行を下(次)にする
                }
            }
        }
    }

}

まとめ


今回は「テキストファイルの読み取りと表示ができる機構」をUnityで作りました。
プログラム自体はぱっと見 長ったらしいですが、やってることは「テキストファイルを一行ずつ読み込み、リストに入れる」「好きな区切り文字(今回はカンマ)で分ける」「分けたものを別のリストに入れる」「入れた文字を表示する」だけなんですね。

今回はセリフを前提として作成しましたが、読み込みが有効なのは何もセリフだけではありません。
例えば、キャラクターのステータスだったり、同じ場所でもフラグによって内容が変わる描写などにも使えそうですね。

外部から取り込めれば変更も簡単ですし、制作がだいぶ楽になると思います。良ければやってみてくださいね。

参考


この記事が気に入ったらサポートをしてみませんか?