どうもいらにかです。
Mesもβテスト準備のためにバグ洗い出しなど色々進めています。
そんな最中でした。
https://twitter.com/alumiloid/status/1642074031330557952?s=20
すごく面白いアイデアです。
いずれは、ティラノスクリプトへの変換など構想はありましたが、Mesで分岐管理がいい感じにできたら面白そうです。
元ネタのアイデアではMermaidという作図記述言語をMesの派生として生み出すみたいな発想ですが、すごく着眼点がいいです。
というのもMesの思想として、記述言語が目的にそぐわない場合は「内部のデータ構造だけ再利用して、記述言語を再設計する」ことを初めから意識しています。
(Unix哲学的に言うと、捨てる準備をしておけみたいな)
ただ、色々と考えていくうちに「Mesの構造をぶっ壊さずにいい感じに実装できるんじゃないか?」という気がしてきました。
Mesの設計思想として、「脚本(書きやすさ)と台本(読みやすさ)の分離」があるため、脚本記述には複雑性を持ち込ませずに台本側でビジュアライズ(要は図の生成)できたら理想形だと思います。
Mesには非推奨ですが「拡張フィールド」というデコレーターが存在しています。
このデコレーターは「好きなデータを突っ込んでいい」という無秩序があるため、すごく便利なんですが同時にバグの危険性と仕様の複雑化が懸念される諸刃の剣です。
まぁ、今回はこいつを使えば既存の仕様を破壊せずに分岐管理を実装できるかも知れません。
知れませんでは確証が持てないので、とりあえず実装してみます。
セクションという持て余していた機能
分岐管理において必要なのは、「AからBへいく」という情報です。
例えばAというシーンにおいて、分岐からBとCに行くとき、
A-->B
A-->C
のような情報があれば表現できます。
ただ、これだともう少しリッチな表現ができるよう「どういう場合にAからBへいく」みたいな情報も持たせたいですよね。
Mermaidの場合はこんな感じの記述になります。
A-->|Bを選ぶ|B
A-->|Cを選ぶ|C
一般的なゲームエンジンやスクリプトでは、ラベル(場所)とgoto(jump)で記述されます。
ラベルも拡張フィールドで定義してもいいかもしれませんが、Mesにはセクション(==)というシーンの区切りみたいな機能があります。
今回はセクション名をラベルの代わり使うことにします。
(要は上記のABCの代わりにセクション名で関係を定義します)
セクションを明記するとこんな感じ。
== Aルート
ここはAルートです
== Bルート
ここはBルートです。
== Cルート
ここはCルートです。
これに、選択肢の内容とジャンプ先の情報があれば作れるはずです。
ちなみに、選択肢が無く、矯正ジャンプの場合もあるので選択肢の内容は記述しない場合もあり得ますね。
というわけで、30分くらい考えてみました。
?の中に設定値としてchoose:<value>,
とjump:<セクション名>,
を設定する感じにしました。
(試験的な実装なので、カンマは末尾でも必ず必須にしています)
とりあえず、サンプルテキストも書いてみました。
== 共通ルート
ここは共通ルートだぜ。
どこにジャンプする?
? choose:Aを選ぶ, jump:ルートA,
? choose:Bを選ぶ, jump:ルートB,
? choose:Cを選ぶ, jump:ルートC,
== ルートA
ここはルートAです
?jump:共通エンド,
== ルートB
ここはルートBです。
ルートBはCとAルートの選択があります。
? choose:Aを選ぶ, jump:ルートA,
? choose:Cを選ぶ, jump:ルートC,
== ルートC
ここはルートCです。
? jump:共通エンド,
== 共通エンド
ここは共通エンドです。
で、これをMermaidで分岐を作図するとこんな感じになるはずです。
graph TD;
共通ルート-->|Aを選ぶ|ルートA;
共通ルート-->|Bを選ぶ|ルートB;
共通ルート-->|Cを選ぶ|ルートC;
ルートA-->共通エンド;
ルートB-->|Aを選ぶ|ルートA;
ルートB-->|Cを選ぶ|ルートC;
ルートC-->共通エンド;
※共通ルートとルートAのように語順の統一がされてないのはMermaidで使える文字をテストするためなのであまり気にしないでください。
Mesで解析
→拡張フィールドを解析してグラフ用のデータ構造に変換
→それをMermaid形式に書き起こす(PlantUMLとか他の記述言語でもいい)
こんな感じの処理を実装すれば実現できそうです。
試験実装をsmweに公開
というわけで、MesのテキストからMermaidテキストに変換する機能を実装してsmweに試験実装しておきました。
https://smwe.iranika.info/
一番下のサンプルテキストから「分岐図のサンプル」を選ぶと上記のサンプルテキストに切り替わり、詳細>デバッグ情報>分岐グラフ用のMermaid からMermaidに変換されたテストが見れます。
あとはMermaidの作図ライブラリとか入れれば、smweのアプリ内でもビジュアライズ(可視化)できますね。
ただ重くなりそうなので、グラフ表示の実装はスキップします(いつかやるかも)。
ちなみに、生成されたMermaidテキストをmmdcコマンドでpngに出力したのがこれです。
Mesでの記述仕様や実装したコードはまだまだ改良の余地があるので、これから検証と検討を重ねて合理化を詰めていきます。
とりあえず、Mesを使う人に「こういうことも出来そうだよ」と提示できたのは良かったなと思います。
Mesのポテンシャルはまだまだあるので、これからも伸ばしていきたいですね。
余談:Mermaidは使えない文字がある
MermaidはGithubのMarkdownに組み込まれたりして活用場面が広くなりましたが、個人的にはPlantUMLで事足りているので、話題になった時に少し触ったくらいの知識でした。
ただ、今回の実装にあたって一番ハマったのが直接使えない文字があることです。
【Markdown】Mermaid.jsで使えない?文字
これらの文字は文字参照するしかないみたいで、結構手間がかかります。
文字参照
今回の実装では、なにも手を加えていないので使えない文字はそのまま出力されます。
(それをMermaidにくわせてもエラーになるだけ)
PlantUMLはダブルクオートで囲ってしまえばどうにかなるので、このあたりの文化の違いに一番時間を溶かしました。
余談:ソースコード
ここにあります。
https://github.com/tokakuya/Mes/blob/main/Mes.Extension/GameBookExtention.cs
勢いで書いたので汚いですが、今回書いたソースコードです。
命名が微妙だったり、一部は汚いです。
どうせ後からリファクタするのでいいんですが。
Mesテキストをパースすると、Mes.core.Mesという構造化されたデータになります。
そのMes.core.Mesの拡張メソッドとして、Mermaidへの変換処理が実装されています。
なのでmes.ExportMermaid()のように呼び出せて便利です。
別の記述言語へのExport実装も簡単にできると思います。