■ 新・ゲーム開発講座 |
■ へっぽこプログラミング入門♪ |
■第32夜:選択肢@ 今回は選択肢を選ぶ処理を実装したいと思います。いよいよ、プレーヤがゲームに関与できる部分に手を出す訳ですね♪ フラグも、ラベルジャンプも、#if_flag 文による分岐も、みなこの「ユーザーが選択肢を選ぶ」という機能がなければ存在意義がないと言っても良いくらいです。 今回作る選択機能は、図のようにテキスト中に3択の選択肢を表示し、マウスがクリックされるとそのメニューに応じた分岐をするというものです。あまり奇をてらったものを作ってもナニですので、オーソドックスに参りましょう。前回マウス入力を取得する仕掛けを作りましたので、それもうまく使っていきましょう。ソースはこちらになります。
書式を見ると、ちょっと込み入った感じがしないでもありませんね。もしこの項から読み始めた方がおられましたら、「へっぽこプログラミング入門」の少し前の章から読み直すことをお勧めします(^^;)テキストのブロック処理については「分岐」の項で解説していますし、マウス入力に関しては前項で扱っています。またスクリプトコマンドの基本的な部分については第10夜、第11夜が参考になると思います。 ■作業用サーフェイス さて「選択」と言いますのはプログラム的に見た場合どのような処理になるのでしょうか。基本的には選択肢となる文字列を表示して、その矩形領域内でマウスの左ボタンがクリックされるのを監視する・・・というものです。マウスがクリックされた座標に応じて、該当するテキストアドレスに制御を飛ばせば処理を分岐できます。ゲームらしい視覚効果を狙うなら、クリックを待つあいだ単に空ループを回すのでは芸がないので、ここでは選択肢の上にマウスカーソルが来たら文字色を反転表示させてみましょう。 そのためには、新たに作業用のサーフェイスを2枚用意する必要があります。1枚は「選択肢文字列+背景」を保持するもの、もう1枚は「反転文字+背景」を保持するものです。処理上はこの他に「文字なし背景」を保持するサーフェイスが必要ですが、これは既に BG面として実装していますので特に新規コーディングはしないで済みそうですね。なお、メモリ効率を考えるなら選択肢の面積分だけ作業用サーフェイスを確保すべきなのですが、ここでは面倒なので640×480のゲーム画面そのままのサイズにしようと思います。何故って?・・・サーフェイス間転送で座標変換を気にする必要がありませんので♪(このへんが「へっぽこ」らしい安直なところ ^0^)。 |
|
選択コマンドの実装は、以下のような要領になります。@初期化(撃つ)の部分で作業用サーフェイスを作成して文字列を展開する。Aマウス入力監視タスク(回す)ではマウス位置によってノーマル色/反転色の選択肢画像をバックサーフェイスに転送し続ける。B終了処理(締める)では選択肢に応じたデリミタ(区切り文字)以降にテキストポインタ *TEXT を飛ばす・・・というカンジでしょうか。では、以下具体的な実装の話を進めます。 ■フラグ Mainloopで参照しているフラグ構造体 Mode_stat (main.cpp参照)に選択処理用のメンバ flag_select3 を加えます。Mainloopではこのフラグを見て「選択処理」のタスク部分を呼び出します。 |
struct _Mode_stat {
|
■コマンド呼び出し部 テキストエンジンのコマンド名評価→呼び出し関数 Command_call() (TextEngine.cpp)に以下の行を追加します。スクリプト上で #select3 というコマンド文字列を見つけたらその処理関数 Com_select3() を呼ぶ、ということですね。 |
void Command_call() {
|
■Mainloop() ↓のようにメインループ(main.cpp)に処理を加えます。フラグがONならタスク処理を呼び出すようにしているだけです。 |
void Mainloop(void) {
|
■コマンド処理開始部(撃つ) ではコマンド開始部 Com_select3() です。↓ちょっと見るとややこしいように見えますが、似たような処理の繰り返しなのでじっくり観察してください(そんなこと言う前に奇麗に分割して記述しろってば>自分 ^0^;)。 ここでは、大雑把に 事前チェック → 選択肢文字列を解析 → サーフェイスの準備 → 文字列展開 → フラグのセット までを行っています。まず一番最初は、事前チェックとして選択肢を3行分表示する余裕があるか(実際には余裕を見て5行分)を調べます。ここでは面倒なので、NGの場合はいきなり PostQuitMessage() を投げて終了しています(笑) 次の選択肢文字列の取込みですが、スクリプトの書式に従って最初の「{」を飛ばし、さらに改行その他のコントロールコードを含んでスペースまで(ASCIIコードでは全部まとめて 0x20=SPACE 以下の値になります)を飛ばしながらテキストポインタを進め、選択文字列先頭にたどり着いたら順次 Kaiseki_TextStr_CR() で文字列取込みをしていきます。Kaiseki_TextStr_CR() は TextEngine.cpp に記述してある便利関数で、行末までを文字列として取り込みます。いつものようにスペースを区切りに使わないのは、選択肢は単語1つに限らないこと、また英文のようにスペースを頻繁に挟んだものが指定される可能性もあるからです。 選択肢取込みが終了したら、作業用サーフェイスを確保して選択肢を展開します。味付けのためにいつもの50%増しで改行を行い、さらに20ピクセルほどインデントを付けていますが、このへんはお好み次第というところでしょう。また文字ピッチと文字列長さを用いて選択肢の矩形領域を決めて保持しておきます。ここで計算に用いている文字表示位置XYやピッチなどの情報については、テキストエンジンの雛形を作った第9夜を参照して下さい。グローバル変数で持っているので特に断わりもなく使っています。途中から読み始めると???になるかも知れませんね(大汗
^^;)。 選択肢画像が出来たら、それを表画面に転送し、フラグをセットして準備完了です。あとはフラグによって Mainloop からタスク処理部分が呼ばれ続けます。 |
HDC Select1_DC; //選択肢文字列用 HDC Select2_DC; //反転文字列用 RECT rect1,rect2,rect3;//選択肢1〜3の画面表示領域 #define POSI_COLOR1 RGB(255,255,100)
//選択肢の色 /------------------------------ void Create_select_Surface1()
} void Delete_select_Surface1()
//------------------------------ void Create_select_Surface2()
void Delete_select_Surface2()
} //------------------------------ // コマンド起動部(撃つ) //------------------------------ int Com_select3()
|
■タスク処理(回す+締める) タスク処理では、選択メニューの矩形部分毎に _Check_mouse_in_Rect() を呼び出してマウス位置を確認し、それに応じて反転/非反転画像をバックサーフェイスに転送してやります。これによって、マウスがメニューをポイントしているときは反転画像が、またメニューから外れれば通常メニュー画像が現われることになります。マウスが領域内をポイントしているときに左ボタンが押下げられていれば「選択」操作が行われたということですから、該当するテキストブロックにテキストポインタを飛ばしてフラグ解除、さらに作業用サーフェイスを消去して終了します。 |
//------------------------------ bool _Check_mouse_in_Rect( RECT rect
)
//----------------------------- int Com_select3_task()
|
テキストポインタを飛ばす際に、条件分岐の項で出てきた next_slush() が↑何度も呼ばれていますね。選択肢をもっと増やしたいときは4回、5回・・・とコールすればいくらでも分岐を増やせます。また選択肢文字列の取込み部分を少し工夫すると可変選択肢対応も可能になります。雛形はここに示しましたので、いろいろな変形Versionを作ってみて下さい。 |