メッセージループから描画する

画像から Sprite を切り出して、回転しながらアニメーションします。
DirectX のようにメッセージループから描画すると、非常に滑らかに回転できました。

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

プロジェクトの設定

  1. 広範囲に画像を扱うことが出来る Image Object Class を使用しています。
    Object Class のソースコードは Object Class Library から取得して下さい。
    Image.cpp をプロジェクトに追加して Image.h をイクルードします。
    timeGetTime() を使うので mmsystem.h をインクルードして winmm.lib をリンクして下さい。
    hBackDC, hBackBitmap は BackBuffer の領域です。
    Sprite は画像を切り出して回転する Image ObjectClass の定義です。
    rot は座標の回転角度で timeGetTime() を元に計算します。
    cnt は Sprite の番号を計算する領域で、小数点以下の計算を行います。
    no は Sprite の番号を設定する領域で、cnt を整数にして求めます。
        /***********************************************/
        /*★ 回転しながらアニメーション      前田 稔 ★*/
        /***********************************************/
        #define     NAME    "Rotate Anime"
        #include    <windows.h>
        #include    <mmsystem.h>
        #include    "Image.h"
    
        #pragma     comment(lib,"winmm.lib")
        #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
    
        IMAGE       *Sprite= NULL;      //IMAGE Object Class
        float       rot= 0.0f;          //回転係数
        float       cnt= 0.0f;          //Sprite の番号計算ワーク 
        long        no= 0;              //Sprite の番号    
        
  2. WinMain() ではウインドウを表示してメッセージループから描画関数を呼び出します。
    Render(); が画面を描画する関数です。
        int PASCAL  WinMain(HINSTANCE hInst, HINSTANCE, LPSTR,int nCmdShow)
        {   MSG         msg;
                    :
                    :
            ZeroMemory(&msg,sizeof(msg));
            while(msg.message!=WM_QUIT)
            {   if (PeekMessage(&msg,NULL,0U,0U,PM_REMOVE))
                {   TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else    Render();
            }
            return msg.wParam;
        }
        
  3. CALLBACK 関数の WM_CREATE: です。
    CreateBuf() で BACKBUF を生成します。
    次に IMAGE Object Class を生成して gif 画像をロードします。
    IMAGE Object Class を使用すると gif ファイルや jpeg ファイルを描画することが出来ます。
    7,1 は横に7枚並べた画像を切り出すパラメータです。
        switch(msg)
        {   case WM_CREATE:
                CreateBuf(hWnd);
                Sprite= new IMAGE(hWnd);
                Sprite->LoadFile("Girl.gif",7,1);
                break;
        
  4. WM_DESTROY: ではリソースを開放して下さい。
    描画する関数はメッセージループから直接呼び出すので CALLBACK の記述はこれだけです。
    メッセージループから直接呼び出す方法は DirectX で良く使われる方法です。
            case WM_DESTROY:
                SAFE_DELDC(hBackDC);
                SAFE_DELOBJ(hBackBitmap);
                SAFE_DELETE(Sprite);
                PostQuitMessage(0);
                return 0L;
        }
        
  5. 回転しながらアニメーションを行う Render() 関数です。
    timeGetTime() で時刻を取得して、回転角度を計算します。
    回転角度が前回と同じときは描画の必要がないので、そのままリターンします。
    cnt が Sprite 番号を小数点以下で計算する領域です。
    no が Sprite の番号で、cnt を元に0~6に設定します。
    Back Buffer をクリアして、Sprite->ShowRot() で Back Buffer に描画します。
    BitBlt() で FrontBuffer に一括して転送することにより「チラツキ」を無くします。
        VOID  Render()
        {   HDC     hDC;
            RECT    rc;
            int     nowTime,wl,hl;
            float   wk;
    
            nowTime= timeGetTime();
            wk= (float)((nowTime/50)%360);
            if (wk==rot)    return;
            rot= wk;
            cnt+= 0.3;
            no= (int)cnt%7;
            GetClientRect(g_hWnd,&rc);
            //Back Buffer をクリアして Sprite を回転しながら描画
            FillRect(hBackDC,&rc,(HBRUSH)GetStockObject(LTGRAY_BRUSH));
            Sprite->ShowRot(hBackDC,no,240L,120L,rot);
            //FrontBuffer に一括して転送
            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);
        }
        
  6. ビルドに続いて実行をすると DirectX のように全く「チラツキ」が無く描画されることを確かめて下さい。

【演習】

  1. チラツキを無くすために BackBuffer を生成して下さい。
  2. IMAGE Object Class に代わる Sprite を切り出して、画像を回転できる Class を作成して下さい。
  3. new で Object Class を生成します。
    LoadFile() で画像を入力するのですが、このとき画像を切り出す「横の分割数と縦の分割数」をパラメータで渡します。
  4. IMAGE を描画するメンバ関数です。
    BackBuffer に対して no の画像を rot で回転して描画します。
    Sprite->ShowRot(hBackDC,no,240L,120L,rot);

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