描画処理の基礎

この章で使用するプログラム

描画処理手順

02.基礎知識の(5)DirectX Graphicsプログラミングの基礎で学んだとおり、ダブル・バッファリングを使って画像を描画するには、次のようにプログラムすればよい。

gl_lpD3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ZRGB(255,255,255), 0.0, 0);
gl_lpD3ddev->BeginScene();
//
// ここに描画などの処理を入れる
//
gl_lpD3ddev->EndScene();
gl_lpD3ddev->Present(NULL, NULL, NULL, NULL);

では、これらの処理をどこに組み込めばよいかを考える。

プログラムへの組み込みを考える

通常、ゲームは場面によって描画する画像が違う。例えばスタート画面ではタイトルやメニューを表示し、ゲーム画面ではキャラクタを表示する。
今まで作ってきたプログラムは、場面によって柔軟にプログラムが行えるよう、状態別処理を考えてプログラムされている。ということは、スタート処理用関数(StartFrame関数)や、ゲーム処理用関数(GameFrame関数)に上記プログラムを組み込めば、状態(場面)ごとに描画するものを好きなように変更できる。

//-----------------------------------------------------------------------------
// 関数名 : StartFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void StartFrame(void)
{
    // 画面のクリア
    gl_lpD3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255,255,255), 0.0, 0);
    // シーン開始
    gl_lpD3ddev->BeginScene();
    //
    // ここに描画などの処理を入れる
    //
    // シーン終了
    gl_lpD3ddev->EndScene();
    // フリップ
    gl_lpD3ddev->Present(NULL, NULL, NULL, NULL);

    // リターンキーが押されたら、ゲーム開始
    if ( gl_KeyTbl[VK_RETURN] & 0x80 ) g_FrameNo = GAME_INIT;

}

《解説》

しかし、この方法には「無駄」があることが分かるだろうか?確かに状態によって描画したい画像は変わる。しかし、画面のクリアやシーンの開始/終了、フリップ処理は、どんな画像を描画しても必ず行わなければならない処理である。つまり、状態別処理で変化するのは描画する画像のみであり、他の処理はどんな場面でも同じであるということが分かる。

よって、無駄を省くには次のようにプログラムするべきである。

UpdateFrame関数

//-----------------------------------------------------------------------------
// 関数名 : UpdateFrame()
// 機能概要: ゲームメイン処理
//-----------------------------------------------------------------------------
BOOL UpdateFrame(void)
{

    /* 現在のキー情報を取得 */
    if ( !GetKeyboardState(gl_KeyTbl) ) {
        MessageBox(hWnd, "キー情報の取得に失敗", "ERROR", MB_OK);
        return (FALSE);
    }

    /* 画面のクリア */
    gl_lpD3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255,255,255), 0.0, 0);
    /* シーン開始 */
    gl_lpD3ddev->BeginScene();

    /* 処理の振り分け */
    switch ( g_FrameNo ) {
        case START_INIT:
            // スタート画面を表示する前に実行する初期化関数を実行
            StartInit();
        case START_FRAME:
            // スタート画面表示処理関数を実行
            StartFrame();
            break;
        case GAME_INIT:
            // ゲーム画面を表示する前に実行する初期化関数を実行
        case GAME_FRAME:
            // ゲーム画面表示処理関数を実行
            break;
        default:
            MessageBox(hWnd, "g_FrameNoの値が例外です。", "ERROR", MB_OK);
            return (FALSE);
    }

    /* シーン終了 */
    gl_lpD3ddev->EndScene();
    /* フリップ */
    gl_lpD3ddev->Present(NULL, NULL, NULL, NULL);

    return (TRUE);
}

《解説》

StartFrame関数

//-----------------------------------------------------------------------------
// 関数名 : StartFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void StartFrame(void)
{
    //
    // スタート処理に必要な画像の描画を行う
    //

    // リターンキーが押されたら、ゲーム開始
    if ( gl_KeyTbl[VK_RETURN] & 0x80 ) g_FrameNo = GAME_INIT;

}

《解説》

※上記修正を行い、画面が白色でクリアされるかどうかを確かめよう!!

さらに考える(参考)

キャラクタは本来、上下左右に動き回る。表示した画像を動かすにはゲーム・ループのたびに表示座標を変化させてやればよいことは想像できる(キャラクタの移動処理は別項で説明)。移動処理はもちろん状態別処理(StartFrame関数、GameFrame関数など)で行うが、先に教えた方法では、シーンの開始と終了の間に移動処理が入ってしまう
 シーンの開始と終了の間に描画処理以外の処理を入れなくない場合は、シーンの開始・終了はUpdateFrameではなく、状態別の処理内で描画処理を行う前後に入れればよい。



NEXT(DirectX Graphicsでの2D表示)