■ 新・ゲーム開発講座 |
■ へっぽこプログラミング入門♪ |
■第10夜:スクリプトコマンドの基礎「遅延」@ 前回までの内容で、ノベルらしいテキストの表示の基礎は出来ました。しかし現在の状態では、読み込んだテキストをただ垂れ流し的に表示しているだけで、そのままゲームに応用するには無理があります。テキスト表示には適当なところで一時停止して欲しいですし、場合によっては選択肢を選べるようにもしたいものです。 このような文章本体以外の制御用の命令は、スクリプトコマンドとして解釈 →
実行できるようにしておくと便利です。たとえばテキストを次のように記述しておきます。
このようなテキストを読み込んで逐次文字を表示していき、識別子 # を見つけたところで一時停止(カーソルをブリンクさせてマウスクリックを待つ)をさせるような処理が出来れば良い訳ですね。今回は、このようなコマンドの解釈 → 実行部の雛形を作ります。 ■基本は「遅延」コマンド とはいえ、いきなりカーソルブリンクをさせるのは少々面倒な部分もありますので、もっと基本的なコマンドから実装していきましょう。最も基本的な(簡単という意味ではない)コマンド・・・それは、「遅延」です。テキストを表示していく途中で、ある一定時間だけ表示を停止させるものです。仕様としては次のようなもので良いでしょう。
たったこれだけですが、これ以降整備していく各種コマンドの基本要素はすべて入っています。その基本要素とは・・・「飲む」「打つ」「買う」・・・ではなくて(爆死)、
の3要素です。なんのこっちゃ、とお思いの方もいることと思いますので、もういちどスケルトンの動作を思い出してみましょう〜♪ |
|
図を見ていただければ思い出して頂けると思いますが、キー入力などのイベントが無い限りプログラムの実行はひたすら Mainloop() を駆け抜ける構造になっています。遅延処理といえどもこのプログラム構造から逃れることは出来ません。「とにかく止まればいいんだろう」的に Sleep(1000) などと安直な記述をすると、キースキャンも含めてプログラム全体の動作が止まってしまい、じつにみっともない格好になります。なんとか工夫をして、タッチ&ゴー式に Mainloop() を駆け抜け続けるような記述にする必要があります。つまり、一見単純そうにみえる遅延処理も、処理開始(撃つ)、処理本体ループ(回す)、処理終了(締める)の要素に分解して時間スライス的にインプリメントする必要がある訳です。 ■Mainloopの処理 そのために、フラグを一つ用意します。そして、以下のような組み方をします。 |
int flag_delay; //遅延処理用のフラグ void Mainloop(void)
} |
実際にゲームとして成立するくらいの数のコマンドをインプリメントした場合はもう少し気の効いた記述にした方が良いのですが、まずは Mainloop の中でフラグによって処理を振り分ける構造を理解して頂きたいと思います。実はこの部分が、コマンド処理の「回す」部分にあたります(実際の処理部についてはもう少し先で解説します)。 ■フラグを操作するには? さて、Mainloop でフラグを参照しながらコマンド実行部を呼ぶことは分かりました。ではフラグそのものはどこで操作するのでしょう。 ・・・もちろん、テキスト解釈のどこかで操作する訳です。ここでは、テキスト表示をする部分を拡張して、コマンド解釈部を作ってみましょう。こんなソースになります。 第7夜で決めた仕様で、コマンドは識別文字として頭に #を付けることにしてありました。「#」は1バイト文字ですので、1バイト文字の表示部分で識別して処理を振り分けるのが妥当でしょう。そんな訳で、TextEngine() の中身を以下のように変更します。Check_control_chr() というのが今回新しく付け加えた関数で、テキストポインタの指している1バイト文字が「#」ならばコマンド解析を行って true を返し、「#」意外なら false を返しています。ここから、「撃つ」処理に向かって枝葉を伸ばしていきましょう。 |
//------------------------------------- // ここがエンジン部本体ね♪ //------------------------------------- int Text_engine () {
} |
■制御文字の解析 さてそこで飛んできた Check_control_chr() ですが、制御文字を switch 文で振り分け、「#」 を見つけた場合にはさらにコマンド処理部を呼ぶようにします。また、ここでは同時に TAB、LF、CR について「無視」をするようにしておきましょう。こうすることで、テキスト中の TAB や改行(LF+CR)が無効になり、いわゆるフリーフォーマット記述が出来るようになります。つまりスクリプトをソースレベルで見やすいように TAB やスペースでインデントをつけても、画面表示には影響しなくなる訳です。ただしそのかわり、明示的に改行表示をしたい場合は別途「改行コマンド」を作成しなければなりません。改行については別途後述します。 |
int Check_control_chr()
{
} ※*TEXT はグローバル変数として存在するポインタです。読み込んだテキストを表示しながら順次インクリメントされています。 |
■コマンドの解析 さて、テキスト中で1バイト文字を見つた → それが「#」であることを確認した ・・・までは出来ましたので、次はコマンド名の識別部を作りましょう。識別といっても「へっぽこ」式に単純にします。「#」以降のコマンド名を切り出して strcmp() で比較するという原始的なもので、当たりが出たら該当するコマンド処理関数を呼んでいます。 なんだよ〜、strcmp() でいちいち評価するとコマンド数が増えたときに遅くなるんじゃないか、との声が聞こえて来そうですが・・・まったくそのとおりです(笑)。ただし私はよほど激しいアクションゲームでも作らない限り、現在のCPU速度なら甘受できるレベルだろうと思っています。高速化したいのであれば、コマンド名は文字列で持たずにコード番号(switch 文で振り分けられる)で持つという手段もありますし、根性があればテキストをコンパイルして中間コード生成するという手もあります。しかしまあ、このコーナーは「へっぽこプログラミング入門」なので、面倒なことは避けて文字列評価のまま進みましょう♪ スクリプトを書く人はプログラマとは限りませんので、少しでも分かりやすいコマンド名を文字列で与えてあげるのが親切というものです(^^;) |
void Command_call()
{
} |
・・・とゆーことで、ちょっと長くなってしまったので次回に続きます♪ |