この記事で扱っていること
- RPGチックなメッセージ表示をする方法
を紹介しています。
注意:すべてのエラーを確認しているわけではないので、記事の内容を実装する際には自己責任でお願いします。また、エラー配線は適当な部分があるので適宜修正してください。
LabVIEWの文字列表示器は、表示器に渡した文字列すべてを一度に表示します。
そのため、RPGなどのゲームでみられるような、一文字ずつ表示されていくような見せ方にするためには、当然一文字ずつ増やした状態の文字列を繰り返し文字列表示器に渡す必要があります。
単純に表示する文字列を増やすだけの仕組みなら比較的単純なプログラムとなりますが、表示のさせ方に凝ったり、「文字列表示器をクリックしたら次のメッセージが表示される」ような仕組みを作ろうとするとイベントストラクチャを使用して少し複雑になります。
本記事では比較的簡単な実装から少し凝った作りにするパターンまで紹介しています。
どんな結果になるか
フロントパネルには文字列表示器と停止ボタンがあります。

本来は動画で紹介すればわかりやすいのですが、一応図で紹介すると以下のような感じです。
早送りボタンを押すと文字が1つずつ表示されるまでの間隔が短くなるようにしています。

プログラムの構造
文字列の中から任意の数の文字を抜き出すことができる関数を用いて、ループの中でその抜き出す文字の数を変えることでそれらしい演出を行います。
ブロックダイアグラムは以下の通りです。
Whileループに入る前にあるmessagesという文字列配列は、表示させるメッセージが複数ある場合を想定しています。
この部分、配列の要素それぞれを表示させることを決めているのであればForループでもいいじゃないかと思われるかもしれませんが、後述する「前のメッセージを表示する」ような機能を設ける場合を想定してWhileループとしています。

部分文字列の関数で、段々と「取り出す」文字列の数を増やしていくことで、表示される文字が少しずつ増えていく感じが出ます。
ローカル変数を使用しない場合
ローカル変数を使用せず、メッセージの途中から前のメッセージに戻るような作りにする場合のプログラムとして、イベントストラクチャを使用した生産者消費者のデザインで実装しました。

ブロックダイアグラムは以下の通りです。
ユーザーからのボタン操作、つまりイベントが発生した場合には生産者ループから消費者ループへ特定の指示が飛びますが、それがない場合には消費者ループ自身でエンキューをしてループが回り続けます。

なお、消費者ループである下側のWhileループにクラスタを使っていますが、ここにはこのループの様々なケースの中で使用する値をまとめています。
クラスタの中身は以下を参考にしてください。

生産者ループである上側のWhileループはイベントが起これば実行されます。
このプログラムではフロントパネル上のボタンの数も少ないのでイベント自体も多くはありません。

消費者ループは実質的にキューで状態を遷移するステートマシンのような形になっています。
それぞれのステートの中身を紹介します。
まずはInitializeステートです。
こちらは、表示するメッセージの配列と、クラスタの値の初期化を行っています。
次にDecide Display Messageステートに移行するように指示もしておきます。

Decide Display Messageステートは以下のようにしており、Messages配列の何番目の要素を表示するかを決定します。
Messages配列の要素数と、何番目の要素を表示するかの数字を比べて、次に表示するメッセージがあるかあるいはもう無いかで次に進むのがPrepare MessageかFinishかを判定します。
他にはクラスタの値の更新も行っています。
counterは、今から表示するメッセージの文字列の総数で、iterationが文字数をカウントし、その数がcounterの値を越えたらすべてのメッセージを表示したとみなします。
Wait Counterは、文字を表示してから次の文字を表示するまでの時間間隔を測るために使用する指標です。

Prepare Messageステートでは、表示する文字があるかどうかを判定します。
部分文字列の関数を使用して、表示するメッセージ文字の大きさの指定もここで行っています。
上で書いたように、counterとiterationの大小を比べて、iterationの大きさがcounterを越えるようになったらもう表示する文字はないとみなしますが、それまではDisplayとWaitを繰り返すようにしています。

Displayステートではメッセージを実際に表示していきます。
Prepare Messageで部分文字列を使って抜き出した文字だけが表示されることになります。

Waitステートでは、指定したLoop WaitとWait Counterの値があるしきい値(以下の図では100としていますがこの値は適宜調整)を越えたかどうかを判定します。
一見回りくどいことをしていて、例えば待機関数を使用して次の文字列を表示するのに待つ時間をそのまま指定してWaitのステートを一回だけ実行するようにすればいいんじゃないかと思われるかもしれませんが、それだと、その待機時間が過ぎるまでこのループは他に何もできなくなります。
例えばその待機の時間が過ぎるまでにユーザーがボタン操作をした場合、イベントストラクチャでエンキューされた要素があったとしてもこのループがそれに反応するのはその待機時間が過ぎた後になるので応答性が悪くなります。
これを防ぐため、敢えて待機時間は20ミリ秒と短くし、このWaitステートが繰り返されるようにすることで、生産者ループから何かしらの指令がエンキューされた時にも素早く反応できるようにしています。

Waitステートのケースストラクチャの中身は以下を参考にしてください。
Loop WaitとWait Counterの掛け算としきい値を比較した結果がTrueになった場合、つまり次の文字列を表示するまでの時間が過ぎた場合には、Modeの値によって次にどのステートに移るかが変化します。
もし表示する文字列(Messages配列の要素の文字)が全て表示されたら、次のMessages配列要素をまた表示させるためにPrepare Messageに戻りますが、表示する文字列が複数行に渡る場合でまだ次の行がある場合にはNew Lineに移ります。

Finish Prepare Messageステートでは、メッセージを表示し終えた後にメッセージリストの長さを見て、0であれば終了するためにFinishステートへ、0でなければ次の行へ進むものとしてNew Lineステートを実行します。

New Lineステートでは、ターゲットメッセージから改行部分を探し、そこが空なら次の表示するメッセージを決める、空でないのであれば待つためにWaitステートに進むという判断を行います。

Fast Modeステートは、ループ速度の切り替えのために、クラスタのLoop Waitの値を更新します。
下の図では100としていますが、別に値は何でも構いません。

Normal ModeステートもFast Modeと同じことをやっていますが、Fast Modeよりも値の小さい数字をLoop Waitに渡すようにします。

Message Backステートでは、前のメッセージに戻るようにしています。

最後、Finishステートでは、ユーザーイベントを発生させてイベントストラクチャを停止できるようにしています。

文字列を表示させたらクリックするまで次の表示に進まないようにする
ゲームだと、文字の送りはボタン操作をしないと次のメッセージが出ないような場合があると思います。
そんな、文字列表示器をクリックしたら次のメッセージの読み込みを行うような作りに変更する場合、自動的に次のメッセージの表示に入らないようにします。
今までは、次のメッセージに進むかどうかの判断について、New Lineステートで判断していましたが、ユーザーからの操作があるまで待つためにNext Messageステートを追加しています。
このNext Messageは、部分文字列の出力(文字列表示器)のマウスダウンイベントから実行されるステートになります。

必要な処理のためのフラグを用意するため、クラスタも更新し、finish ?というブール値を加えておきます。

これまでになかったIdleステートを追加し、Message Noと、メッセージリストの大きさが一致したら終了するようにしています。

Decide Display Messageでは、以下のように修正を行います。
修正点としては、新たに追加したfinish ?をFalseにするという処理だけになります。

Finish Prepare Messageについてもfinish ?のフラグをTrueにするという修正を行っておきます。

Next Messageステートの中身では、finish ?の値から、New Lineに進むか次に進まないかの選択を行います。

よりRPGチックにするために
より某RPGチックな見た目にする場合には、フォントの見せ方も考えなくてはいけません。
そのためには、文字列表示器だけでなく、ピクチャ表示器を使用するという選択肢もあります。
フォントの使い方や表示の例については以下の記事を参考にしてみてください。
本記事では、RPGチックなメッセージ表示の方法について紹介しました。
紹介した内容としては、どちらかというとLabVIEWの本職である、データの測定や解析などのアプリケーションで使用するというよりはゲームを作ったりするときに実装する機能になるかなと思いますが、参考になればうれしいです。
ここまで読んでいただきありがとうございました。


コメント