見出し画像

ノベルゲームエンジン「Light․vn」なら商業級の機能はどこまで実装できる?(Ver.16.3.1現在)


【導入】商業級の機能、あるとうれしいわな

ノベルゲームエンジン「Light.vn」は、多ジャンル対応を盛んに推進している一方で、源流たるノベルジャンルに関する機能は安定している。
そうなるとね、こんなことが気になったりするんじゃないかな?

具体的にどんな機能なら実装できるの?
「『商業級の作品を作れる』って、具体的にどのくらいまでいけるの?

この記事は、そんな具体化の求道者たるあなたの知りたいことがほとんど網羅されている……ことを願うものだ。いや、願いなら叶えてやるんだ……
それじゃあ、商業作品に実装されている機能を挙げながら、その実装可否(Ver.16.3.1 での実装を想定)と、実装可能であればその方法(中級者以上を想定)、注意事項、実装の難易度などを解説してゆこう!

参考資料

  • 「エロゲ/ノベルゲUIを調査。36ブランドのシステムUI・仕様調べてみた。(2022年版) - 声オタおにじくんの声学審問H!」
    約2年前のブログ記事。個人的に開発コミュニティで紹介したくらいに参考させていただきました。

解説動画

「雪原たかし」が本記事の解説をした配信のアーカイブを公開しているよ。動画で内容を確認してみてもいいかもしれないね。


【列記】できるのかい? できひんのかい?

ここからは機能名と実装可否とを小見出しにしている。
実装可否は、下記のように表しているよ。

  • ☆:実装できる。採用実績もある。

  • ◯:実装できる。僕が試作するなどで確認済。

  • △:いちおう実装できる。あまり現実的ではない。

  • ✕:実装できないか、できたとしても実用にたえない。


1.「コンティニュー」……☆できる!!

「コンティニュー」は、次回起動時に前回の終了時点から再開できる機能。作品が強制終了してしまった場合などは無理にしても、ふつうに終了させた時にはそこから再開できるようにしておけたら理想的だよね。特に初回プレイ時には常に物語の最前線にいるから、セーブ操作をしなくてもそこから再開できたら便利なはず。

さて、実装可否の結論だけれど、これは実装できる
実装方法は「イベント『close_signal』の後処理としてコマンド『ゲームエンド』の前に設定する」などもあるけれど、おすすめは「ダイアログスクリプトでメッセージを読み取って判別させる」だね。
ダイアログスクリプトは、コマンド「ダイアログ」で指定する文字列を受け取ってそのまま表示する……というのは実のところ正確じゃない。文字列を受け取るのは正しいんだけれど、それをどう使うかはダイアログスクリプトでの処理次第なんだ。想定しているよりもダイアログスクリプトの自由度は高いって考えてもらっていいと僕は思うよ。
話を戻して、受け取った文字列は変数「vn_msg_str」に格納されるから、これをコマンド「もし」で読み取って、タイトルへ戻ったり作品を終了したりするダイアログだった場合に他と被らない番号でセーブを実行すれば、そのセーブデータを使ってコンティニュー機能を提供できる。
注意事項としては、ダイアログ省略機能(後述)を実装する際に、省略の判定前にコンティニューの判定を行うようにすることと、Ver16.3.0未満においてはコンティニュー用セーブの処理位置に注意する必要がある(ロード時に読み込みが再開されてダイアログスクリプト上の処理を続行してしまうことを想定しなければならない)ことが挙げられる。
実装の難易度は、複雑さと考慮すべき処理構造を考慮するとやや高めだといえるかな。とはいえ、プレイヤーが安心できる機能のひとつではあるから、がんばってみてほしい。

(補足)
コンティニューに類する機能として、起動時にすぐ前回の場面から再開する「サスペンド」という機能があるんだけれど、これも実装できる
実装方法は、コンティニュー機能を実装していれば、起動時にサスペンド関連の判定を行うだけでいい。ただ、サスペンド機能を提供するなら、ダイアログや機能切替などの設定項目を併せて提供したほうがいいね。さすがに避けては通れないとは思うよ。


2.「可変ウィンドウ」……☆できる!!

「可変ウィンドウ」は、ウィンドウの縁にカーソルを合わせるとカーソルが両矢印になってウィンドウの大きさを自由に変更できる機能。Light․vnの総合サンプルやテンプレートではウィンドウサイズを任意の値に変更することができるんだけれど、この機能はもっと直感的なものだね。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、作品設定(エディターのメニューバーから開くサブウィンドウで設定するもの)の中にある「ウィンドウサイズ調整」にチェックを入れる。これだけでいい。
注意事項としては、この機能がサイズを指定する方式のウィンドウサイズ調整機能とやや相性が悪いことが挙げられる。より厳密には、UIの設計としてサイズの可変性をどう扱うのかを考慮したほうがいいということだ。ボタンでいくつかのサイズを提供しているのに、現在のウィンドウサイズがそれらのいずれとも合致しないのであれば、さてどのように表示しよう? そんな観点を導入する必要があるんじゃないかと僕は思うよ。
実装の難易度は実に低いといえる。ただし、UIの機能設計という落とし穴もあるから、問題なく提供するまでのところでちょっと検討が必要にはなるね。


3.「フォント変更」……△いちおう

「フォント変更」は、プレイヤーがテキストウィンドウのテキストのフォントを選択することができる機能。フォントにこだわらない作品なら、プレイヤーの選択に委ねるというのも設計として有用だとは思うね。

さて、実装可否の結論だけれど、これはいちおう実装できる
「いちおう」としたのは、変更できるフォントの自由度が小さいからだね。
おそらく本記事の読者や作品のプレイヤーが想定しているのは「各々の環境に導入されているフォントから自由に選択できる」という形式だろうけれど、それはLight․vnでは実装できない。可能なのは、制作側で用意したフォントファイルのみなんだよ。たとえば、プレイヤーの環境になにかいい感じのフォントがあったとしても、それが作品側に収録されていなければ選択することはできない。逆に、収録さえしてしまえばプレイヤーの環境に無いフォントでも使用可能となる。
実装方法は、フォルダ「Fonts」にプレイヤーへ選択肢として提供するフォントのファイルを格納しておき、リスト等(参照用変数)を用意してプレイヤーに選んでもらうようにする、という形態が考えられる。文字装飾(「<コマンド>」と「</>」とで囲んで、テキストにルビをふったりできる特殊な記述)で一部分だけフォントを変えている場合でも、フォントファイルの指定部分に変数代入を用いるなどでフォント変更の対象にすることができる。
実装の難易度は地味に高い。というのも、フォントの選択肢をUIとして用意するところが意外と面倒だったりする。3フォント程度であれば列記するだけでいいだろうけれど、もっと多くのフォントを提供したくなったらスクロール可能なリストを用意する必要があるだろう。そのようなリストは自作する必要があり、色々と考慮すべき仕様が多くなってくる。そもそも用意するフォントを選ぶのだって簡単じゃないんだから、それなら特定のフォントのみをバシッと制作側で選択して、それだけで表示をするほうがいいんじゃないかと僕は思ったりするよ。


4.「フォント色変更」……☆できる!!

言葉のまま、フォントの色を変更できる機能。たいていの作品では文字が白または黒で、影がその逆というのが基本になっているんじゃないかな。これをたとえばちょっと青っぽくしてみたりとか、そういったお好みに合わせる機能を提供するのがこれだね。

さて、実装可否の結論だけれど、これは実装できる
そもそもコマンド「文字色」があるからスクリプトで色指定はいくらでも可能なんだけれど、この機能を実装するにあたって最も重要となるのは「色の選択をどのように提供するか」という点だね。
最も楽なのは、基本色やそれらのセットを設けてその中から選んでもらうというものだ。こういう時に提示する基本色というのもだいたい決まっているから、いくらか作りやすいと思うよ。
プレイヤーからの最も高級な要望は「スライダー系UIでRGBやHSLの値を調整できるようにしてほしい」ってところじゃないかな。でも、ペイントソフト等で色を選ぶ時に見るようなUIは、特にカラーマップみたいなものを実装しようとするとかなり面倒なことになる。
実装の難易度は中or高といったところかな。難易度に幅があること関しては上記が理由になるね。なんにせよ、最近ではアクセシビリティの観点から色覚への配慮が注目されているし、他の機能でも言えることだけれど「やろうと思えばできる」という状態になっておきたいね。


5.「既読文字色変更」……☆できる!!

「既読文字色変更」は、いちど読んだことのあるシナリオテキスト(既読テキスト)の色を変更する機能。なにもしなければ未読と既読とでフォントカラーに違いは無いんだけれど、Ver.16.3.0で追加されたコマンド「文字色既読」によって、それぞれに異なる色を設定することができるようになった。かなり基本的な機能でありながら追加されたのはつい最近ということで、ちょっと意外に感じられるかもしれないね。

さて、実装可否の結論だけれど、これは実装できる
だってまさにそのためのコマンドがあるんだからね。ただしUIは自分で作る必要があるから、まあがんばろうねって話になるかな。
実装の難易度は中or高。機能自体はコマンド一発だけれど、「フォント色変更」と同様にUIが面倒なところだね。16.3.0未満だとコマンド「文字色既読」が無かったから、テキスト表示単位の既読判定を自力で作らなければならなくて、まあ非現実的だった。それを思うと、まだ少しくらいは楽になったと言えるかな。


6.「ctrlスキップ」……△いちおう

「ctrlスキップ」は、シナリオ文章が既読かどうかにかかわらずctrlキーを押していればスキップができる機能。画面上のUIでは既読判定付きでのスキップを可能として、ctrlキーでのスキップはその判定をせずに強制スキップをするというものだね。

さて、実装可否の結論だけれど、これはいちおう実装できる
「いちおう」としたのは、既読判定を有効とした状態で強制スキップをすることが直接的にはできないからだね。テキスト進行関連のコマンドの変更があったVer.10.6.0を境に、スキップを連続で有効化することで事実上の強制スキップとする方式でのctrlスキップが実装できなくなったんだよ。今は、スキップの有効化をしても、既読判定を有効化していて未読判定をされたら、スキップが有効化されたままで進行自体は通常速度という、理解が容易ではない状態となる。
じゃあ実装方法も無いんじゃないかって話になるんだけれど、これは力技で解決できなくもない。まず、Light․vnには既読判定が有効かどうかの状態を保持するシステム変数「vn_sysTextSkipMethod」がある。これを利用して、下記のように処理を構成することで、ctrlスキップとして実装することができる。

  • コンフィグでコマンド「文字スキップ方式」を適用する際の処理に、「選択されている文字スキップ方式を記録するための変数」(以下「フラグ」)への代入を追加する。

    • この代入の際に「vn_sysTextSkipMethod」を利用することができる。

  • 「キーダウン ctrl.l」「キーダウン ctrl.r」に対して、コマンド「文字スキップ方式 0」を適用したうえで、コマンド「文字進行スキップ true」を適用するようなスクリプトを割り当てる。

    • この時点で「vn_sysTextSkipMethod = 0」となり、既読判定が一律で無効化される。

  • 「キーアップ ctrl.l」「キーアップ ctrl.r」に対して、フラグが立っている場合にのみコマンド「文字スキップ方式 1」を適用したうえで、コマンド「文字進行スキップ false」を適用するようなスクリプトを割り当てる。

    • フラグを利用して本来の既読判定に戻している。

  • フラグは起動時に初期設定されるようにしておく。コンフィグなどの各種画面に入った際に、コマンド「文字スキップ方式 (フラグに記録された状態)」を適用するようにしておく。

まあ、面倒なのはわかってもらえるかな?
というわけで、実装の難易度は中程度ってところかな。ctrlスキップとして適切に処理されるための設計は上記で済んでいると考えれば、あとはこの面倒さを理解した状態で実装をすればいいだけとも言えるだろうから、このくらいに留めてみたよ。


7.「全体ボリューム」……☆できる!!

言葉のまま、作品全体のボリューム(音量)を調整する機能。主音量とも呼ばれるかな。これとは別にBGMや効果音に対する個別音量が設けられることがほとんどで、作品によっては登場人物毎に音量調節が可能なことも……って、話が逸れたね。

さて、実装可否の結論だけれど、もちろん実装できる
そもそも公式のサンプルですでにコンフィグの項目として提供されているからね。あまり語るところも無いかな? 逆にこれが実装できないエンジンって存在しないんじゃないかとすら思っているんだけれど……
実装の難易度はもちろん低い。公式サンプルのトラックバーを用いた方式以外に、ボタンを並べて離散値での調整を提供することなどもできる。簡単に実装できるぶん、いろいろと試して遊んでみるのもいいかもしれない。


8.「セリフ時BGM音量抑制」……◯できる!

「セリフ時BGM音量抑制」は、音声再生時においてのみBGMの音量を下げたり消音したりする機能。ボイスが強い作品などでは需要が大きい機能だね。

さて、実装可否の結論だけれど、これは実装できる
実装方法というか、そもそもの方式は2つ考えられる。いずれの方式においても、音量を保つ音声個体の個体名に接頭辞などの命名規則を設けることを推奨するよ。コマンド「コマンドマクロ」による独自コマンドを作ることも追加で推奨しておくよ。

  1. BGMを定率で下げる

  2. BGMを任意の割合で下げる

1の方式は、コンフィグでは音量を抑制するかどうかだけを選択してもらえばいいし、独自コマンド内では「コマンド『音量』でBGMの音量をあらかじめ決めた割合に変更する」「コマンド『砂時計』を反復指定で設定し、音声個体が存在しなければ(LVN関数『lvExistsObj』を使用)、砂時計個体を抹消しつつ音量を元に戻す」の2つの処理を実行すればいい。
2の方式は、コンフィグでは数値の操作UIを提供する必要があって、独自コマンドは1とほとんど同じ処理を組めばいい。音量の割合のところだけ変数の参照を行うようにすれば、処理もすぐに完成するね。
注意事項としては、「背景音個体の個体名は設定しないほうがいい」が挙がる。というのも、背景音個体の個体名は、個体設定時に指定しなければ既定で「vn_sysBGM」が設定されて、基本的にはそれを対象名として音量の変更を行う。これが任意の個体名を設定してしまうと、コマンド「音量」の対象名に正規表現を使うことができない(つまり1コマンドにつき1対象しか指定できない)ことによって、いちいち背景音の個体名を引数として渡したりする必要が生じてしまう。
また、砂時計個体は適用時毎に代入変数を参照するという仕様にも注意が必要だね。このあたりの仕様はそこそこの頻度で変更されていて、処理の把握においては難解さを増大させる可能性がある。だから砂時計の適用コマンドでスクリプトを参照する場合には引数を持たせないように処理を構成することを推奨するよ。引数にしたかった変数は臨時全域変数や保存変数で代用すればいいかな。
実装の難易度は高い。音量を下げるという処理だけなら簡単でも、音声が消えたあとに音量を元に戻す処理を作り、それを他と干渉させることなく実装するのは、作品の処理構成に関する深い理解と意識を必要とするからね。

(補足1)
エッ……な作品では、他のボイスが再生されていない時にのみ再生されるボイスである「背景ボイス」というものがあったりする。たとえば、場面としてこう……キャラの感情や感覚が昂揚していて、そのキャラのボイスが常に聞こえていてほしいって場合があったりするんだ。
そして、実はこれも実装できる
実装方法は、コマンド「背景ボイス」を使うだけ。僕はまだこのコマンドを使った作品を認知できていなけれど、この個体に反復指定が存在しないことが理由かもしれないね。上記(常に聞こえている)を目的とするなら、反復指定は必要だろうからね。自動ボイス個体は反復再生される仕様となっているから、うまくつながるようにボイスファイルを調整する必要があるね。
ん? なんで取り消し線があるのかって? それは簡単な実証を怠った僕への戒めだよ……

(補足2)
砂時計の反復指定による実装は、反復の周期次第で動作に支障をきたすことが考えられるし、間違えれば無限ループを生み出す可能性があって、個人的にはちょっと避けたくなるような形式ではあるね。
実は実装方法の検証で最初はコマンド「変数式確認機」とLVN関数「lvExistsObj」とを組み合わせて使うものを試作したんだけれど、音声個体の抹消の方式が複数存在している(ボイスカットによる抹消、再生終了による抹消、抹消系コマンドによる抹消)ことによって、どうもうまく処理されないことが多くて諦めたんだよ。
僕は今のところ他の方式を思いついていないから、もしいいものがあったら教えてほしい。


9.「ボイスカット」……☆できる!!

「ボイスカット」は、新しいシナリオ文章を表示する際に再生中のボイスを中断する機能。地文と前の文章のボイスとが被ることを良しとしない人もいれば、ボイスが中断されないことを良しとする人もいるから、これはたいていコンフィグで調整対象に加えられているね。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、コマンド「ボイス再生形式」の適用。これだけだね。UIの実例は公式サンプルにあるから、どう記述すればいいかというのも困らない。
そうなると、当然に実装の難易度は低い。注意事項も特には無いから、この機能はボイスのある作品なら実装しておくべきだって断言してもいいんじゃないかな。


10.「マウスジェスチャ」……◯できる!

「マウスジェスチャ」は、マウスの右ボタンかホイールをクリックしながら動かすことで特定の操作を実行する機能。各方向に異なる操作を割り当てることができるようになっていたりして、今のマウスカーソルの位置からあまり動かさずに特定の操作ができることを評価される機能だね。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、コマンド「キーダウン」「キーアップ」にUIの設定と抹消をそれぞれ割り当てるのが基本となるかな。「キーダウン」でマウスカーソルの座標を変数「vn_mouse_x」「vn_mouse_y」から取得・記録して、「キーアップ」でこの機能のUIを抹消しながら再度マウスカーソルの座標を取得して、移動方向に応じた個別の操作へと移行する。うん、書くのは易く……だね。
実装の難易度は高い。ただし、多少簡単にできる方式もあるにはあるから、この機能に関しては補足も確認してほしい。
まず、キーの入力と解除とがどちらも抜けることなく検出される確率は、読者のみんなが想像するよりもおそらく小さいはずなんだよ。それを許容しないとものすごく仰々しい安全装置を設ける必要があって、その方向に作業を進めるほどに現実的なものじゃなくなってくる。そして、処理過程において最も難解となりそうなのが座標記録とそれを利用した方向算出だろうけれど、これには絶対に三角関数が必要になる。三角関数がわからないって人には実装できない方式ってことだから、やっぱり難易度は最高になっちゃうね。
それだけじゃなく、マウスジェスチャは「やっぱやめた」ができるようになっていないと誤操作時のストレスがとても大きい。一定以上の距離がなければキャンセルされるような処理も用意することを強く推奨するよ。

(補足1)
マウスジェスチャの実装方法として、台形のボタン素材を配置してコマンド「ボタンタッチ時」による移動検出を設ける、というものがある。
この方式は、実装の難易度がやや高いまで下がる。ただし、注意事項は下記のとおり、かなり多くなる。

  1. 斜辺を持つボタン素材は意外と難しい

  2. 設定項目「透明無視」の指定を忘れない

  3. 台形を小さくしすぎない

  4. 中央にもボタンを配置する

  5. コマンド「タッチ処理順位 1」を適用しておく

台形は斜辺を持つけれど、4方向はまだ簡単で、6・8方向は難しい。というのも、台形が斜辺を持つことで、検出領域に空白や重複を持たないUI素材を用意するのが想像以上に面倒なんだよ。45°なら外縁がわかりやすい階段状になってくれるからマシなんだけれど、6・8方向は数ピクセルくらいは重複か空白が生まれてしまったり、対称性が崩れたりしがち。空白は処理漏れを、重複や対称性の崩れはデザイン的な崩れを招くから。しっかりと注意を払ったほうがいい作業だね。
設定項目「透明無視」を忘れると、せっかく角度に注意して緻密に作り上げたUI素材がただの方形素材として扱われて無意味になってしまう。
台形を小さくしていると、UIタッチの検出漏れが発生する可能性が高まるから、多少厚めにしておこう。
中央にボタンを配置するのは、「やっぱやめた」用だね。ボタンタッチでの方向検出は変数操作が基本となるけれど、前処理としてその初期値を設定しておいて、中央のキャンセルボタンのタッチでその初期値を代入するようにしておこう。
そして、素材による領域の空白や重複に対する最後の砦として、コマンド「タッチ処理順位 1」を適用して、隣接ボタンのレイヤーが重複しないように設定しておくようにしよう。中央が最も低レイヤーで、あとは最低でも2レイヤーに分ける必要があるね。

(補足2)
似たような機能として「メニュー展開」がある。これは、右クリックやホイールクリックをした座標を基準に操作UIを展開する機能で、方向算出の処理が不要になるから、マウスジェスチャよりも実装の難易度は格段に下がる。
実装方法は、「キーダウン」で新しい画面領域上にメニューを展開するのを基本構成とし、UIの配置をできれば円形か正方形にしつつ、配置基準をキーダウン時点のマウスカーソル座標にする。まあ、公式サンプルのメニュー機能を改造するのが最も楽な方法かな。
注意事項としては、方向検出が不要であることが挙がるね。これはつまり、台形などの斜辺を持つボタンをきっちり隣接するように配置する必要もそうデザインする必要も無いってこと。あえて難しくすることもないんじゃないかって、僕は思うよ。
というわけで、難易度的にもマウスジェスチャはメニュー展開を実装できるようになってから挑戦するのをおすすめするよ。


11.「セーブショートカット」……◯できる!

「セーブショートカット」は、セーブ画面を開かなくてもメイン画面上でセーブができる機能。これにはいくつかの方式があって、ウィンドウの端部にカーソルを近づけるとセーブスロットが展開されてくるものや、UIにカーソルを当てるとセーブスロットが数個展開されてくるもの、ボタンを押せば適切なスロットに自動で記録してくれるもの、などがある。

さて、実装可否の結論だけれど、これは実装できる
実装方法は方式次第だね。セーブスロットを展開するものは、透明なボタン個体を設定して(素材はコマンド「方形画像登録」での生成を推奨)、その個体にコマンド「タッチ開始時」「タッチ終了時」を適用してセーブスロットの展開と収納を割り当てる。自動記録は、スロットの記録状態をリスト変数に記録しておいて、疑似for構文などを使って最初に空となっているスロット番号をインデックスの値として取得するなどして、該当スロットに記録させる。
注意事項としては、スロット記録状態のリスト変数は、初回の定義時に使用中・未使用スロットに適当な値(真偽値とかでいいんじゃないかな)を割り当てておいて、未使用スロット側の値を初期値として与えておくことだね。初期値を与えずにundefinedとしていると、その後のスロットの記録情報の変化に追従するのが難しくなってくる。このあたりは試してみたら実感できると思うよ。
実装の難易度はいずれの方式でもやや高い。スロット展開のほうは展開時の個体管理、セーブ処理の理解、セーブデータ変更時のスロット差し替えなど、考慮すべきものがかなり多い。その点では自動記録のほうがセーブスロットを表示する必要がなくて楽かもしれないけれど、すべてのスロットが埋まっている時に適切な処理を用意する必要があるなど、依然として懸案事項は多くて重いと思うよ。


12.「お気に入りボイス」……◯できる!

「お気に入りボイス」は、作品で再生されるボイスのうち、プレイヤーが指定したものをリストとして記録しておいて、あとから再生することができるようにする機能。直近のボイスは「ボイス再生」で再生できるけれど、特定の場面のボイスを、後になってもういちど聴きたくなることもあるかもしれない。そんな時に役立つ機能だね。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、まずボイス専用の独自コマンドを作るところから始まるね。最もやりやすいのは、素材側で連番を使った命名規則を作っておいたうえで、コマンド「ボイス」によるボイス個体の設定と、ボイス番号・キャラ名(いずれも命名規則に組み込まれているものとする)の保存変数への記録とを組み合わせることかな。この変数への記録が機能の要で、これが無いと、どのボイスをお気に入り登録するのかを特定することができない。
お気に入り登録のUIは、メイン画面上で登録できるようにしたり、専用画面を呼び出してセーブスロットと同じように登録させたり、他にも方式は考えられる。お気に入りボイスのリスト画面においてはボイスのテキストが表示されているべきだから、LVN関数「lvText」を用いてテキストウィンドウ内のテキストを変数に格納するなどして、お気に入りボイスに紐づける準備をする必要があることには留意しておこう。
注意事項としては、テキストウィンドウへ複数段階に分けてテキストを表示させると、lvTextで取得するテキスト(テキストウィンドウのテキストを先頭から取得する)とボイスのテキストとが完全一致ではなくなってしまうことが挙げられるね。これを回避する方法は、現時点では力技ばかりで処理的に不利となるものばかりだから、あえて紹介はしないでおくよ。Luaを使おうにも、Luaコマンドは実行完了を待たずにスクリプトの読み込みを進めてしまうから、この類の処理では正確さを保証できなくなってしまう。だからといってボイスにテキストが紐づいていないと、登録したボイスを判別するには再生してみるほかないってことになってしまう。ここはたくさん悩んで妥協案を探ってみてほしい。直近に文字窓個体で出力した文字列のみを取得するLVN関数とかが追加されてくれればいいんだけれどね。
実装の難易度は高い。素材の命名規則次第で難易度は下げられるけれど、テキストウィンドウに何段階もテキストを送ってなかなか刷新しないような伝統的ノベルゲーム(ボイスに向いているかはさておいて)だと、ほとんど適さない機能となってしまうことだって考えられる。声演に光を当てることのできるすばらしい機能ではあるけれど、実装にあたっては作品の基礎設計と技術が求められるね。


13.「選択肢移動」……☆できる!!

「選択肢移動」は、現在の場面の前後の選択肢まで移動できる機能。既読部分などをスキップで移動させなくても瞬時に分岐点まで移動できる。作品によっては前の選択肢には戻れないものもあるみたいだね。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、選択肢で区切られる区間毎に識別子を設けて、進行中はそれの設定をしつつ、ボタン等での移動選択時にその識別子に応じた前後箇所への移動を行えばいい。
注意事項としては、移動時にフラグや個体等の適切な再設定を行う必要があることが挙げられる。最もやりやすい対策は、選択肢移動用のセーブ番号をいくつか確保しておいて、そこに記録させておくことかな。ただし、この対策だと既読部分にしか通用しない。予め用意しておくにしても、うっかり消してしまったり、プレイヤーに消されてしまったり(フォルダ「SaveData」で直接削除ができてしまう)、確実な処理が保証されないのを許容するかどうかにかかっているね。とはいえ、他の対策としては、選択肢前のすべての個体等の状況を再現する処理を選択肢毎に用意するという、しんどいに決まっているものになる。
実装の難易度はやや高め。ルート分岐が関与する場合には、選択肢の前後関係を適切に把握する必要がある。「適切に」というのは、選択肢到達時点でありうるすべてのフラグ状態に対応しきってみせるということだから、ルート設計の担当者の堅実さ次第になるね。


14.「タッチUI」……△いちおう

「タッチUI」は、タッチスクリーンでの入力に対応したUIを提供する機能。モバイル環境は基本的にタッチスクリーンのみで、かつ画面のサイズが小さい。そうなるとUIは大きめのものを用意すべきだし、タッチスクリーン特有の操作にも対応したいところだよね。

さて、実装可否の結論だけれど、これはいちおう実装できる
「いちおう」としたのは、タッチスクリーン特有の入力には対応していないから。スワイプやピンチアウト、フリックなどを反映できない……といっても、現時点では一般ユーザーがモバイル対応できるのはブラウザ向けで、ブラウザ版ではそれらに対応させることは無いからっていう話だから、PC向けを基本とする現時点での事実上そこまで困らないんじゃないかな。
実装方法は、システム変数「vn_system_name」でモバイル環境の判定を行い、それに応じたUIセットを設定する処理を用意するのが基本だね。モバイルではちょっと大きめのUIを用意することを推奨する。単にWindows版の素材を大きくするだけじゃなくて、ちょっと正方形や円に近づけようとしてみると、タッチしやすいUIになるよ。
実装難易度はやや低い。やるのは環境変数の判定とUIセットの用意だけだからね。素材を用意できるなら、今後を見据えて準備してみてもいいかな。

(補足)
現時点で、モバイルの入力への対応はタップのみで、タップはenterと同じ扱いになっているよ。


15.「クイックジャンプ」……✕できひん

「クイックジャンプ」は、バックログから過去の場面に戻る機能。各ログ単位にジャンプ用のボタンが付いていて、それをクリックすればそのログのところまで戻ることができる。参考記事でも書かれていたけれど、最近では標準装備って感じの機能だね。

さて、実装可否の結論だけれど、これは実装できない
Light․vnのバックログは、動作の軽快さを重視していて、「テキストウィンドウのテキストの記録」「ボイス再生」の2つに特化している。そして、標準のバックログではこれ以外の機能を追加することはできない。だからクイックジャンプも実装できないということになる。
バックログを自作すること自体は不可能じゃない。「テキストウィンドウに出力したいテキストをコマンドに渡し、それをリスト変数へ格納してからテキストウィンドウに出力する」という機構を用意したうえで、「リスト変数を参照し、レコード毎にUI付きのブロックを生成して、スクロール位置に従って配置する」という機構も用意すれば、実装することはできる。
とはいえ、察してもらえるとは思うけれど、これは明らかに実現が難しい。まずリスト変数への格納時にネイティブの機構を使用できていないし、リスト変数のサイズに対する処理の重さへの対策ができない。そして、2つの機構はどちらも実装難易度がとても高い。この難しさを軽減するためにバックログ機能はエンジンに標準装備されているんだなぁって学ぶのはいいことだけれど、それで開発の苦しみと見返りの乏しさ(実装できても実用にたえない可能性が高い)がどうにかなるわけじゃないね。

(補足1)
バックログに関しては、Light․vnのDiscordコミュニティ内で試作されたことがあって、100レコード程度の実証がなされたんだけれど、結果はあまり芳しくなかった。高機能バックログは僕も自作に挑戦する予定だから、期待せずに待ってみてほしいな。

(補足2)
バックログのレコードにカーソルを当てるとその場面のスクリーンショットが表示される機能「シーンサムネイル表示」というものがある。
クイックジャンプが実装できるようになったらの話にはなるけれど、これはいちおう実装できる。ただ、現時点で考えられる実装方法はデータサイズと動作性のいずれかを犠牲にしなきゃいけなくて、実用にたえるかが微妙なところだね。シーンのスクリーンショット(セーブ時に撮影されるほう)を表示するのはけっこう厳しいけれど、妥協して場面の背景のみを表示させるなら、まだ可能性はあるかもしれないね。


16.「ウィンドウ非活性時の入力受付」……✕できひん

「ウィンドウ非活性時の入力受付」は、ウィンドウがアクティブになっていない場合でもカーソルがウィンドウ領域内にあれば入力を検出して実行する機能。ブラウザとかで、開いているウィンドウにカーソルを持っていけばアクティブウィンドウじゃなくてもスクロールができたりするあれだね。

さて、実装可否の結論だけれど、これは実装できない
Light․vnのあらゆる入力はアクティブウィンドウである場合のみ可能だから、エンジンの仕様として不可能ということだね。ちなみにイベントリスナー(ある事象の発生を検出し、それに付随して処理をする機能)として登録されているのは「ウィンドウの『閉じる』」「ロード処理」「コマンド『開く』の実行が禁止された状態での、同コマンドの実行命令」「ポップアップウィンドウが禁止された状態での、ポップアップウィンドウの生成」の4つで、「ウィンドウの活性化・非活性化」は含まれていないよ。


17.「可変UI配置」……◯できる!

「可変UI配置」は、メイン画面のUIの配置を変更できる機能。配置順を変えたり、ウィンドウの下部から上部に移動させたりといった配置変更をする機能だね。自分の好みに合わせた配置ができれば、操作性の満足度は確保しやすくなるかな。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、通常のUI配置の段階から位置情報を変数で管理しておき、プレイヤーには配置調整のUIを用意しておくことになるかな。等間隔で配置できるようなUIデザインにしておくと好都合だね。
実装の難易度は中程度。リスト変数を扱えれば、基本的な等間隔配置の実現は楽にできるし、UI全体の位置変更では基準用個体(コマンド「個体」で用意するのがおすすめ)への親子設定が使えればかなり楽になる。

(補足1)
親子設定の利用において、もしメイン画面のUIにトラックバー個体を使用している場合には注意が必要になる。というのも、親子設定において、トラックバー個体の位置関係は対象になるんだけれど、スクロール領域は置き去りになってしまうという仕様があるんだ。それによって、入力を事実上受け付けなくなる(動かなくなったり、上限と下限の2値しか取れなくなる)から、トラックバー個体は親子設定をせずに絶対座標で指定する必要がある。
まあ、メイン画面で初期配置段階からトラックバーを必要とする機能は少ないから、該当する状況も限られてくるんだけれどね。

(補足2)
可変UIには、配置だけでなく表示の可変性を操作する「UI表示固定」というものもある。画面の特定の領域にカーソルを当てるとUIが表示されるUI格納機能などが該当するんだけれど、これは実装できる
実装方法は、透明ボタン個体へのコマンド「タッチ開始時」「タッチ終了時」の適用が基本となる。タッチ開始で表示を、タッチ終了で格納を実行させればよくて、この格納を行うかを全域変数のフラグで管理すれば、機能として完成する。
実装の難易度はやや高め。UI表示も格納も他の機能との関連を考慮する必要があって、不具合なく実装するのが意外と難しい。でも、これを実装できたらUIの完成度はわかりやすく高まってくれるから、けっこうおすすめの機能だよ。


18.「スクリーンショット」……☆できる!!

言葉のまま、スクリーンショットを撮る機能……なんだけれど、ちょっとだけ形態が分かれたりする機能でもある。
というのも、表示されたままのウィンドウ内を撮影するものもあれば、UIを除いて撮影するものや、そこに作品ロゴを加えるものもあるんだよ。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、上記の形態毎に説明しようか。
まず、表示されたままのウィンドウ内を撮影するものは、コマンド「スクリーンショット」を適用するだけでいい。たいていキートリガーかボタンに割り当てるね。撮影された画像はフォルダ「Screenshots」に格納されるよ。
次に、UIを除いて撮影するものは、セーブ時のスクリーンショットと同じ……じゃないことに注意しよう。コマンド「スクリーンショット」は除外個体を指定することができないから、前処理としてUIを非表示(抹消すると面倒だと思うよ)にしてから撮影して、後処理としてUIを再表示させることで実現しよう。
最後に作品ロゴを加えるものは、前処理でのUI非表示に併せてロゴ表示を行い、後処理でロゴを抹消すればいい。
実装の難易度は中程度。ちょっと面倒なのが、UIの透明度が統一されていない場合かな。たとえばテキストウィンドウの背景は半透明でUIは不透明だとして、撮影時にテキストウィンドウも非表示にするような場合だと、再表示の際には不透明のものと半透明のものとで処理をちゃんと分けないといけない。見落とすとせっかくの半透明が無効化されてしまうことになるから注意したいところだね。


19.「シーンリプレイ」……☆できる!!

「シーンリプレイ」は、シナリオの特定の区間を再度見ることのできる機能。物語の山場だけでなく、日常パートや伏線の場面など、プレイヤーが振り返りたい部分がリプレイで用意されていると喜んでもらえる。たいていは特定の区間を踏破したらシーンリスト画面に戻るんだけれど、シーンリプレイというよりもシーンバックの性質がある作品などでは終端を設けずにそのまま進行できることもある。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、「選択肢移動」とほとんど同じ。対象シーンの開始時点の状態は、必要であれば再現しておくことになるね。
注意事項としては、一部UIを変更する必要があることが挙げられる。というのも、シーンリプレイは通常プレイと違ってセーブ・ロードを許可しちゃいけないんだよ。だからUIもキートリガーもクイックを含めて抹消しておく必要があるというわけだ。
実装の難易度はやや高め。難易度だけじゃなくて作業負荷も高めだから、あまりひとりだけで作業をしないほうがいい機能かもしれないよ。

(補足)
シーンリプレイが少しでも実装しやすくなるようにと思って僕が作ったプラグイン「SGS_reminiscence」の使用をおすすめさせてもらうよ。
形式としてはセーブデータを使用したものになっていて、シーンリストもテンプレートとして提供しているから、カスタマイズして楽をしてね。ちなみに無料プラグインだよ。


20.「シナリオ検索」……✕できひん

「シナリオ検索」は、シナリオから特定の語句を検索し、リスト化して表示する機能。該当したシナリオの場面にジャンプしたりすることもできるらしい。ちなみに、僕はどんな機能なにかを知っているだけで、実際に使ったことはないよ。

さて、実装可否の結論だけれど、これは実装できない
これは「いちおう実装できる」にしようか迷ったんだよね。というのも、LVN関数「lvSubstringCount」で文字列に特定の語句が含まれているかどうかを判別することはできて、あとは全シナリオを分割して判定にかけて、該当テキストを返すようにすれば実現はできるんだよ。
ただ、これをするためには、そもそもの「全シナリオを分割」を既存のシナリオ形式に極力干渉しないように実現することと、その分割したシナリオを網羅的に管理しておく機構の作成、これら両方をかなりの無理をして実行しなきゃいけない。そのうえ、全シナリオの検索にかかる時間を抑えるのはまったくもって簡単じゃない。ここまでになると、ちょっと現実的だとは言えないって、僕は判断したんだよ。

(補足)
Light․vnでは、シナリオをスクリプトに直接記述する方式と、CSVファイルからカラムを指定して出力する方式とでシナリオの実装を選択できる。そして、僕はCSV形式の採用を推奨するよ。
CSVファイル方式は多言語対応を主目的としているけれど、スクリプトにシナリオを記述しなくなったり、シナリオの各文章を番号で管理できたり、スクリプトに関する利点がいくつかある。ただ、シナリオの修正をすぐに反映するには未使用素材解放やタブ変更などをしなきゃいけなくて、直接記述方式よりもかなり面倒だっていう弱点もある。シナリオがほぼ完成してからCSV形式でシナリオを実装すれば、ある程度はそれも克服できるけれどね。


21.「ボイス準拠演出」……☆できる!!

「ボイス準拠演出」は、1レコードぶんのシナリオ文章にボイスが充てられている場合において、ボイスの途中で立ち絵の差分変更や演出操作を実行する機能。文章中で表情が変わってほしかったり立ち絵を動かして身振りを表現したかったりすることってあると思うんだけれど、それを実現するものだよ。これができれば、立ち絵での表現がとても豊かになるね。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、コマンド「音源確認機」で時間を指定して差分変更等のコマンドを実行するのが基本になるね。
注意事項としては、ボイスカットが発生した場合などに、文章の末尾において想定したものとは異なる差分や座標のままになってしまうのを回避する必要があることが挙げられる。これはボイスカットとの兼ね合いもあるから、最終差分と座標を保存させておいて、適切な時機に更新をかける機構を慎重に構築することになるね。
実装の難易度は高い。処理の中断や変更が発生しうる処理というのはどうしても難しくなるものだからね。とはいえ、立ち絵表現をシナリオに沿わせることの強みはとても魅力的なものだし、僕としてはぜひ挑戦してほしい機能のひとつだよ。

(補足1)
ボイス準拠演出は「動的演出」のひとつだけれど、動的演出の最たるものとして挙げられるのは「Live2D」などの2Dモーフィング系の機能だろうね。
Light․vnはまだ2Dモーフィング系のツールとの連携をしていないから、これは実装できない。よく問い合わせがあるから要望としては把握されているし、水面下で頑張ってはいるから、気長に待っていてほしいな。

(補足2)
ボイス準拠演出は、僕の作品『春に生きれば』に実装されているよ。立ち絵の表情やポーズが細かく変化するとかなり効果的だってことを体感してみないかい? (露骨な宣伝行為)


22.「フローチャート」……◯できる!

「フローチャート」は、「シナリオの進行状況と分岐を可視化する機能」「いちど通過した選択肢にあった他の選択肢を選んでルートを解放する機能」「選択肢の前後から、フローチャートで選択したルートを通った場合のフラグを設定したうえで開始する機能」で構成されている。機能説明は複雑になったけれど、それは開発・実装が複雑ということでもあり、提供すればプレイヤーが大いに喜んでくれる機能ということでもある。参考記事によると、ゆずソフトにしかない機能らしいよ。

さて、実装可否の結論だけれど、これは実装できる
実装方法は、シナリオ構造と分岐を可視化するUIを構築するところから始まる。その際に選択肢のフラグも把握するだろうから、選択肢に応じてそのフラグを保存変数に代入してゆけば、フローチャートからの開始時点で必要となるフラグの設定は遂行できる。あとは「シーンリプレイ」と同じ要領で状況再現の処理を用意すれば、満足度が絶大な機能を実装できてしまう。
実装の難易度は、もう言うまでもなく高い。本記事で紹介した実装可能な機能の中でも最高に難しい。実装方法は削ぎ落として説明したけれど、1文節に込められた作業と構成は……もしなにがなんでも実装してやると思っているわけじゃないなら深追いしないほうがいい。
それでも、処理的に無理をしているわけじゃなくて制作者に無理を強いているだけ(だけ?)だから、「いちおう」とは言わないよ。

(補足)
僕はフローチャートが必要になるような作品を作っていないから、まだ実例を見せることはできない。そして、これは実例のためだけに開発するにはあまりに複雑な機能だということを理解しておいてもらえると、まあちょっとうれしいかな。もし作ってほしいという声があれば、ユキハラ創作企画が動き出す……かもしれない。


【結論】“ほぼ”実装できて、今でもじゅうぶん

まあ、ちょっとした識者なら「クイックジャンプが実装できなくて『じゅうぶん』とは何事か」とおっしゃるんだろうけれどね。まあ、現状の話をしているとはいえ、そんなことを叫んでいたら、いつの間にか実装できるようになるかもしれないよ? Light․vnはそのくらい急に進化するエンジンだ。7年このエンジンを見てきて日本代表にまでなった僕は、その進化の過程を知っている。光は、速いんだよ。
とはいえ、今回紹介しなかったけれど実装できる機能(「マスク」「多言語切替」「画像ループ」「アニメーション」)はまだあるし、本記事の機能を網羅すれば、機能だけでいえば商業級だと言えるんじゃないかと僕は思うんだよ。
もちろん、作品として「商業級」となるためには、各素材や演出などの質を追求する必要があるし、ちゃんと宣伝して認知を獲得することだって大切。完成を優先するとしても、それらを妥協しすぎないことが求められる。
そんな困難を克服して生まれる作品だからこそ、商業級の作品は強い光となってみんなに届く。商業作品がそうなるために費やしたもの、追求したものを、きみも体験してみないかい?
そうすると決めたなら、さあ、すぐに試作をしてみよう!

ここまでのお相手はLight․vn日本代表の「雪原たかし」が務めました。
ほしい機能の実装に困った時には、ぜひ僕の屋号「ユキハラ創作企画」にご相談くださいませ。

より良い作品にしようと思い立ったきみに、素晴らしい創作生活を。

この記事が気に入ったらサポートをしてみませんか?