🚿
ScenarioFlow覚え書き
初めに
自前で会話ダイアログを実装したい!という動機でネットを漁っていたところ、ScenarioFlowというライブラリを発見!
基本的な機能が実装されているかつカスタマイズ性が高いということで、これをベースに会話イベントを作れると開発効率が上がりそう。というモチベーションからGithubのREADMEを読み込んだので、理解した内容を覚え書きとして記載します。
ScenarioFlowとは
ScenarioFlowは、ゲームやインタラクティブなアプリケーションのシナリオ管理を容易にするためのフレームワークです。このフレームワークは、シナリオの記述、実行、制御を簡単に行うためのツールと機能を提供します。
ScenarioFlowが持つ機能
シナリオスクリプトの管理
- シナリオスクリプトは、ストーリーやイベントの進行を記述するためのスクリプトです。
- これらのスクリプトをパースし、実行する機能をScenarioFlowは提供しています。
タスク実行の制御
- シナリオスクリプト内に記載したひとつのイベント単位を[タスク]と呼びます。
- ScenarioFlowはタスクの順序やタイミングを制御することができます。
コマンドとデコーダの登録
- シナリオ内で使用するカスタムコマンドやデコーダを登録することができます。
- 特定のアクションやデータの変換をシナリオ内で実装できます。
スキップモードとキャンセル機能
- ユーザーがシナリオの特定部分をスキップ/キャンセルする機能を提供します。
非同期処理のサポート
- 非同期的なシナリオ進行(タイプライター風のテキスト演出など)を実装することができます
- ScenarioFlowはUniTaskを使って非同期処理を実装しています。
ScenarioFlowのコンポーネント
ScenarioScript
- シナリオの内容を記述するスクリプトファイルをScenarioScriptと呼びます。
- 独自のフォーマットが実装されていて、視覚的にわかりやすいテキストになっています。
ScenarioBookPublisher
- シナリオスクリプトをScenarioBookに変換するためのクラスです。
- コマンドやデコーダを登録して利用します。
ScenarioBook
- ScenarioScriptを元にして生成されたデータ構造です。
-
ScenarioBookPublisher
によって生成され、ScenarioBookReader
が読み込みます。
ScenarioBookReader
-
ScenarioBook
を読み込み、会話処理をアウトプットするクラスです。 -
ScenarioTaskExecutor
を呼び出してシナリオを実行します。
ScenarioTaskExecutor
- シナリオに書かれたタスクを管理するクラスです。
-
ScenarioBookReader
から参照され、以下を管理/実行します。- 次のイベントへの通知
- キャンセル/スキップ処理
- 各タスクの順序制御
- タスクの実行
- 非同期タスクの制御
各コンポーネントの関係性
-
ScenarioScript
を参照し、シナリオを読み込みます。 -
ScenarioTaskExecutor
で通知/キャンセルのインターフェイスを設定します。 -
ScenarioBookPublisher
から登録されたコマンドやデコーダを読み込みます。 -
ScenarioBookPublisher
がScenarioScript
をScenarioBook
に変換します。 -
ScenarioBookReader
がScenarioBook
を読み込んで会話を実行します。 -
ScenarioBookReader
はScenarioTaskExecutor
で定義した内容を元に、会話中の通知/キャンセル処理を実行します。
各クラスで定義される機能
ScenarioTaskExecutor
- INextNotifier
- ユーザ入力や他イベントによってシナリオを進めるための定義です。
- ICancellationNotifier
- タスクのキャンセル機能を定義します。
- シナリオ実行中にタスクをキャンセルするための機能です。
- ScenarioTaskExecutor
- 上記で定義したインターフェイスを引数にして初期化します。
- 後続でインターフェイス化(
IScenarioTaskExecutor
)した上でscenarioBookReader
に渡します。
ex.) ScenarioManager.cs からの呼び出し
// ScenarioTaskExecutorの設定
EnterKeyNotifier enterKeyNotifier = new EnterKeyNotifier();
INextNotifier nextNotifier = enterKeyNotifier;
ICancellationNotifier cancellationNotifier = enterKeyNotifier;
ScenarioTaskExecutor scenarioTaskExecutor = new ScenarioTaskExecutor(nextNotifier, cancellationNotifier);
- ISkipActivator
- シナリオ自体のスキップ機能を定義するために使われます。
- ScenarioManager.cs内で条件を定義してUniTaskのキャンセル機能(GetCancellationTokenOnDestroy)を呼び出します。
ex.) ScenarioManager.cs からの呼び出し
// シナリオタスクのキャンセル処理の設定
IDisposable disposable = scenarioTaskExecutor;
disposable.AddTo(this.GetCancellationTokenOnDestroy());
// スキップモードの管理
ISkipActivator skipActivator = scenarioTaskExecutor;
skipActivator.Duration = 0.05f;
UniTaskAsyncEnumerable.EveryUpdate()
.Select(_ => Input.GetKeyDown(KeyCode.S))
.Where(x => x)
.ForEachAsync(_ =>
{
skipActivator.IsActive = !skipActivator.IsActive;
Debug.Log($"Skip Mode: {skipActivator.IsActive}");
}, cancellationToken: this.GetCancellationTokenOnDestroy()).Forget();
ScenarioBookPublisher
- ILabelOpener
- シナリオスクリプト内の特定のラベルを処理するためのインターフェースです。
- シナリオ中で定義されたラベルに対応する処理を行うために使用されます。
- ICancellationTokenDecoder
- シナリオの実行中にキャンセルに関する情報を解析するためのインターフェースです。
-
ScenarioScript
中に含まれたキャンセル処理を解読してキャンセル処理を実行します。
- IReflectable
- 定義したコマンドやデコーダを纏めたインターフェイスです。
-
ScenarioBookPublisher
を初期化する際に引数として渡します。
- IScenarioBookPublisher
- 上記で初期化した
ScenarioBookPublisher
をインターフェイス化した上でScenarioScript
をScenarioBook
に変換します。
- 上記で初期化した
// ScenarioBookPublisherの設定
ILabelOpener labelOpener = scenarioBookReader;
ICancellationTokenDecoder cancellationTokenDecoder = scenarioTaskExecutor;
ScenarioBookPublisher scenarioBookPublisher = new ScenarioBookPublisher(
new IReflectable[]
{
new BranchMaker(labelOpener),
new CancellationTokenDecoder(cancellationTokenDecoder),
new ColorDecoder(),
new ConsoleDialogueWriter(),
new DelayMaker(),
new PrimitiveDecoder(),
new MessageLogger(),
new BoolDecoder(),
});
// ScenarioBookの生成
IScenarioBookPublisher scenarioBookPublisherInterface = scenarioBookPublisher;
IScenarioScript scenarioScriptInterface = scenarioScript;
ScenarioBook scenarioBook = scenarioBookPublisherInterface.Publish(scenarioScriptInterface);
ScenarioBookReader
- IScenarioTaskExecutor
- 上記で定義したScenarioTaskExecutorをインターフェイス化し、引数として渡します。
- IScenarioBookReader
- 初期化した
scenarioBookReader
をインターフェイス化します。 - ReadAsyncコマンドで引数に渡した
ScenarioBook
を実行します
- 初期化した
ex.) ScenarioManager.cs からの呼び出し
// ScenarioBookReaderにIScenarioTaskExecutorを設定
IScenarioTaskExecutor scenarioTaskExecutorInterface = scenarioTaskExecutor;
ScenarioBookReader scenarioBookReader = new ScenarioBookReader(scenarioTaskExecutorInterface);
// ScenarioBookReaderによるscenarioBookの読み込みと実行
IScenarioBookReader scenarioBookReaderInterface = scenarioBookReader;
scenarioBookReaderInterface.ReadAsync(scenarioBook, this.GetCancellationTokenOnDestroy()).Forget();
サンプルを使わずに実装するには?
- INextNotifier,ICancellationNotifierを継承したクラスの定義
- scenarioTaskExecutorに対してキャンセル条件(IDisposable)を実装
- IReflectableに含めるコマンド、デコーダーの実装
- スキップモード(GetCancellationTokenOnDestroyを呼び出す処理)の実装
- [各クラスで定義される機能]に従って各クラスを呼び出し、ScenarioScriptを実行する
(でよさそう?実装してみて気づきがあれば都度修正していきます)
Discussion