■ 新・ゲーム開発講座




■ へっぽこプログラミング入門♪


■第17夜:終了

え〜、突然ですが、本講座は本日を持ちまして終了です。

・・・というのは悪質な冗談です(大爆死 ^0^)。今回は「終了」コマンドを実装します。本当なら「改ページ」を行いたいところなのですが、早いうちにスクリプト上から明示的に終了を宣言できるようにしておかないと、文章の終端以降、延々とゴミを表示し続けるハメになってしまいますので・・・(現在はページ端に達したところで強制終了になっています)。

■書式:#halt

テキスト表示を停止して、マウス左ボタン或はスペース/リターンキーが押されるまで待機し、プログラムを終了する。


実装は、カーソルブリンクと良く似た形になります。ソースは
こちらです。

■処理の概要

基本的にこの実装は「遅延」の一種です。オリジナルの遅延処理は終了条件として「一定時間の経過」を定めていましたが、今回はプレーヤーがマウスクリックするまで待つ、ということですから終了トリガはイベントハンドラになります。形式としては「カーソルブリンク」のカーソル無し版に近いものになります。そしてマウス(キー)入力を検知したら、テキスト表示を再開するのではなく、そのままプログラムを終了させてしまいます。

■フラグ

以下のように flag_halt を創設します(main.h参照)。



struct _Mode_stat {

int flag_text; //テキスト表示:ON=進行 OFF=停止

int flag_delay; //遅延処理:ON=遅延あり OFF=遅延なし

int flag_cursor_blink; //カーソル点滅:ON=点滅 OFF=点滅なし

int flag_halt; //終了:ON=終了 OFF=終了ではない

};


■コマンド解釈部

以下のようにコマンド初期化関数 Com_halt() を追加します。(TextEngine.cpp 参照)



void Command_call()
{

char com_name[256];
int i=0;
int flag=OFF;

TEXT++; //#を飛ばす

//コマンド文字列の切り出し
while( *TEXT>0x20 ){
com_name[i] = *TEXT;
TEXT++;
i++;

}
TEXT--;
com_name[i]=0; //これはNULL文字

//コマンド名の評価 → 処理関数呼び出し
if( strcmp(com_name,"delay" )==0 ){ Com_delay(); flag=ON;} //遅延
if( strcmp(com_name,"wait" )==0 ){ Com_cursor_blink(); flag=ON;} //カーソルブリンク
if( strcmp(com_name,"w" )==0 ){ Com_cursor_blink(); flag=ON;} //カーソルブリンク
if( strcmp(com_name,"halt" )==0 ){ Com_halt(); flag=ON;} //カーソルブリンク

// ↑
//そのうちここに他のコマンドも追加していきましょう
// ↓

//フラグを見て、切り出されたコマンド名が有効なコマンドだったかどうか確認する
if( flag == OFF ){

char str[256];

sprintf( str,"警告:無効なコマンド [#%s] が記述されています",com_name);
MessageBox(NULL,str,"Command_call()",MB_OK);


}


}


■Mainloop()

フラグを参照して、タスク処理部をコールするようにします。

void Mainloop(void)
{

//テキスト表示
if( Mode_stat.flag_text == ON ) Text_engine();

//遅延
if( Mode_stat.flag_delay == ON ) Com_delay_task();

//カーソルブリンク
if( Mode_stat.flag_cursor_blink == ON ) Com_cursor_blink_task();

//終了
if( Mode_stat.flag_halt == ON ) Com_halt_task();

HLS_stc_FPS(Back_DC);
BitBlt(win_hdc,0,0,640,480,Back_DC,0,0,SRCCOPY); //裏画面 → 表画面にコピー


}



■撃つ

コマンド初期化関数で行う内容は、フラグ操作のみです。テキスト表示を止めて、終了処理のフラグを立てます。


int Com_halt()
{

//テキスト表示を停止する
Mode_stat.flag_text = OFF;

//終了のループフラグを立てる → Mainloop()参照用
Mode_stat.flag_halt = ON;

return 0;


}


■回す

今回はキー入力があれば即終了なので、何もすることがありません。タイマーチェックも無しです。

int Com_halt_task()
{
return 0; //なにもしない(^^;)・・・ただ待つだけ

}


■締める

前回のカーソルブリンクと同様、WM_LBUTTONDOWN、WM_KEYDOWN でマウス左ボタン、スペース/リターンキーをチェックし、押されていれば終了関数 Com_halt_end() を呼ぶようにします。


LRESULT WndProc(HWND hwnd,UINT msg,WPARAM wprm,LPARAM lprm)
{

//↓この例では、Window右上の消去ボタンを押して終了させることしか出来ません(爆)
//後に、解説しながらいろいろなイベント処理を追加していきます。

switch ( msg ) {
case WM_CREATE: //Windowが生成された
break;
case WM_DESTROY: //Windowの消去操作がされた
PostQuitMessage(0);
break;
case WM_KEYDOWN: //キーが押された
switch(wprm){
case VK_ESCAPE: //ESC
PostQuitMessage(0); //終了
break;
case VK_SPACE: //SPACE
case VK_RETURN: //RETURN
//カーソルブリンク中ならブリンク解除
if( Mode_stat.flag_cursor_blink == ON ) {
Com_cursor_blink_end();
}
//終了待ち中なら終了処理
if( Mode_stat.flag_halt == ON ) {
Com_halt_end();

}
break;
}
break;
case WM_LBUTTONDOWN: //マウス左ボタンが押された
//カーソルブリンク中ならブリンク解除
if( Mode_stat.flag_cursor_blink == ON ) {
Com_cursor_blink_end();
}
//終了待ち中なら終了処理
if( Mode_stat.flag_halt == ON ) {
Com_halt_end();

}

break;
default:
//その他のイベントはWindowsのシステムにお任せ(楽ちん、楽ちん)
return DefWindowProc(hwnd,msg,wprm,lprm);
}

return 0;

}

終了関数では、終了するときの常套句、PostQuitMessage() を終了コード0で投げてプログラムを終了させます。・・・これだけ(笑)

int Com_halt_end()
{

//終了
PostQuitMessage(0);

return 0;


}

え? 「随分あっさりした解説だ」 ですって? …それはですね、内容的に以前実装した「遅延」の変形版であるということもありますが、最初に「定型化」のタネを蒔いておいたからです。コードがパターン化してしまえば追加/改造がラクになります。…そういえば、不思議なことですが、WEBを見渡しても「細かいTIPS集」みたいなページはたくさんあるのですが、構造的なハナシを低レベルで解説してくれているところはあまり見かけませんね…なぜでしょう(^^)。

蛇足的に付け加えるなら、上記の解説は一部インチキ(笑)を含んでいます。タスク処理の中身は空っぽですから、Mainloop() に追加した部分は意味がありません。実際にフラグを参照しているのはイベントハンドラの中だけです。・・・しかしまあ、これも多少無理にでも定型化して示して行こうという一種の方便ですので、許容して頂きたいと思います(汗 ^^;)

なおここではまだ考えませんが、MIDIやCD-DAでBGMを流していた場合は PostQuitMessage() の前に演奏を終了させておく必要がありますのでご注意下さい。