はじめに
インディーズゲームを作っている中でNPCの会話を実装をすることがあります。はじめから外部にセリフデータを分離した実装を行っていればいいのですが、Unity内部で持ってしまうことがあります。
今回は以下のDreamIslandにて Fungusを使用してFlowchart内に入れた実装にしてしまいました。
やりたいこと
以下の画像のように FungusのFlowchart内部にもっているセリフデータをjson形式にすべて一括でexportする (会話するNPCは100体以上存在するため、一つ一つだと時間がかかってしまいます)
Demo
Inspectorの Export Dialogue To Json
のボタンを押すことで、以下の画像のように Inspector及びローカルのjsonに一覧化して見やすくしました
追加の拡張機能宣伝
以下では セリフデータを Google Sheetに保存する機能を作りました。 Unityを見ずらいjsonやInspectorではなく、使い慣れた Sheet形式で見れるのでかなり便利だと思います。 //別記事として作成中
環境
- Unity 2022.2.0f1
- Fungus v3.13.8
実装内容
一セリフデータを格納するクラスの作成
Flowchart内でセリフデータが入っているComponentは、以下画像のSayになります。
こちらのコンポーネントを見ると、以下の情報を持っていることが分かります。
- ItemId(Commandの順番、本来は非表示)
- Story Text (セリフ)
- Character(セリフを話しているcharacterコンポーネント)
上の情報に セリフの文字数と都合上 nameの二つを追加したクラスを作成します。
[Serializable] public class Dialogue { public string characterName; public string characterViewName; public string text; //セリフデータ public int commandIndex; public int dialogueCount; }
一キャラのセリフデータを格納するクラスの作成
一つのNPCに対して(FlowchartがアタッチされているNPC) 、セリフデータは以下のように複数存在しています。
そのため、複数のセリフを格納するために先ほどの Dialogue
をリストにして NPCの名前と紐づけた 一キャラのセリフデータクラスを作成します。
[Serializable] public class CardEventDialogue { public string dialogueNo; public List<Dialogue> dialogues; }
Flowchartコンポーネントから情報を取得する
クラスの作成は終わったので、一キャラのflowchart内から情報を取得していきます。
private static CardEventDialogue GetCardEventDialogues(Component flowchart) { var dialogueList = new CardEventDialogue { dialogueNo = flowchart.name, dialogues = new List<Dialogue>() }; var sayList = flowchart.GetComponents<Say>(); foreach (var say in sayList) { if (say._Character == null) continue; var dialogue = new Dialogue { characterName = say._Character.name, characterViewName = say._Character.NameText, text = say.GetStandardText(), dialogueCount = say.GetStandardText().Length, commandIndex = say.ItemId, }; dialogueList.dialogues.Add(dialogue); } dialogueList.dialogues.Sort((a, b) => (a.commandIndex - b.commandIndex)); return dialogueList; }
またセリフデータを取得する際に、セリフの順序がバラバラになると困るので ItemId
でソートを行います
dialogueList.dialogues.Sort((a, b) => (a.commandIndex - b.commandIndex));
シーン上のすべての会話NPCの取得
ボタンを押したときにシーン(ヒエラルキー上)に存在する flowchartがアタッチされている NPCを取得します。
private void GetAllDialogues() { _cardEventDialogueList.Clear(); var dialogues = FindObjectsOfType<Flowchart>(); foreach (var dialogue in dialogues) { _cardEventDialogueList.Add(GetCardEventDialogues(dialogue)); _cardEventDialogueList.Sort((a, b) => (GetCardEventInt(a.dialogueNo) - GetCardEventInt(b.dialogueNo))); } }
Jsonにクラスを保存
クラス内がリスト型のため、JsonUtility.ToJson
ではうまく変換ができないため以下のような関数を作り疑似的に保存できるように変更しています。
public static class JsonExtension { public static string ListClassToJson<T>(T listClass, string className) { var tmpClass = new TempClass<T> { ClassName = className, Data = listClass }; return JsonUtility.ToJson(tmpClass, true); } private class TempClass<T> { public string ClassName; public T Data; } }
取得してきたセリフデータをjsonに保存します。
private async UniTask ExportJson() { var json = JsonExtension.ListClassToJson(_cardEventDialogueList, nameof(_cardEventDialogueList)); Debug.Log("json: " + json); var writer = new StreamWriter(JsonSavePath, false); await writer.WriteAsync(json); await writer.FlushAsync(); writer.Close(); }