プログラミングのメモ帳(C/C++/HSP)

日々のプログラミングで気づいた点や小技集を紹介します。(Windows 10/XP/Vista、VC2017、HSP)

ダブル・バッファリングの方法(1)

2010年08月12日 19時48分00秒 | グラフィック関連
簡単なアニメーションやゲームなどを作成したとき、キャラクタの数が多いと画面がちらつく事があります。 これは WM_ERASEBKGND メッセージで背景を描画してから、WM_PAINT メッセージで図形などを描画するためです。 通常は、この仕組みで先にキャラクタの背景が描画され、その後にキャラクタを描画するからゲームなどで キャラクタを連続的に描画すると毎回キャラクタが背景と共に消去されます。これがちらつきの原因です。 それでは、どうやったらちらつきをなくせるのか。 その方法の一つに[ダブル・バッファリング]という方法があります。 WM_PAINT メッセージの処理だけ紹介します。 (1)裏画面であるオフスクリーンにアニメーションなどの背景とキャラクタを全て描画。 (2)表画面であるクライアント領域にオフスクリーンの画像を一気に描画。 なお、WM_ERASEBKGND メッセージは処理しないようにします。 これを忘れると背景が毎回消去されてちらつきを防げません。 それでは具体的なプロシージャの記述を紹介します。

サンプル

// break付きのキーワード
#define CASE            break;case
#define DEFAULT         break;default

// オフスクリーンの画面サイズ
#define MAX_WIDTH       (640)
#define MAX_HEIGHT      (480)

// ウインドウ・プロシージャの関数
LRESULT CALLBACK mainWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    static HBITMAP  hBitmap;    // ビットマップ
    static HDC      hMemDC;     // オフスクリーン
    static UINT     saveX;
    static UINT     saveY;
    
    switch ( uMsg ){
        CASE WM_CREATE:
        {
            HDC hDC;
            
            // DCコンパチブルの作成
            hDC         = GetDC( hWnd );
            hMemDC      = CreateCompatibleDC( hDC );
            hBitmap     = CreateCompatibleBitmap( hDC, MAX_WIDTH, MAX_HEIGHT );
            SelectObject( hMemDC, hBitmap );
            SelectObject( hMemDC, GetStockObject(DC_PEN) );
            SelectObject( hMemDC, GetStockObject(DC_BRUSH) );
            ReleaseDC( hWnd, hDC );
        }
        CASE WM_CLOSE:
            // DCコンパチブルの破棄
            DeleteDC( hMemDC );
            DeleteObject( hBitmap );
            DestroyWindow( hWnd );
        CASE WM_DESTROY:
            PostQuitMessage( 0 );
        CASE WM_PAINT:
        {
            PAINTSTRUCT     ps;
            HDC             hDC;
            
            // DCコンパチブルの描画
            hDC = BeginPaint( hWnd, &ps );
            BitBlt( hDC, 0, 0, MAX_WIDTH, MAX_HEIGHT, hMemDC, 0, 0, SRCCOPY );
            EndPaint( hWnd, &ps );
        }
        CASE WM_ERASEBKGND:
            // 何も処理しない⇒塗り潰した
            return 1;
        CASE WM_LBUTTONDOWN:
        case WM_LBUTTONDBLCLK:
        {
            UINT cx = (SHORT)LOWORD(lParam);
            UINT cy = (SHORT)HIWORD(lParam);
            
            if ( GetKeyState(VK_SHIFT) < 0 ){
                cx = saveX += 16;
                cy = saveY += 16;
            }
            SetDCPenColor(   hMemDC, RGB(0xFF,0xFF,0x00) );
            SetDCBrushColor( hMemDC, RGB(0x00,0x00,0xFF) );
            Rectangle( hMemDC, cx, cy, (cx + 16*5), (cy + 16*3) );
            InvalidateRect( hWnd, NULL, FALSE );
        }
        CASE WM_RBUTTONUP:
            saveX = 0;
            saveY = 0;
            PatBlt( hMemDC, 0, 0, MAX_WIDTH, MAX_HEIGHT, BLACKNESS );
            InvalidateRect( hWnd, NULL, FALSE );
        DEFAULT:return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }
    return 0;
}

解説

WM_CREATE でオフスクリーンであるDC(デバイス・コンテキスト)コンパチブルを作成します。 WM_CLOSE でオフスクリーンである hMemDC、hMemDC に関連付けた hBitmap を破棄します。 WM_PAINT でオフスクリーンである hMemDC を BitBlt 関数で一気にコピーします。 WM_ERASEBKGND の処理を無効にするため何も記述しません。そして処理したから 1 を返す。 これで常にオフスクリーンである hMemDC の内容が表画面(クライアント領域)にコピーされます。 ちなみにオフスクリーンである hMemDC には別のタイミングで背景やキャラクタを描画します。 ゲームの場合は、一定の間隔であるタイマー(スレッド)で背景やキャラクタを描画します。

その他

上記では WM_LBUTTONDOWN と WM_LBUTTONDBLCLK で長方形の図形を描画してます。 [SHIFT]キーを押しながらクリックすると左上隅から順番に長方形を描画します。 WM_RBUTTONUP でオフスクリーンの画面を全て消去してます。

コメント    この記事についてブログを書く
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« MP3専用プレイヤー(タイプC) ... | トップ | ダブル・バッファリングの方... »
最新の画像もっと見る

コメントを投稿

グラフィック関連」カテゴリの最新記事