第2章で、ゲームプログラムの作り方ついて説明した。あとは、画像をバックサーフェイスに転送し、フリップ処理を行えば、画面に画像を表示できる。
画像を表示する場合、プライマリ・サーフェイスにパレットを設定しなければならない。(詳しくは1-4(c)を参照)
パレットは、オフスクリーン・サーフェイスに使用する画像ファイルから生成する。手順は次のとおり。
- パレット・オブジェクトをグローバル変数として宣言する。
// グローバル変数
LPDIRECTDRAWPALETTE g_pDDPal = NULL; // プライマリ・サーフェイス・パレット
char szStartBmp[] = "STARTBMP"; // スタート画像リソース名 InitializeDraw関数のreturn文の前に、次のプログラムを追加する。
// パレットの生成
g_pDDPal = DDLoadPalette(g_pDD, szStartBmp);
if (g_pDDPal == NULL) return InitFail(hWnd, hRet, "DDLoadPalette FAILED");
// プライマリ・サーフェイスにパレットを設定する
hRet = g_pDDSPrimary->SetPalette(g_pDDPal);
if (hRet != DD_OK) return InitFail(hWnd, hRet, "SetPalette FAILED");- DirectDrawオブジェクト開放時、
パレット・オブジェクトを開放するよう、RestoreDraw関数を修正する。
//----------------------------------------------------------------------------- // 関数名 : ReleaseDraw() // 機能概要: Direct Draw オブジェクトの削除 //----------------------------------------------------------------------------- void ReleaseDraw(void) { if (g_pDD != NULL) { if (g_pDDSPrimary != NULL) // プライマリーサーフェイス { g_pDDSPrimary->Release(); g_pDDSPrimary = NULL; }if (g_pDDPal != NULL) // パレット{g_pDDPal->Release();g_pDDPal = NULL;}g_pDD->Release(); // DirectDrawオブジェクト g_pDD = NULL; } }※複数の画像ファイルがあっても、どれか1つから生成すればよい。
バック・サーフェイスに転送する画像は、
ビデオメモリ上にオフスクリーン・サーフェイスとして読み込んでおく必要がある(詳しくは第1章を参照)。
スタート画面で使用する画像はリソースとして準備してあるので、DirectDrawオブジェクトの初期化を行うInitializeDraw関数に、リソースの画像を元にオフスクリーン・サーフェイスを作成するプログラムを追加する。作成方法は第1章のとおりだが、エラー処理も含めると、次のようになる。
// グローバル変数 LPDIRECTDRAWSURFACE7 g_pDDSStart = NULL;// スタート画像用サーフェイス // InitializeDraw関数内(return文の前に追加) // オフスクリーン・サーフェイスを生成する(画像毎) /* スタート画像 */ g_pDDSStart = DDLoadBitmap(g_pDD, szStartBmp, 0, 0); if (g_pDDSStart == NULL) return InitFail(hWnd, hRet, "DDLoadBitmap StartBmp FAILED");DirectDrawオブジェクト開放時、
オフスクリーン・サーフェイスを開放するよう、ReleaseDraw関数を修正する。
//----------------------------------------------------------------------------- // 関数名 : ReleaseDraw() // 機能概要: Direct Draw オブジェクトの削除 //----------------------------------------------------------------------------- void ReleaseDraw(void) { if (g_pDD != NULL) { if (g_pDDSPrimary != NULL) // プライマリーサーフェイス { g_pDDSPrimary->Release(); g_pDDSPrimary = NULL; }if (g_pDDSStart != NULL) // スタート画像用サーフェイス{g_pDDSStart->Release();g_pDDSStart = NULL;}if (g_pDDPal != NULL) // パレット { g_pDDPal->Release(); g_pDDPal = NULL; } g_pDD->Release(); // DirectDrawオブジェクト g_pDD = NULL; } }
画像の描画は、
オフスクリーン・サーフェイス上の任意の矩形を、バック・サーフェイスの任意の座標に転送し、Flip処理によりプライマリ・サーフェイスとバック・サーフェイスを切り替えることにより行われる。
例えばスタート画像からスタート画面用の矩形(0, 0, 640, 480)を切り取り、バック・サーフェイスに転送するには、次のようにプログラムする。
- スタート画面用の矩形を格納する変数をグローバル変数として宣言する。
// グローバル変数
RECT startRect; // スタート画面背景画像矩形- スタート画面表示前初期化処理関数
StartInitで、矩形を初期化する。
//------------------------------------------ 各変数の初期化
SetRect(&startRect, 0, 0, 640, 480);- スタート画面表示処理関数
StartFrameで、指定した矩形をバック・サーフェイスに転送する。
//----------------------------------------------------------------------------- // 関数名 : StartFrame() // 機能概要: 画面更新処理 //----------------------------------------------------------------------------- void StartFrame(HWND hWnd) {HRESULT hRet;// スタート画面用背景画像を転送するhRet = g_pDDSBack->BltFast(0, 0, g_pDDSStart, &startRect, DDBLTFAST_NOCOLORKEY);if (hRet != DD_OK)return;※BltFast関数については、第1章5−4を参照
※RECT構造体、SetRect関数については、用語集「RECT構造体」を参照
BltFast関数は、
転送した画像がサーフェイスからはみ出すと、描画に失敗し、エラーを返す。![]()
2−3に載せたプログラムをもう一度見てみよう。
BltFast関数の戻り値を調べ、DD_OK以外ならreturn文を実行、つまり、StartFrame関数を抜けるようにプログラムされている。よって、複数の画像を表示する場合、どれか1つでも描画が失敗したら、それ以降の描画処理は実行されずにStartFrame関数を抜けてしまうということである。
画像の矩形や転送先座標がサーフェイスからはみ出さないようなプログラムを組むことが重要である。
画面の表示は
バック・サーフェイスに画像を転送し、フリップ処理によりプライマリ・サーフェイスとバック・サーフェイスを切り替えることにより行われる。つまり、フリップした直後はプライマリ・サーフェイスで表示していた画像が残っていることになる。
この状態でキャラクタ画像を転送しても、前の画像が残っているために、キャラクタ画像が複数表示されてしまう。つまり、新しい画像を表示するには、前の画像を消さなければならない。
前の画像を消すには次の3通りのアプローチ(手法)が考えられる。
- バック・サーフェイスをすべて何かの色で塗りつぶしてから描画を行う。
(実習で使用しているプログラムのスタート画面はこの方法を採用している)- バック・サーフェイスと同じ大きさの背景画像を転送し、前の画像にかぶせてしまう。
(座学で使用しているプログラムでは、この方法を採用している)- 前の画像との変更部分のみ、背景から描画しなおす。
(描画処理が最小限ですむため負荷は一番少ないが、プログラムが複雑になる)![]()
バック・サーフェイスを画像を使わずに任意の色に塗りつぶす方法は、実習編「DirectX小技集」を参照。
オフスクリーン・サーフェイスの画像をバック・サーフェイスに転送しただけでは、ディスプレイに表示されている画像は更新されない。更新するには、プライマリ・サーフェイスとバック・サーフェイスを切り替える、つまり
フリップ処理を行わなければならない。
フリップ処理は、どんな場面(スタート場面、ゲーム場面など)でも関係なく行う必要があるため、UpdateFrame関数の一番最後で実行するのが望ましい。UpdateFrame関数の一番最後に、次のプログラムを追加する。
/* フリップ処理 */
g_pDDSPrimary->Flip(NULL, 0);※これでビルドを行うと、スタート画像が表示される。
※プログラム的にはゲーム・ループにより高速にフリップし続けている[ 実行結果サンプル ]
問題1
スタート画面用画像には、背景画像のほかに、「タイトル」「スタートメッセージ」が用意されている。StartFrame関数に、タイトル画像とスタートメッセージ画像を表示する処理を追加せよ。なお、表示場所は自由とする。
問題2
ゲーム画面用画像をダウンロードしてプロジェクトのフォルダにコピーし、リソースに追加せよ。リソース名は「"GAMEBMP"」とする。
(既存のリソースにビットマップを追加するには、リソースウィンドウで「Bitmap」フォルダを右クリックし、「インポート」を選ぶ)問題3
スタート処理と同様に、「GameInit関数」「GameFrame関数」を作成せよ。GameFrame関数では、以下の処理を行う。
- 背景画像(宇宙)を画面いっぱいに表示
- 天使の画像の1つ目を、画面の中央(320,240)に表示
- F1キーを押すと、スタート画面に戻る(仮想キーコード参照)
[ 実行結果サンプル ]