前回は、戦闘シーンに突入する部分を作ってみました。今回は、この戦闘の具体的な処理を作り、さらに主人公の体力を表示するステータス表示を入れてみましょう。
戦闘処理の基本は、主人公と敵が一定の条件(交互、あるいはすばやさ順など)で攻撃や魔法などの行動を起こし、その結果主人公や敵のパラメータ(体力・魔力など)を変化させる、という流れでしょう。今回は、最も簡単な例として主人公と敵が交互に攻撃しあう、という戦闘シーンを作ってみました。
与えるダメージは、簡単のために乱数で決めます。ダメージを決めたら、その分を主人公や敵の体力から引き、敵の体力を0以下にしたら主人公の勝ち。今回は、「ゲームオーバー」の処理を入れてないので主人公の体力が0以下になっても戦闘を続行し、また主人公はいつでも「逃げる」事が出来るようにしました。
void battle() { // 戦闘処理
int a,b,mhp=6;
char msg[80],wrk[32];
message("敵に遭遇!");
wait();
message("どうしますか?");
while (dialog("闘う","逃げる")==1) {
a=rand() % 4; // ダメージを乱数で決定
mhp-=a;
// 主人公の攻撃メッセージ
lstrcpy(msg,"主人公の攻撃!\n\n");
wsprintf(wrk,"敵に%dのダメージ!\n\n",a);
lstrcat(msg,wrk);
if (mhp<=0) { // 敵を倒したらループ脱出
message(msg);
wait();
break;
}
b=rand() % 3;
hp-=b;
// 敵の攻撃メッセージ
lstrcat(msg,"敵の攻撃!\n\n");
wsprintf(wrk,"主人公は%dのダメージ!",b);
lstrcat(msg,wrk);
drawState(); // 主人公のステータス表示
message(msg);
wait();
message("どうしますか?");
}
if (mhp<=0) // 戦闘結果表示
message("敵を倒しました。");
else
message("逃げました。");
Sleep(100);
wait();
// 通常状態に復帰
iState=0;
drawScreen();
}
戦闘後、通常状態に戻す時には状態変数iStateを0にして画面を描きなおすdrawScreen()を呼んでいますが、この現在の状態の取得/設定や画面再描画などは整理する必要がありますね。今、画面を構成する要素は、背景、マップ、メッセージウインドウ、選択ダイアログ、ステータスウインドウ、といったものなのでこれらをすべてサーフェスとし、フラグで表示・非表示にするようにするとすっきりするかもしれません。再描画関数は、このフラグを見て必要なサーフェスを画面に描画して行くわけです。
表示処理を一箇所にまとめておけば今後サーフェスのロストなどにも対応しやすくなるし、次に見るステータスの描画もステータスサーフェスを更新して再描画関数を呼ぶだけで処理できるようになるでしょう。
主人公のダメージ計算の後にdrawState()という関数がありますが、これが主人公のステータス(状態)を表示する関数です。
ステータスは、224×400ピクセルのサーフェスを作成し、その中に主人公の体力を表示します。ステータスサーフェスの描画は、単に枠を描いてその中に体力を表示しているだけですね。画面に描画する時にいろいろやっていますが、ここではまず現在の画面に更新したステータスを上書きし、その後で背景にもステータスを上書きしています。
なぜ背景にも上書きするかというと、現在のプログラムではあちこちで「背景−マップ」の順番でバックバッファを再描画しているからです。背景は全画面を覆う大きさなので、ここにステータスを描画しておかないとフリップした時にスターテスが表示されないフレームが出来る事になり、ちらつきます。
void drawState() { // ステータス表示
HDC hdc;
char str[32];
RECT rec={0,0,224,400};
RECT rec2={4,4,220,396};
lpDDSState->GetDC(&hdc);
// サーフェスに枠描画
FillRect(hdc,&rec,GetStockObject(WHITE_BRUSH));
FillRect(hdc,&rec2,GetStockObject(BLACK_BRUSH));
// 表示用フォント・領域設定
SelectObject(hdc,hFontH);
SelectObject(hdc,hFontH);
RECT recText={8,8,216,40};
SetTextColor(hdc,0x00ffffff);
SetBkMode(hdc,TRANSPARENT);
// 体力表示
wsprintf(str,"体力 %3d / %3d",hp,hpm);
DrawText(hdc,str,-1,&recText,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
lpDDSState->ReleaseDC(hdc);
// 現在のプライマリサーフェスをバックバッファにコピー
lpDDSBack->BltFast(0,0,lpDDSPrimary,&recScreen,DDBLTFAST_NOCOLORKEY|DDBLTFAST_WAIT);
// バックバッファにステータスウインドウを上書き
lpDDSBack->BltFast(560,56,lpDDSState,&recState,DDBLTFAST_NOCOLORKEY|DDBLTFAST_WAIT);
// バックバッファをフリップ
lpDDSPrimary->Flip(NULL,DDFLIP_WAIT);
// 背景に更新したステータスウインドウを上書き
lpDDSScreen->BltFast(560,56,lpDDSState,&recState,DDBLTFAST_NOCOLORKEY);
}
カーソルキーとリターンキーで操作してください。メッセージやダイアログが表示されていない状態でエスケープキーを押すと終了します。
プログラムをコンパイルする時には、DirectX5以上のSDKをインストールする必要があります。ソースをダウンロードしたら、拡張子をcppにしてプロジェクトの設定でddraw.libをリンクしてからビルドしてください。