DirectX でゲームを作ってみよう
プログラム開発者向けのページです。
ここに書いてある内容は自分で試行錯誤した結果です。
決してここに書かれているやり方が正解とは限りませんので、
他のサイトなどもいろいろと調べてプログラムしてください。

第 4 回 カラーキーとクリッピング

§カラーキーで重ね合わせ
今回はカラーキーとクリッピングを解説します。
どちらも DirectDraw の基本的な機能なのでサクサクっと解説します。

まず、カラーキー。これは描画( Blt や BltFast )の際に指定した色を転送しない技術です。"抜き色"や"透過色"と呼ぶ場合もあります。
下の絵を見ればどういうものか一目瞭然ですね。この場合は、女の子の絵の紫色をカラーキーに設定しています。
+ =
このカラーキーを指定するには IDirectSurface7 の SetColorKey メソッドを使います。
そして描画の時に DDBLT_SRCCOLORKEY を指定します。これで指定した色が転送されずに上の絵のようにうまく背景と重なります。

DDCOLORKEY      ddck;

//  カラーキーを紫に設定します
ddck.dwColorSpaceLowValue = ddck.dwColorSpaceHighValue = RGB(255, 0, 255);
lpSurface->SetColorKey(DDCKEY_SRCBLT, &ddck);

上の例では RGB マクロを使って色を指定していますが、カラーキーの指定は透過色を直接 RGB 値で指定せずに、サーフェスから透過色にしたい色を抜き取ってその色を SetColorKey メソッドに渡す方が安全確実のようです。
これはビットマップ画像をサーフェスへ転送する際に画像のフォーマット( 色深度 )が変換された場合、指定した RGB 値と異なる色がサーフェスに書き込まれる事になり、結果として正しくカラーキーが設定されなくなることを防ぐためです。

よく使うのが以下のソースのように画像の左上の色を透過色に設定する方法です。
( この処理は InsideWindows 誌( 現在は休刊 )で Bio_100% が掲載したソースをベースにしています )
//  カラーキーの設定(ビットマップの左上の色をカラーキーとする)
COLORREF ReadLeftUpper(LPDIRECTDRAWSURFACE7 lpSurface)
{
    DDSURFACEDESC2  ddsd;
    DDCOLORKEY      ddck;
    HRESULT hres;

    //  ロックしてポインタを得ます
    ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2));
    ddsd.dwSize = sizeof(ddsd);
    do{
        hres = lpSurface->Lock(NULL, &ddsd, 0, NULL);
    }while(hres == DDERR_WASSTILLDRAWING);

    //  左上のピクセルカラーを得ます
    DWORD dw = CLR_INVALID;
    if(hres == DD_OK){
        dw  = *(DWORD *)ddsd.lpSurface;
        dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1;
        lpSurface->Unlock(NULL);
    }

    //  カラーキーに設定します
    ddck.dwColorSpaceLowValue = ddck.dwColorSpaceHighValue = dw;
    lpSurface->SetColorKey(DDCKEY_SRCBLT, &ddck);

    return dw;
}

§自力でクリッピング
続いてクリッピングの話です。
クリッピングとは画面の端にキャラクターを描画するときに、画面からはみ出す部分を描画しないようにする事です。この処理をしない場合、画面からはみ出した部分というのは画面外のエリア( =メモリ領域 )に画像を書き込むことになり、場合によってはメモリー保護違反で落ちます。

このクリッピングを実現するためにあるのが IDirectDrawClipper というインターフェースです。
これを使って画面サイズと同じ大きさをクリッピング領域として登録すると、Blt や BltFast での描画ではみ出す部分があってもはみ出す部分は描画しないように DirectDraw 側で処理してくれます。

しかし、この IDirectDrawClipper は処理速度が非常に遅いです。
IDirectDrawClipper を使った途端、フレームレートが下がります。
クリッピングの必要・不要に関わらずとにかくクリッピング処理が走るのが原因だと思います。

これではせっかく高速な DirectDraw も台無しですし、そもそもクリッピング程度の処理ならわざわざ IDirectDrawClipper を使わなくても簡単に実現できます。
Blt や BltFast で描画する際にあらかじめはみ出す部分を RECT から除いておけばいいだけなんでソースはこんな感じになります。

//  x  : 描画する X 座標
//  y  : 描画する Y 座標
//  rc : 描画するビットマップの矩形

//  画像の幅と高さを計算
int nWidth  = (rc->right - rc->left);
int nHeight = (rc->bottom - rc->top);

//  DirectDrawClipperを使わない クリップ処理
if(x < 左上X座標){
    rc.left += 左上X座標 - x;
    x = 左上X座標;
}
if(y < 左上Y座標){
    rc.top += 左上Y座標 - y;
    y = 左上Y座標;
}
if(x + nWidth > 右下X座標){
    rc.right -= (x + nWidth) - 右下X座標;
}
if(y + nHeight > 右下Y座標){
    rc.bottom -= (y + nHeight) - 右下Y座標;
}

//  カラーキー付き描画
lpBackSurface->BltFast(x, y, lpSurface, &rc, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);

§サンプルの解説
今回のサンプルは女の子が芝生の上にカラーキー付きで描画されるものです。

画面の中央に表示されている芝生の部分のみ描画されます。上下左右( 矢印キー )で女の子を移動させてください。芝生からはみ出す部分はクリップされます。

この sample3.lzh には実行ファイル( sample3.exe )とソースとヘッダー、画像ファイルが格納されています。コンパイルをする際には dxguid.lib と ddraw.lib をリンクしてください。
なお、このサンプルは ESC キーで終了します。

( 22.3KB )