BackBuffer を使う

BackBuffer を生成して「画像のちらつき」を抑えて回転しながら描画します。
DirectX では常識ですが、Windows API で対処する方法を説明します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プロジェクトの設定

  1. 「画像のちらつき」を抑えて描画する方法を考えます。
    1. BackBuffer を生成して、画面全体のイメージを作成してから一括して FrontBuffer に転送します。
      これで画面を切り替えるロス時間を最小限に抑えられます。
    2. WM_PAINT: から呼び出すとちらつくので WM_TIMER: から直接呼び出します。
      WM_PAINT: を受けて描画する方法ではオーバーヘッドが避けられません。
  2. 自前で作成した BMP Object Class と Sprite Object Class を使っています。
    背景画像は BMP Object Class で描画します。
    騎士の画像は Sprite Object Class で透明色を設定して回転しながら描画します。
    BMP, Sprite Class に代えて、広範囲に画像を扱うことが出来る Image Object Class を使うことも出来ます。
    Object Class のソースコードは Object Class Library から取得して下さい。
  3. Sprite.cpp と Bmp.cpp をプロジェクトに追加して Sprite.h と Bmp.h をインクルードします。
    ID_TIMER はタイマのIDです。
    hBackDC と hBackBitmap は Back Buffer の領域です。
    rot は座標の回転角度で、タイマ割り込みでアップされます。
    Sprite は Sprite Object Class の定義です。
    BMP は BMP Object Class の定義です。
        #include    <windows.h>
        #include    "Sprite.h"
        #include    "Bmp.h"
        #define     ID_TIMER    (WM_APP + 0)
        #define     SAFE_DELETE(p)  { if (p) { delete (p); (p)=NULL; } }
        #define     SAFE_DELDC(p)   { if (p) { DeleteDC (p);   (p)=NULL; } }
        #define     SAFE_DELOBJ(p)  { if (p) { DeleteObject(p); (p)=NULL; } }
    
        HWND        g_hWnd;
        HDC         hBackDC=NULL;       //Back Buffer DC
        HBITMAP     hBackBitmap=NULL;   //Back Buffer HBitMap
    
        float       rot= 0.0f;          //回転係数
        SPRITE      *Sprite= NULL;      //SPRITE Object Class
        BMP         *Bmp= NULL;         //BMP Object Class
        
  4. WinMain() ではウインドウを表示するだけです。
  5. CALLBACK 関数の WM_CREATE: です。
    CreateBuf() で Back Buffer を作成します。
    BMP Object Class をインスタンス化して背景画像をロードします。
    SPRITE Object Class をインスタンス化して画像をロードします。
    タイマを起動して、WM_TIMER: で回転角度(rot) をアップして描画します。
            case WM_CREATE:
                CreateBuf(hWnd);
                Bmp= new BMP(hWnd);
                Bmp->Load("ayu.bmp");
                Sprite= new SPRITE(hWnd);
                Sprite->Load("Kishi.bmp");
                SetTimer(hWnd,ID_TIMER,300,NULL);
                break;
        
  6. WM_TIMER: で回転角度(rot) をアップして Render() 関数で描画します。
    WM_PAINT: から呼び出すとちらつくので、ここから Render() を呼び出します。
            case WM_TIMER:
                rot+= 3.0f;
                if (rot>360.0f) rot-= 360.f;
                Render();
                break;
        
  7. WM_DESTROY: ではタイマを停止してリソースを開放します。
            case WM_DESTROY:
                KillTimer(hWnd,ID_TIMER);
                SAFE_DELDC(hBackDC);
                SAFE_DELOBJ(hBackBitmap);
                SAFE_DELETE(Sprite);
                SAFE_DELETE(Bmp);
                PostQuitMessage(0);
                return 0L;
        
  8. Back Buffer を生成する関数です。
    表画面と共通の設定で、640*480 の BackBuffer を生成します。
        void  CreateBuf(HWND hWnd)
        {   HDC     hDC;
            hDC= GetDC(hWnd);
            hBackDC= CreateCompatibleDC(hDC);
            hBackBitmap= CreateCompatibleBitmap(hDC,640,480);   //表画面と同じ画面生成
            SelectObject(hBackDC,hBackBitmap);                  //DC と画面本体を関連付ける
            ReleaseDC(hWnd,hDC);
        }
        
  9. 描画処理を行う Render() 関数です。
    BackBuffer に画面イメージを作成してから一括して FrontBuffer に転送します。
        VOID  Render()
        {   HDC     hDC;
            RECT    rc;
            int     wl,hl;
    
            GetClientRect(g_hWnd,&rc);
            //裏画面を黒でクリア
            FillRect(hBackDC,&rc,(HBRUSH)GetStockObject(BLACK_BRUSH));
            Bmp->Show(hBackDC,0,0);
            Sprite->Show(hBackDC,0,60,30,rot);
            //表画面に裏画面をコピー
            wl= rc.right-rc.left;
            hl= rc.bottom-rc.top;
            hDC= GetDC(g_hWnd);
            BitBlt(hDC,0,0,wl,hl,hBackDC,0,0,SRCCOPY);
            ReleaseDC(g_hWnd,hDC);
        }
        

【演習】

  1. BMP Object Class と Sprite Object Class または、これに代わる関数を作成して下さい。
  2. プログラムを完成させて下さい。
  3. 画面がちらつかずに、スムースに描画されることを確認して下さい。

超初心者のプログラム入門(Win32API C++)