• ベストアンサー

描画のカクカクについて

描画された画像が移動中たまに(動画を見ながら実行すると多く)カクカクするのですが、どうしたらこのカクカクをなくすことができるでしょうか? 多分ここだろうなと言うところを載せます。 //グローバル変数 static HDC g_hdcOff,g_hdcImg[MAX_IMAGE]; static HBRUSH g_hbrBg = NULL; // 背景用ブラシ static HBITMAP g_hbmImg[IMG_MAX]= {NULL}; // 画像のビットマップ //================================================================================ // オフスクリーンへ画像表示 // idx : 画像データの添え字 // x,y : 表示位置x,y // sx,sy : 画像切り出し位置x,y // w,h : 画像切り出し幅、高さ // src : 転送モード指定。通常はSRCCOPY,SRCPAINT,SRCANDなど //================================================================================ void DrawImg(int idx,int x,int y,int sx,int sy,int w,int h,DWORD src) { BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],sx,sy,src); } //================================================================================ // オフスクリーンへ画像表示。マスク表示→画像表示 // idx : 画像データの添え字 // x,y : 表示位置x,y // sx,sy : 画像切り出し位置x,y // w,h : 画像切り出し幅、高さ // mx,my : マスク切り出し位置x,y //================================================================================ void DrawImgMask(int idx,int x,int y,int sx,int sy,int w,int h,int mx,int my) { BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],mx,my,SRCPAINT); BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],sx,sy,SRCAND); } void Draw() { // 画面消去 HBRUSH oldbr = (HBRUSH)SelectObject(g_hdcOff, g_hbrBg); Rectangle(g_hdcOff, -1, -1, CLIENT_WIDTH+1, CLIENT_HEIGHT+1); SelectObject(g_hdcOff, oldbr); //---------------------------------------------------------- // ↓ここから描画処理 // キー状態の更新 KeyUpdate(); // プレイヤー移動 if (KeyIsHold(KC_LEFT)) { g_Player.x -= PL_VELX; if (g_Player.x < PL_LIMITL) g_Player.x = PL_LIMITL; } if (KeyIsHold(KC_RIGHT)) { g_Player.x += PL_VELX; if (g_Player.x > PL_LIMITR-g_ImgData[g_Player.type].w) g_Player.x = PL_LIMITR-g_ImgData[g_Player.type].w; } // プレイヤーの表示 DrawChr(g_Player.type, g_Player.x, g_Player.y); // ↑ここまで描画処理 //---------------------------------------------------------- // 再描画 InvalidateRect(g_hWnd,0,FALSE); UpdateWindow(g_hWnd); } // キャラクタ描画関数 // type : キャラタイプ(eChrType) // x : 表示位置X // y : 表示位置Y void DrawChr(int type,int x,int y) { DrawImgMask(g_ImgData[type].idx, x,y, g_ImgData[type].sx, g_ImgData[type].sy, g_ImgData[type].w, g_ImgData[type].h, g_ImgData[type].mx, g_ImgData[type].my); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { ・・・ ・・・ ・・・ // メッセージループ while(true) { if (PeekMessage(&msg, NULL, 0,0,PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { static DWORD before_time; DWORD now_time; DWORD ideal_time=0; static int frame=0; TCHAR buff[100]; DWORD team=0; static long surplus_time=0; now_time=timeGetTime(); if(!before_time) before_time=timeGetTime(); surplus_time=(long)(now_time-before_time); ideal_time=(DWORD)(frame*1000)/60; wsprintf(buff,TEXT("%d %d %d\n"),ideal_time,surplus_time,(long)ideal_time-surplus_time); OutputDebugString(buff); if((long)ideal_time-surplus_time>0) Sleep((long)ideal_time-surplus_time); team=timeGetTime(); team-=before_time; if(team>=1000) { before_time=timeGetTime(); frame=0; } frame++; } Sleep(1); } ・・・ ・・・ ・・・ return (int)msg.wParam; } 環境はVC++2005、C言語とWINAPIっだけです。 説明が足りないのなら補足します。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

そういえば、透過とかできないからマスクしてなんてこともしてたなぁとか すこしずつ思い出しつつ んで、昔作ったソース探したのですが見つからないものですね(笑 今じゃもう、ビルド環境すらないのでどうにもならない今日この頃なのですけど やっていることはあっている感じですねぇ でも全画面転送にWM_PAINTは経由していなかった気がする なんとなく、GetDC()で取ってきて直接書き込んでいたと思います。 ためしに UpdateWindow() を呼び出さない形にしてみてはいかがでしょう? UpdateWindow() はWindow作成時の一度しか明示的な呼び出しは不要だったはずです あとWM_SIZEとかリサイズ周りの時? Zバッファというは、スプライトの描画を後ろから書いていくことで 重なったときに全面のスプライトを表示するという意味です。 WIN32APIでも、マスク画像を準備してあげることで透過スプライトを出力できるのでてっきりやっているものだと。。

79562
質問者

お礼

返信ありがとうございます。 なんとか出きるようになりました。 TransparentBlt()関数使っていたりしてたらいつの間にか出来てました。 お騒がせしました!点数は配分するので!

その他の回答 (3)

回答No.3

ダブルバッファリングの基本は、書き直しと一括転送です。 気になったのは >オフスクリーンはorで切り取って、 >次にandで切り取ったのを背景に貼り付けるような作りになっています。 >そしてPAINTで表画面に貼り付ける感じになっております。 の部分ですね。 プライマリの画面から一瞬でもスプライトが消えていたりしませんか? 消えちゃうとカクカクとした感じになったりします。 あとは、単純に描画回数が多すぎて処理落ちしているケースもありますけど。。 コーディングしていたのはずっと昔なので関数とか用語は忘れちゃいましたけど、 ダブルバッファリングの流れを簡単にかくとこんな感じです。 1. 初期化用の背景バッファ作成 2. バックバッファ(オフスクリーン)とプライマリバッファ(普通のDC)作成 3. オフスクリーンを背景バッファで塗りつぶし 4. オフスクリーンにすべてのスプライト書き直し 5. プライマリバッファに全画面転送 6. 「3.」へもどる 昔はVGAが遅かったのとスプライトが少なかったため、 オフスクリーンの部分的に消し描きなんてのもしていましたが 今のご時世ならスプライトも多いでしょうから一度クリアして書き直した方が早いです。 (Zバッファが効くので都合もよい)

79562
質問者

お礼

>プライマリの画面から一瞬でもスプライトが消えていたりしませんか? >消えちゃうとカクカクとした感じになったりします。 >あとは、単純に描画回数が多すぎて処理落ちしているケースもありますけど。。 画面からは消えていません。カクカクしたままです。

79562
質問者

補足

返信ありがとうございます。 >1. 初期化用の背景バッファ作成は、 // 背景表示ブラシ作成(背景色の設定) g_hbrBg = CreateSolidBrush(RGB(255,255,255)); >2. バックバッファ(オフスクリーン)とプライマリバッファ(普通のDC)作成は、 // オフスクリーンの作成 hdc = GetDC(hWnd); g_hdcOff = CreateCompatibleDC(hdc); hbmp = CreateCompatibleBitmap(hdc,CLIENT_WIDTH,CLIENT_HEIGHT); ReleaseDC(hWnd, hdc); SelectObject(g_hdcOff,hbmp); DeleteObject(hbmp); >3. オフスクリーンを背景バッファで塗りつぶしは、 // 画面消去 HBRUSH oldbr = (HBRUSH)SelectObject(g_hdcOff, g_hbrBg); Rectangle(g_hdcOff, -1, -1, CLIENT_WIDTH+1, CLIENT_HEIGHT+1); SelectObject(g_hdcOff, oldbr); で >4. オフスクリーンにすべてのスプライト書き直し void DrawImgMask(int idx,int x,int y,int sx,int sy,int w,int h,int mx,int my) { BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],mx,my,SRCPAINT); BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],sx,sy,SRCAND); } 、>5. プライマリバッファに全画面転送は、 case WM_PAINT: hdc = BeginPaint(hWnd,&ps); // オフスクリーンからウィンドウに転送 BitBlt(hdc,0,0,CLIENT_WIDTH,CLIENT_HEIGHT,g_hdcOff,0,0,SRCCOPY); EndPaint(hWnd,&ps); return 0; ですよね?これでもカクカクが取れません。なにか間違っているのでしょうか? >今のご時世ならスプライトも多いでしょうから一度クリアして書き直した方が早いです。 (Zバッファが効くので都合もよい) は多分DirectXですよね。WIN32APIにもあるのでしょうか? 回答お待ちして居ります。

回答No.2

g_hdcOffはどのように得ていますか? g_hdcOff=CreateCompatibleDC(NULL); のように、バックバッファ用にデバイスコンテキストを作成しているのであれば、良いと思うのですが、コールバック関数の引数のウインドウハンドルから得ていると、直接windowに表示してしまっていることになります。その場合、画面がちらつきます。

79562
質問者

お礼

コールバック関数のWM_CREATEは次のようになっております。 case WM_CREATE: // オフスクリーンの作成 hdc = GetDC(hWnd); g_hdcOff = CreateCompatibleDC(hdc); hbmp = CreateCompatibleBitmap(hdc,CLIENT_WIDTH,CLIENT_HEIGHT); ReleaseDC(hWnd, hdc); SelectObject(g_hdcOff,hbmp); DeleteObject(hbmp); SetTimer(hWnd,NULL,1000,NULL); return 0; これは、 >コールバック関数の引数のウインドウハンドルから得ていると、直接windowに表示してしまっていることになります。その場合、画面がちらつきます これは、hdc = GetDC(hWnd); こういうことを指しているのでしょうか? 回答お待ちしております。

79562
質問者

補足

g_hdcOff = CreateCompatibleDC(hdc); の所をhdcをNULLに変えてやりましたが、カクカクしたままです。

回答No.1

コードはほとんどみていません。 一昔前の2Dゲームとかだと、「ダブルバッファリング」という手法を 利用していました。 これは、表示しようとする画面を裏で作成しておいて いっぺんに表示するという方法です。 こうすることで、ちらつきや目に残る残像を減らすことができます。 オフスクリーンという単語が入っているので 使っているようにも見えたのですけど なんか、オフスクリーンを直接表にしていたりしませんかね? 本当なら、オフスクリーンの出力がメッセージループの アイドル処理部分とかにあるはずなんだけど見あたらないですね。 あと余談ですが キャラの座標移動と描画はイベントを分けた方がいいと思いますよ。

79562
質問者

お礼

if(team>=1000) { before_time=timeGetTime(); frame=0; } frame++;    <ーーここ のところにDraw();が入ります。入れるのを忘れました。 それでもカクカクが消えません >オフスクリーンという単語が入っているので >使っているようにも見えたのですけど >なんか、オフスクリーンを直接表にしていたりしませんかね? void DrawImgMask(int idx,int x,int y,int sx,int sy,int w,int h,int mx,int my) { BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],mx,my,SRCPAINT); BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],sx,sy,SRCAND); } オフスクリーンはorで切り取って、 次にandで切り取ったのを背景に貼り付けるような作りになっています。 そしてPAINTで表画面に貼り付ける感じになっております。 なにか間違っているのでしょうか? 直すことはできないのでしょうか? 回答お待ちしております。

関連するQ&A