ゲーム実行中に新しく書いたシナリオを読み込む


投稿日:2024年5月5日 | 最終更新日:2024年5月19日

概要

基本的には、宴のシナリオはUnityエディタ上で事前にインポートする必要があります。
ですが、Unityエディタ上でインポートしていないシナリオを読み込みたいことがあります。
ここではそのやり方を解説します。
この手法は例外的で実験的なもので、上級者向けの特殊なカスタム方法となります。

ゲーム中にプログラム上でシナリオを作成し読み込む

ゲーム中に、プログラム上でシナリオデータを作成しそれを読み込む方法を以下にまとめます。

RuntimeScenarioImporter

ランタイムでシナリオデータをAdvEngineにロードするには、RuntimeScenarioImporterコンポーネントを使用します。
Engineプロパティには、対象となるAdvEngineを設定してください。

サンプルプログラム

プログラムからシナリオデータを作成し、AdvEngineにロードするサンプルプログラムです。
サンプルプログラムはAssets/Utage/Sample/Scripts/SampleRuntimeScenario.csにもあります。
サンプルシーンはAssets/Utage/SampleOthers/RuntimeScenario/SampeRuntimeScenario.unityにあります。

using System.Collections.Generic;
using UnityEngine;
using Utage;

//ランタイムでプログラム上で作成したシナリオを読み込むサンプル
public class SampleRuntimeScenario : MonoBehaviour
{
    public AdvEngine engine;
    public RuntimeScenarioImporter scenarioImporter;

    public void SampleMakeScenarioAndLoad()
    {
        //シナリオ名。エクセルのシート名に相当
        const string scenarioName = "SampleRuntimeScenario";
        StringGrid scenario = MakeScenario(scenarioName);
        //StringGridDictionary scenarioをインポートして、シナリオをロード
        if (!scenarioImporter.TryImportAndLoadScenario(scenario))
        {
            Debug.LogError("Failed Import Scenario " + scenarioName, this);
            return;
        }
    }

    //「宴」形式のシナリオを作成
    StringGrid MakeScenario(string scenarioName)
    {
        StringGrid grid = new StringGrid(scenarioName, scenarioName, CsvType.Csv);
        grid.AddRow(new List<string>
        {
            //ヘッダー部分
            "Command", "Arg1", "Arg2", "Arg3", "Arg4", "Arg5", "Arg6", "WaitType", "Text", "PageCtrl", "Voice",
            "WindowType"
        });
        grid.ParseHeader();

        //キャラと台詞表示の例
        grid.AddRow(new List<string> { "", "こはく", "喜", "", "", "", "", "", "こんにちは!私Unityちゃん!", "", "", "" });

        //コマンド記述の例
        grid.AddRow(new List<string>
        {
            AdvCommandParser.IdPauseScenario, "", "", "", "", "", "", "", "", "", "", ""
        });
        return grid;
    }
}

ChatGPTを使った使用例

応用として、ChatGPTなどの生成AIなどでシナリオを作成して、それをゲーム中で実行・再生することも可能です。

実装例

ChatGPTを使うには、APIを利用できるように開発者登録したり、Unity上から利用できるように専用の通信プログラムを書く必要があります。

要点として、生成AIに宴形式のシナリオを出力させるプロンプトをうまく書くのが難しい場合
・AIが生成しやすいシナリオ形式になるようにプロンプトを指定
・生成された会話を取得
・生成された会話を解析するプログラムを書いて宴のシナリオ形式に変換
・ゲームにシナリオを読み込ませる
という多段階の流れで処理する必要がでてくるかと思います。

また、ChatGPTはAPIの利用料金が高いので、これを前提にしたゲームを作る場合はゲームプレイヤー全員が使用するChatGPTの料金が開発者の負担になるのでその点には十分に注意しましょう。

ゲーム中にシナリオをExcelやCSVファイルから読み込む

ゲーム中にシナリオをExcelやCSVファイルから読み込む方法を以下にまとめます。
この手法を使えばデバッグ用のアプリを作成して、それをシナリオプレイヤーとして利用すれば、エクセルファイルを編集するだけでシナリオの調整できるので、ライターがUnityを使わずにシナリオ編集作業ができるようになるかもしれません。
ただ、以下の制限があるため、実際にはUnity上で作業するほうがやりやすいかと思います。

  • テクスチャやサウンドなどの素材の追加はUnity上でしかできない
  • シナリオの記述エラーなどをしたときのフォローが難しい
  • ユーザーからもシナリオファイルが丸見え・編集可能になってしまう(正規リリース時にはUnityでインポートする形式に変えるなどして対応する必要がある)

RuntimeScenarioFileReader

ゲーム実行中にシナリオファイルをロードするには、RuntimeScenarioFileReaderコンポーネントを使用します。

「ScenarioFileReaderSettings」には、プロジェクト名/ScriptableObjectフォルダ以下にある、CsvFileReaderSettingsと、ExcelFileReaderSettingsを設定してください。

アプリのビルド前に

ExcelFileReaderSettingsはエクセルファイルの読み込みのために、NPOIというライブラリとそれを利用するプログラムを使っています。
これはUnityエディタ上でのみ使用するよう設定されているので、ビルド後のアプリでも動かせるようにする必要があります。
Utage/Editor/ExcelParserフォルダを、フォルダごとEditorフォルダ以下から移動して

UtageExcelParserのADFの対象プラットフォーム設定を変更してください(Editorのみにチェックされているのを対象プラットフォームに合わせてチェックを入れるか、Any Platformにチェック)

各プログラムが対象プラットフォームで動作するとは限らないので、その点は実機テストするなどして確認する必要があります。(WebGLなどは厳しいかもしれません)
NPOIはApache License2.0を使用しているので、アプリで使用する場合はそのライセンスに従う必要があります。
この変更は「宴」をアップデートすると巻き戻ってしまうので、アップデート時に上書きインポート対象にならないようにするか、再設定する必要があります。

サンプルプログラム

RuntimeScenarioFileReaderを使ってファイルを読み込み、それをRuntimeScenarioImporterを使ってAdvEigneにロードさせています。
サンプルプログラムはAssets/Utage/Sample/Scripts/SampleRuntimeScenarioFile.cs にもあります。
サンプルシーンはAssets/Utage/SampleOthers/RuntimeScenario/SampeRuntimeScenarioFile.unityにあります。
File

using UnityEngine;
using Utage;

public class SampleRuntimeScenarioFile : MonoBehaviour
{
    public AdvEngine engine;
    public RuntimeScenarioFileReader scenarioFileReader;
    public RuntimeScenarioImporter scenarioImporter;
    //シナリオディレクトリのパス。Assetsフォルダではなく、プロジェクト直下またはexeファイル置き場所からの相対パスになる
    public string directoryPath = @"DynamicScenario";

    public void OnClick()
    {
        //指定のディレクトリ以下のシナリオファイルをロードして、StringGridDictionary scenarioとして取得
        if (!scenarioFileReader.TryReadScenario(directoryPath, out StringGridDictionary scenario))
        {
            Debug.LogError("Failed Read Scenario " + directoryPath, this);
            return;
        }
        //StringGridDictionary scenarioをインポートして、シナリオをロード
        if (!scenarioImporter.TryImportAndLoadScenario(scenario))
        {
            Debug.LogError("Failed Import Scenario " + directoryPath, this);
            return;
        }
    }
}

シナリオファイルの置き場所

シナリオファイルの置き場所(directoryPathで指定する場所)は、
Unityエディタ上では、Assetsフォルダ以下ではなく、一つ上のプロジェクト直下になります。
ビルドした場合はアプリの置き場所からの相対パスになります。