今回は、メッセージを表示するイベントを作ってみます。処理としては、マップ属性mInfo[64][64]の下位8ビットを見て、イベントがあればメッセージを表示するだけなので特に難しい部分はないでしょう。
今回のRPGでは全画面を占有しているので、ダイアログやフレームなどWindowsにあるGUI部品が使えません。メッセージを表示する領域も自分で作る事になります。
メッセージ表示領域は、384×192ピクセルのサーフェス(メッセージウインドウ)として作成しました。このサーフェスの外側2ピクセルに「枠」を付け、内部にDrawText()でメッセージを描画します。DrawText()では、文字の改行などの処理も行ってくれるので、表示領域に枠の部分を除いたメッセージウインドウ全体を指定してやるだけで良いでしょう。
// メッセージウインドウ用サーフェス作成 FillMemory(&ddsdms,0,sizeof(ddsdms)); ddsdms.dwSize=sizeof(ddsdms); ddsdms.dwFlags=DDSD_CAPS| DDSD_WIDTH| DDSD_HEIGHT; ddsdms.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN; ddsdms.dwWidth=384; ddsdms.dwHeight=192; lpDD->CreateSurface(&ddsdms,&lpDDSMessage,NULL);
メッセージウインドウにメッセージを表示する関数messageは以下のようになります。
void message(LPCTSTR str) { // メッセージ表示
RECT rec={32,32,512,512};
HDC hdc;
// メッセージウインドウをクリア
lpDDSMessage->Lock(NULL,&ddsdms,DDLOCK_WAIT,NULL);
LPBYTE lpBuf=(BYTE*)ddsdms.lpSurface; // オフスクリーンサーフェスのポインタ
FillMemory(lpBuf,384*192,255);
for (int i=2;i<190;i++)
FillMemory(lpBuf+2+i*384,380,0);
lpDDSMessage->Unlock(NULL);
// メッセージウインドウサーフェスのデバイスコンテキスト取得
lpDDSMessage->GetDC(&hdc);
// メッセージ描画
SetTextColor(hdc,0x00ffffff);
SetBkMode(hdc,TRANSPARENT);
DrawText(hdc,str,-1,&recMText,DT_LEFT|DT_WORDBREAK);
lpDDSMessage->ReleaseDC(hdc);
// バックバッファに背景を描画
lpDDSBack->BltFast(0,0,lpDDSScreen,&recScreen,DDBLTFAST_NOCOLORKEY);
// バックバッファにマップを描画
lpDDSBack->BltFast(32,88,lpDDSMap,&rec,DDBLTFAST_NOCOLORKEY);
// バックバッファに主人公を描画(透過処理)
lpDDSBack->BltFast(224+32,224+88,lpDDSChr,&recChr,DDBLTFAST_SRCCOLORKEY);
// バックバッファにメッセージウインドウを描画
lpDDSBack->BltFast(48+32,272+88,lpDDSMessage,&recMes,DDBLTFAST_NOCOLORKEY);
// バックバッファをフリップ
lpDDSPrimary->Flip(NULL,DDFLIP_WAIT);
}
まず、今回はメインループの処理の流れを通常、スクロール中、メッセージ表示中という「状態」に応じて分けてみました。具体的には、現在の状態を示す変数iStateを用意し、その値によって違う関数を呼び出しています。
while (1) { /* メインループ */
if (PeekMessage (&msg,NULL,0,0,PM_NOREMOVE)) {
if (!GetMessage (&msg,NULL,0,0)) // メッセージ処理
return msg.wParam ;
TranslateMessage(&msg);
DispatchMessage(&msg);
} else
switch (iState) {
case 0: // 通常時
none();
break;
case 1: // スクロール中
scroll();
break;
case 2: // リターンキー入力待ち
wait();
bewk;
}
}
イベント判定は、通常時(非スクロール時)の最後に移動先(または現在位置)にイベントがあるか調べ、あればイベント処理関数eventを呼び出すようにしました。
void none(void) { // 通常時
int kX,kY;
iDx=32;
iDy=32;
iDdy=0;
iDdy=0;
kX=0;
kY=0;
// カーソルキーで移動
if (GetAsyncKeyState(VK_UP)<0) {
kY=-1;
if(mInfo[iX][iY-1]>32767) {
iY--; // 主人公の位置更新
iDdx=0; // スクロール方向設定
iDdy=-iScd;
kY=0;
iState=1; // スクロールフラグセット
}
} else if (GetAsyncKeyState(VK_RIGHT)<0) {
kX=1;
if(mInfo[iX+1][iY]>32767) {
iX++;
iDdx=iScd;
iDdy=0;
kX=0;
iState=1;
}
} else if (GetAsyncKeyState(VK_DOWN)<0) {
kY=1;
if(mInfo[iX][iY+1]>32767) {
iY++;
iDdx=0;
iDdy=iScd;
kY=0;
iState=1;
}
} else if (GetAsyncKeyState(VK_LEFT)<0) {
kX=-1;
if(mInfo[iX-1][iY]>32767) {
iX--;
iDdx=-iScd;
iDdy=0;
kX=0;
iState=1;
}
}
// イベント判定
if ((mInfo[iX+kX][iY+kY] & 0xff)!=0)
event(mInfo[iX+kX][iY+kY] & 0xff);
}
変数kX, kYはキーが押された方向を保存するために使っています。
void event(int e) {
switch (e) {
case 1:
message("水です。\n2行目");
break;
case 2:
message(
"人物1\nテストテストテストテストテストテストテストテストテスト");
break;
case 3:
message("人物2");
break;
}
iState=2; // 入力待ち
}
カーソルキーで上下左右に移動できます。メッセージウインドウは、リターンキーで閉じ、終了する時にはエスケープキーを押してください。
プログラムをコンパイルする時には、DirectX5以上のSDKをインストールする必要があります。ソースをダウンロードしたら、拡張子をcppにしてプロジェクトの設定でddraw.libをリンクしてからビルドしてください。