サンプルコードのダウンロード

では、まずサンプルコードをダウンロードし、解凍してください。
私はVisual C++6.0でコンパイルしているので、お持ちの方はVisual C++でプロジェクトファイルを開いてください(「ddraw_14.dsw」をダブルクリックすれば開けます)。
圧縮ファイルに含まれる「ddraw_14.exe」をダブルクリックし、実行してみてください。どうでしょう?画面が切り替わり、フルスクリーン化し、でかいスライムが2匹表示されていると思います(左のが垂直帰線待ち無し、右のが垂直帰線待ち有り)。

というわけで、今回は意図的に垂直帰線待ちをする方法を紹介します。

垂直帰線おさらい

以前フリップの所でも触れましたが、もう一度説明しておきます。
ディスプレイは小さな電子銃からでる電子ビームを走査して、ビデオメモリの内容を連続的に反映しています。
電子ビームは、左上から始まり、右へ進み、一番右まで行ったら一つ下へ下がり、一番左まで行き、また右へ進みます。
それらを繰り返し、最後に右下端まで行ったら、一気に左上端に戻ります。
この「右下端から左端上」に戻る間の事を「垂直帰線」とか言います。

で、フリップはこの垂直帰線中に行っているため、画面はちらつきません。
しかしフリップ以外でフロントバッファにBltFastか何かで転送したい場合、垂直帰線中では無いため、画面がちらつく場合があります。
そこで垂直帰線待ちをして、ちらつきを無くそうというわけです(謎

ただ、この垂直帰線中に処理しきれない場合はやっぱりちらつきますので(^^;

というわけでコードの解説

今回も順番に解説していきます。

まず、先に「長方形塗りつぶし」をする自作関数「Boxfill」を説明しましょうかね。

//----------[ 長方形(塗りつぶし)描画 ]--------------------------------------------------------
void Boxfill(LPDIRECTDRAWSURFACE sf,int x,int y,int width,int height,BYTE color){
	DDBLTFX ddbltfx;
	ZeroMemory(&ddbltfx,sizeof(DDBLTFX));
	ddbltfx.dwSize=sizeof(DDBLTFX);
	ddbltfx.dwFillColor=color;
	RECT rect={x,y,x+width,y+height};
	
	//クリッピング
	if(rect.left<cx1)
		rect.left=cx1;
	if(rect.right>cx2)
		rect.right=cx2;
	if(rect.top<cy1)
		rect.top=cy1;
	if(rect.bottom>cy2)
		rect.bottom=cy2;

    sf->Blt(&rect,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&ddbltfx);
}

サーフェイスのメンバ関数「Blt」については以前「DirectDraw基礎 第9回」でやっているので、構文等はそちらを参照してください。
今回は、サーフェイスの一部を一色で塗りつぶしたいので、
Blt関数の最初の引数にRECT構造体のアドレスを引き渡してやります(RECT構造体の解説は「DirectDraw基礎 第8回」でやりました)。
・・・ってもう説明する事無いですね(汗

では次は、今回の本題「垂直帰線待ち」をする自作関数「VsyncWait」の説明~。

//----------[ 垂直帰線待ち ]--------------------------------------------------------------------
void VsyncWait(void){
	lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,NULL);
}

見て解る通りDirectDrawオブジェクト(lpDD)がメンバ関数である「WaitForVerticalBlank」を呼び出します。

構文は以下の通りです。

書式 HRESULT WaitForVerticalBlank( DWORD dwFlags, HANDLE hEvent );
dwFlags 以下のどれかを指定します。

DDWAITVB_BLOCKBEGIN 垂直帰線が開始するまで待つ
DDWAITVB_BLOCKBEGINEVENT 垂直帰線が開始したらイベントを起動するらしいが、現在はサポートしてないらしい。
DDWAITVB_END 垂直帰線が終了するまで待つ

hEvent サポートされていないので、とりあえずNULL
戻り値 成功するとDD_OKが返ってくるらしい。

そんでもって、これらを使用した今回のメインコードはこれ。

//----------[ メイン関数 ]----------------------------------------------------------------------
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow){
    
	略
	
	//DirectDraw開始
	StartDirectDraw(hw);

	//パレット設定
	LoadPalette("slime.bmp");
	
	//ビットマップを作業用サーフェイスへ読み込む
	LoadBitmap(lpWork,"slime.bmp",0,0,32,32);
	//拡大
	BltStretch(lpWork,0,0,320,480,lpWork,0,0,32,32,FALSE);

	int wid=320,hei=480;

	DWORD tim;
	while(1){
		//疑似タイマー処理
		tim=timeGetTime();

		//左側のスライム(垂直帰線待ち無し)
		Boxfill(lpFront,0,0,wid,hei,0);
		Blt(lpFront,0,0,wid,hei,lpWork,0,0,FALSE);

		//右側のスライム(垂直帰線待ち有り)
		VsyncWait();
		Boxfill(lpFront,320,0,wid,hei,0);
		Blt(lpFront,320,0,wid,hei,lpWork,0,0,FALSE);

		do{
			//メッセージループ
			while(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)){
				if(!GetMessage(&msg,NULL,0,0)) 
					Quit();
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}while(timeGetTime()<tim+50);	//今回は適当
	}

ただひたすら「同じ場所に書いたり消したり」するだけで、右のスライムは垂直帰線待ちをし、垂直帰線待ちがどういう働きをするかが一目で解るようなプログラムになっているのである。えっへん(゚o゜)\ばき☆

記事検索

アーカイブ