■ 新・ゲーム開発講座 |
■ へっぽこプログラミング入門♪ |
■第41夜:メニュー窓を開く ■グラフィックな窓もどき
■書式 今回はマウスの右クリック起動なので、スクリプトの書式指定はありません。 ■仕様 書式がなくても、仕様は決めておかなければなりません。まず起動条件ですが、いくら右クリックで起動と言っても時と場合を選ぶことが必要です。テキストの遅延表示中や選択肢を選んでいる最中、あるいは画面切り替えエフェクトの途中でシステムメニューが開いてしまったのでは具合が悪いでしょうし、いらぬバグの元になりそうです。窓を開くには、なによりも安定した状況が欲しいところです。 ではシステム的に最も安定しているタイミングはなにか…という話になりますが、平行して走っているタスクがほとんど無く、テキスト表示も停止している状態といえば、現実的にはカーソルブリンク中が最も安定した状態であるといえるでしょう。そこで、まず
という単純な決まりごとのみ決めて、ウィンドウ画面を表示してみましょう。またメニュー項目としては
の4つがあれば必要充分としましょう。それぞれの処理は、後日別の章にて順次実装していきます。なお、このように同じボタン(キー)を押すごとにONとOFFが交互に切り替わる仕組みを「トグルスイッチ」などと呼びます。 ■窓構造体 ウィンドウのBMPは以下のようなものを用意します。ウィンドウ3つぶんを横に並べた構成で、左から順にポジ、ネガ、バッファになります。ポジはウィンドウのデフォルトの状態で、マウスがメニュー項目に重なったときだけネガ部分の赤文字の部分を表示します。バッファ(緑の部分)はウィンドウによって隠れてしまう背景部分の画像を一時的に退避しておくスペースです。 |
|
//--------------------------------------------------
↑の構造体を見て 最後のタイマーって何だ?と疑問に思う方もいるかと思いますが、トグルスイッチを実現するにはタイマーは不可欠なのでここに入れています。トグルスイッチは押すたびにONとOFFが交互に切り替わるのですが、タイマーを入れない状態で不用意にメニューをクリックすると、北斗神拳もどきの連打として入力されてしまう場合があるからです。仮に50fpsでタスクが回っていると、1回のループは 20msec で完了してしまいますが、人間の指はそんなに早く反応できません。経験的に、再入防止を図るのに適当な入力スキャン間隔は 200〜300msec くらいだろうと思います。100msec を切ると、よほど神経の良い人でないとうまくクリックすることが困難になります。 ■フラグ Mode_stat システムメニューはフラグを多用したタスク処理になりますので、制御フラグ構造体 Mode_stat にメンバを追加しておきます(main.h 参照)。今回は窓が順次重なっていくスタック構造はとらず、単純な1窓毎の切り替え式とする予定なので、この1個のフラグを各窓で使いまわします。使いまわすためにはフラグの値はON/OFFの2値では間に合いませんので、窓毎のフラグの値(ID)を定義しておきます。↓のリストでは101,102…と振っていますが、値は他と区別がつけば何でも結構です。このフラグを手がかりに、main.cppの Mainloop() 内で各種処理が振り分けられます(詳細は後述)。 |
struct _Mode_stat {
extern _Mode_stat Mode_stat; //システムメニューのID |
#define ON 1 とシンボル定義(BasicTips.h)していますので、特に明示的な初期化をしない場合は構造体のメンバもデフォルトでみな OFF になります。手抜きっぽいですが初期化の手間が省けて結構有効なワザではないかと思います(^^;) ■メニューのトリガー部分 メニューの起動条件は @マウスの右ボタンが押されて、かつ Aカーソルブリンク中 でした。そこでmain.cpp のイベント監視ループ LRESULT WndProc() にこのトリガー処理を仕込みます。何パターンか試してみたところ、くににん的には右ボタンを押した瞬間よりも離した瞬簡にメニュー窓が開くほうがしっくりする感じがしたので、イベントメッセージ=WM_RBUTTONUP のところでメニュー起動関数を呼ぶように記述しました。カーソルブリンク処理中かどうかは Mode_stat のメンバ flag_cursor_blink が ON になっているか調べればよいので、ブリンク処理中のみメニュー起動関数 System_menu() を呼ぶようにします。 |
LRESULT WndProc(HWND hwnd,UINT msg,WPARAM
wprm,LPARAM lprm) {
|
■メニューの起動処理 では、いよいよ窓を開く部分です。メニュー処理の本体部分の記述はソースを分けて
SystemMenu_01.cpp に記述していくことにしましょう。まず、窓用構造体を SysMenu_main
という名でグローバル変数として宣言します。グローバル変数にしておくのは「撃つ」「回す」「締める」のタスク処理の3要素それぞれの関数で参照する可能性があるためです。
↓のリストをみると、最初に #define がたくさん並んでいますが、これは記述が冗長になるのを防ぐ為に構造体の各メンバを短縮表記しているものです。あまり気にしないで結構です。(define は直訳すると「定義する」「決める」という意味になります) |
//----------------------------------------------------- _Menu_Win SysMenu_main; //メインメニューの窓情報用 //名前が長いと面倒なので窓構造体のメンバを短縮表記(^^;)
int System_menu() //システムメニュー窓を開く {
|
System_menu() でやっていることはそんなに難しい処理ではありません。窓構造体に必要パタメータを入れて、再入防止とインターバル処理 (前回クリックから一定時間経過するまでは実行を受け付けない) を行い、その後は窓BMPの読み込みと表示、そしてタスク処理にむけたフラグの準備までを行って、最後にタイマーを撃ちます。 ここで重要なのは「再入処理」と「前回クリックから一定時間入力を受け付けない」部分です。再入を禁じるのは、メニューが既に開いている状態で再度右クリックを検出した場合、それは「窓を開く」という意味ではなく「閉じる」意味なのでここでは受け付けないというものです。再入かどうかの判断は Mode_stat.flag_system_menu のフラグを見て行います。Mode_stat.flag_system_menu は、System_menu() の最後のところで SYSMENU_MAIN がセットされ、窓が開いていなければ OFF なので、区別をつける材料としては丁度よいわけです。 ところで、メモリに読み込んだ窓BMPと、バックサーフェイスの関係がすこしわかりにくいかもしれないので図にしてみました。如何でしょう? |
|
■Mainloop() 起動処理を記述したら、次に main.cpp の Mainloop() にフラグ処理部分を追加します。Mainloop() は常にぐるぐると処理が回っている部分で、ここから各タスク処理を呼んでいます。System_menu() で Mode_statのフラグを立てましたので、フラグをもとにシステムメニューのタスク処理関数 System_menu_task() を呼ぶように追記しておきます。switch文の形式にしているのは、これから実装していくEXIT、FPS、LOAD、SAVEの各処理をフラグで分けるための準備と思ってください。 |
void Mainloop(void) {
|
■メニューのタスク処理 メニューのタスク処理は System_menu_task() になります。ここではまだ選択支処理は行わず、マウスの右クリックを Mouse_stat (第31夜参照) を参照しながらスキャンし、右クリックを検出したら退避してあったウィンドウの背景を書き戻してフラグ類をリセットして終了しています。ここでも重要なのはタイマーの扱いで、トグル動作を確実にするために前回のクリックから 200msec 以内での再クリックは無視するようにしています。 |
int System_menu_task() //システムメニューのタスク部分 {
|
さて、これを実行するとどうなるでしょう。テキストが流れている最中はメニューは反応せず、カーソルブリンクしている時のみ右クリックでメニュー窓が開きます。再度右クリックで窓は閉じます。トグル動作がどういうものか、おわかり頂けますでしょうか。
|