UnityNovelProj Unity で VisualNovel を作成する
https://www.youtube.com/watch?v=G2J6RNxbGkc https://github.com/ganyariya/gnovel/pull/18
url: https://www.youtube.com/watch?v=RUuM2C0euC0&list=PLGSox0FgA5B58Ki4t4VqAPDycEpmkBd0i&index=65
title: "Make a Visual Novel in Unity 2023 - Episode 19 (part2) Integrating Input Panel Into Dialogue Files"
description: "With our input panel ready, let's create the logical line system to allow our input panel to be called from our dialogue files.Other Links: - Discord Server ..."
host: www.youtube.com
favicon: https://www.youtube.com/s/desktop/0aaf30d6/img/favicon_32x32.png
image: https://i.ytimg.com/vi/RUuM2C0euC0/maxresdefault.jpg
Logical Line とは
ganyariya "Hi! What's your name?"
input "Tell your name"
ganyariya "Oh! Hello!"
LogicalLine はダイアログファイルの進行を停止させ、ユーザとインタラクションするための仕組みです。
CommandSystem が既存で存在しますが、 Logical Line はそれを利用しません。
input "Tell your name"
のように既存の 会話データ
をパターンマッチし、 input
など 特定のキーワードがあったら強制的に Logical Line として扱う という仕組みを取っています。
[wait] Input("Tell your name")
のようにコマンドで実装してもよいですが、この方のプロジェクトでは、そのままダイアログファイルに通常会話のように入力できるようにすることを基準としたようです。
Logical Line 実現の仕組み
LogicalLineManager という Pure C# を用意し、ConversationManager が LogicalLineManager を使う、という仕組みです。 Manager とついており、これまでの Novel Project だと MonoBehaviour に見えますが Pure C# です。 分かりづらいため、ここについては実装時に名前を変えて、 LogicalLineExecutor としました。
LogicalLineExecutor は ConversationManager から Dialogue Data を受け取り
- bool Matches
- Dialogue Data をパターンマッチして、Logical Line か判定する
- void Execute
- Logical Line であったらならば、該当の Logical Line として IEnumerator コルーチンを実行する という仕組みです。
IDialogueLine というインターフェースを継承したクラスを順番に Matches Check することで、条件を満たした LogicalLine を実行するようにしています。
LogicalLine として判定された場合は、セリフ表示側の処理には流しません。 二重処理になってしまうためです。
実装
LogicalLineExecutor では TryExecute で ILogicalLine
インターフェースを実装しているインスタンスリストを探索して、条件を満たしたら IEnumerator Execute
を StartCoroutine してコルーチンとして実行します。
コルーチン自体の待機はここでは行っていません。
public bool TryExecute(DialogueLineData lineData, out Coroutine coroutine)
{
foreach (var logicalLine in _logicalLines.Where(logicalLine => logicalLine.Match(lineData)))
{
coroutine = DialogueSystemController.StartCoroutine(logicalLine.Execute(lineData));
return true;
}
coroutine = null;
return false;
}
ConversationManager.RunningConversation 側でコルーチンの待機を行っています。 また、LogicalLine と判定されたらそれ以降の「通常セリフ処理」は Skip しています。
private IEnumerator RunningConversation(List<string> conversation)
{
foreach (var rawText in conversation)
{
if (string.IsNullOrWhiteSpace(rawText)) continue;
// 生 string をパースして DialogueLineData に変換する
DialogueLineData lineData = DialogueParser.Parse(rawText);
if (logicalLineExecutor.TryExecute(lineData, out var coroutine))
{
// LogicalLine であった場合はユーザとのインタラクションを実行させる
// インタラクションが終了したら次の Line 処理へ
yield return coroutine;
continue;
}
if (lineData.HasDialogue) yield return RunningSingleDialogue(lineData);
if (lineData.HasCommands) yield return RunningSingleCommands(lineData);
// Dialogue がある場合のみ待つ
if (lineData.HasDialogue)
{
// 会話がある場合は それまでに実行されていたコマンドをすべて停止させる
// (会話がある場合を `ある区切り点(静止点) にしたいため`)
CommandManager.instance.StopAllCommandProcesses();
yield return WaitForUserAdvance();
}