今回は、メッセージを表示するイベントを作ってみます。処理としては、マップ属性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をリンクしてからビルドしてください。