iPad専用アクションゲーム「スライムは投げる」

最近のエントリー

サンプルコードのダウンロード

では、まずサンプルコードをダウンロードし、解凍してください。
私はVisual C++6.0でコンパイルしているので、をお持ちの方はVisual C++でプロジェクトファイルを開いてください(「ddraw_12.dsw」をダブルクリックすれば開けます)。
圧縮ファイルに含まれる「ddraw_12.exe」をダブルクリックし、実行してみてください。
どうでしょう?画面が切り替わり、フルスクリーン化し、流れ星のようなものが表示されると思います。

というわけで、今回はサーフェイスへ直接アクセスする方法を書いていきたいと思います。

ダイレクトアクセス

まずサーフェイスへ直接アクセスするとはどういう事なのかを簡単に説明します。
サーフェイスは、ビデオメモリもしくはシステムメモリ内に作成されます。
メモリは1次元配列で管理されており、DirectDrawでは、サーフェイスが存在しているメモリの先頭アドレスを取得する事が出来ます。
ですから、このアドレスを使って、1次元配列と同じようにサーフェイス扱う事ができると言うわけです。
と言っても、使用するのは「点を描く」と言った処理程度でしょう。

では、実際にサーフェイスへの先頭アドレスを取得する方法を記述します。
サンプルコードを見てみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unsigned char *p;
DDSURFACEDESC desc;
 
<略>
     
    //サーフェイスをロック
    ZeroMemory(&desc,sizeof(DDSURFACEDESC));
    desc.dwSize=sizeof(DDSURFACEDESC);
    lpBack->Lock(NULL,&desc,DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR,NULL);
    p=(unsigned char *)desc.lpSurface;
 
    //データを書き込む
    for(i=0;i<STAR;i++){
        p[x[i]+(int)y[i]*desc.lPitch]=sp[i];
        if((y[i]+=sp[i]/4.0f)>=480)
            y[i]-=480; 
    }
 
    //ロックを解除
    lpBack->Unlock(desc.lpSurface);

今回はサーフェイス作成時にも使用した構造体「DDSURFACEDESC」型を使用するので、最初にこれを初期化しておきます(ZeroMemory等)。
そうしたら、サーフェイスのメンバである「Lock」という関数を呼び出します。
サーフェイスをロックする事により、サーフェイスへの先頭アドレスが取得出来ます。

では、Lockの構文を書いておきます。

書式 HRESULT Lock( LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent );
lpDestRect ロックしたい領域を識別するRECT構造体のアドレス。NULLであれば、全サーフェスがロックされる
lpDDSurfaceDesc サーフェスについての情報を格納する DDSURFACEDESC 構造体へのポインタ。
dwFlags
DDLOCK_SURFACEMEMORYPTR 指定した矩形の先頭への有効なメモリ ポインタを返さなければならないことを表すフラグ。矩形が指定されない場合、一番上のサーフェスへのポインタが返される。
DDLOCK_WAIT 何らかの原因によってロック(アドレス取得)が出来ない場合に、出来るまで待つという指定
hEvent 現在は使用していないのでNULLを指定しなければならないらしい
戻り値 成功した場合はDD_OKが返ってくるらしい。

こうすると、DDSURFACEDESC構造体のメンバであるlpSurfaceに、サーフェイスの先頭アドレスが格納されるので、そのアドレスをあらかじめ用意しておいたunsigned char型(256色なので)のポインタ変数pへ代入してやります。

では、このポインタの扱い方(サーフェイスへのアクセス)を説明します。
前にも記述しましたが、メモリは1次元配列で構成されているので、「座標X、Yへ5を代入」という場合にp[y][x]=5;とは書けません。
メモリは座標(0,0)から右へ順に行き、突き当たったらY座標が1ドット下へいき、X座標が0になる・・・を繰り返す形で存在しています(謎。
ですから、p[0]が(0,0)になります。
それで、本来ならばp[640]が座標(0,1)つまりp[x+y*640]というように考えるはずなのですがビデオカードによっては、自動的にサーフェイスの横幅を少し増やして、そこをキャッシュとして使用するものがあります。
そして、DDSURFACEDESC構造体のメンバであるlPitchには、このキャッシュの部分を含めた正確な横幅が格納されています。
ですから、サーフェイスメモリへアクセスするにはp[x+y*desc.lPitch]と表すのが正確となります。

一通り、アクセスし終えたら、ロックを解除してやる必要があります。

書式 HRESULT Unlock( LPVOID lpSurfaceData );
lpSurfaceData Lockによって取得され、アンロックすべきサーフェスのアドレス。このパラメータは、対応する Lock 呼び出しで lpDestRect パラメータに NULL を渡して全サーフェスをロックした場合に限り、NULL とする。

ちなみに、全体をロックしなかった場合は、desc.lpSurfaceを引き渡す事になります。

戻り値 成功した場合はDD_OKが返ってくるらしい。

これで、一通り終わりです。
実際にサーフェイスへアクセスするのは、ロックからアンロックする間だけにしてください。
ロックしてサーフェイスポインタを取得し、直ぐにアンロックしてしまった後に、サーフェイスへアクセスする事は一応出来ますが、
ビデオカードによっては上手くいかない場合があるので、止めた方がいいです(確認済み)。
それと、ロックという処理は、結構遅いので1ループに1度というのが理想です。
1ループに何度もロック、アンロックを繰り返すのは速度低下の原因となります。
それから、ロックしたあと、アンロックしないと、そのサーフェイスへの書き込み(GDIやBltFast等による転送)が不自由になるので注意してください。

ちなみに、ビデオメモリ内のサーフェイスへのアクセス(読み込み&書き込み)は非常に遅いので、一度システムメモリ内へコピーし、システムメモリ内をいじってから、ビデオメモリ内のサーフェイスへ転送する方がいいかもしれません。

サンプルコードのダウンロード

では、まずサンプルコードをダウンロードし、解凍してください。
私はVisual C++6.0でコンパイルしているので、お持ちの方はVisual C++でプロジェクトファイルを開いてください(「ddraw_11.dsw」をダブルクリックすれば開けます)。
圧縮ファイルに含まれる「ddraw_11.exe」をダブルクリックし、実行してみてください。
どうでしょう?画面が切り替わり、フルスクリーン化し、SLIME(?)と書かれた画像がタイル状に敷き詰められ、どんどん小さくなりながら左上へスクロールするのが解ると思います。

というわけで今回は、DirectDraw側で画像転送時にクリッピングしたいと思います。

DirectDrawクリッパー

DirectDrawにクリッピングをさせるには、DirectDrawクリッパーというものを使用します。
クリッパー作成は、自作関数「StartDirectDraw」内に追加してあります。

1
2
3
4
5
6
7
8
9
10
11
12
//グローバル変数宣言
LPDIRECTDRAWCLIPPER lpClipper=NULL; //クリッパーオブジェクト
 
 
    //クリッパーの設定
    if(lpDD->CreateClipper(0,&lpClipper,NULL)!=DD_OK)
        return FALSE;
    if(lpClipper->SetHWnd(0,hw)!=DD_OK)
        return FALSE;
    if(lpBack->SetClipper(lpClipper)!=DD_OK)
        return FALSE;

意外とソースは短いですかねぇ(^^;。
では、順番に説明していきたいと思います。
まず、DirectDrawオブジェクト(lpDD)のメンバ関数である「CreateClipper」を呼び出し、DirectDrawクリッパーオブジェクトを作成します。
構文は以下の通りです。

書式 HRESULT CreateClipper( DWORD dwFlags, LPDIRECTDRAWCLIPPER FAR *lplpDDClipper, IUnknown FAR *pUnkOuter );
dwFlags このフラグは使用していないらしく、0を指定する必要があるらしい
*lplpDDClipper 新しいDirectDrawClipperオブジェクトを示すポインタへのポインタ。
例によって、LPDIRECTDRAWCLIPPER型自体がポインタ型になっているので、「ポインタへのポインタ」と言っています(たぶん)。
*pUnkOuter 将来拡張のために使用するらしいので今はNULLを指定。
戻り値 成功した場合はDD_OKが返ってくるらしい。

これで、DirectDrawClipperオブジェクトが出来ました。
で次にこいつのメンバ関数である「SetHWnd」を呼び出します。
これは、クリッピング範囲をウィンドウハンドルを参照して設定するというものです。
つまり、プライマリサーフェイスと同じ範囲に設定されるわけです。

というわけで、構文。

書式 HRESULT SetHWnd( DWORD dwFlags, HWND hWnd );
dwFlags このフラグは使用していないらしく、0を指定する必要があるらしい
hWnd クリッピング情報を含むウィンドウ ハンドル
戻り値 成功した場合はDD_OKが返ってくるらしい。

これで、クリッピング範囲を設定し終えたら、次にサーフェイスのメンバ関数である「SetClipper」を呼び出します。
SetClipperによってサーフェイスへクリッパーを関連付けさせれば、そのサーフェイスに対し、クリッピングが可能となります。
一応構文も載せておきます。

書式 HRESULT SetClipper( LPDIRECTDRAWCLIPPER lpDDClipper );
lpDDClipper 関連づけさせるクリッパー。ここを0にしてやると、今まで関連付けられていたクリッパーが外されるらしい(未確認)。
hWnd クリッピング情報を含むウィンドウ ハンドル
戻り値 成功した場合はDD_OKが返ってくるらしい。

設定は以上です。
このクリッパーは、関連付けさせたサーフェイスへ、サーフェイスのメンバ関数「Blt」(自作関数の方では無いので注意してください)で転送した場合にのみ有効となります。
それと、クリッパーが関連づけさせられているサーフェイスへBltFast(自作関数Blt)による転送は出来ません。

今回は、クリッパーをバックバッファ(lpBack)に関連付けさせていますが、ここへBlt(自作関数BltStretch)による転送をしたときにだけ、クリッピングされます。
まぁ、このクリッパーによるクリッピングは私の環境では速度が遅いので使用していません。
ですから詳しいこたぁ、よくわかりましぇ~ん。

後始末

いつも通り、終了する時にはlpClipper->Release();を実行してやらなければなりません。
マクロ使用ではRELEASE(lpClipper);ですね。

サンプルコードのダウンロード

では、まずサンプルコードをダウンロードし、解凍してください。
私はVisual C++6.0でコンパイルしているので、お持ちの方はVisual C++でプロジェクトファイルを開いてください(「ddraw_10.dsw」をダブルクリックすれば開けます)。
圧縮ファイルに含まれる「ddraw_10.exe」をダブルクリックし、実行してみてください。
どうでしょう?画面が切り替わり、フルスクリーン化し、SLIME(?)と書かれた画像がタイル状に敷き詰められ、左上へスクロールするのが解ると思います。

というわけで今回は、自分で画像転送時にクリッピングしたいと思います。

プログラム側でクリッピング

今回のサンプルの注目すべき点は「スクロール」では無く、画面の端っこのほうに表示している「完全でない画像」です(謎。
今まで、BltFast関数を使用した自作関数Bltでは、転送先で少しでも画像がはみ出てしまうと、何も表示されなくなってしまいます。
で、画面から少しでもはみ出してしまった場合、はみ出ていない部分を表示させる処理がクリッピングと言われています(たぶん)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int cx1=0,cy1=0,cx2=640,cy2=480;    //自前クリッピング処理に使用する範囲
 
//----------[ クリッピング付きのデータ転送 ]----------------------------------------------------
void BltClip(LPDIRECTDRAWSURFACE saki,int sx,int sy,int width,int height,
         LPDIRECTDRAWSURFACE moto,int mx,int my,char trans){
    DWORD key[2]={DDBLTFAST_NOCOLORKEY,DDBLTFAST_SRCCOLORKEY};
    if(sx<cx1){
        mx+=cx1-sx;
        width-=cx1-sx;
        sx=cx1;
        if(width<1)
            return;
    }
    if(sy<cy1){
        my+=cy1-sy;
        height-=cy1-sy;
        sy=cy1;
        if(height<1)
            return;
    }
 
    if(sx>=cx2 || sy>=cy2)
        return;
    if(sx+width>cx2)
        width=cx2-sx;  
    if(sy+height>cy2)
        height=cy2-sy;
    RECT rec={mx,my,mx+width,my+height};
    saki->BltFast(sx,sy,moto,&rec,key[trans] | DDBLTFAST_WAIT);
}
//----------[ クリッピングする範囲を変更 ]------------------------------------------------------
void SetClipArea(int x,int y,int width,int height){
    cx1=x;
    cy1=y;
    cx2=x+width;
    cy2=y+height;
}

この自作関数では、転送前に転送先の座標と縦幅、横幅からはみ出している部分を検出し、うまく表示出来るように値を変更しています。
まぁ、見りゃ解りますね(^^;
ついでなのでクリッピングの範囲を変更出来るようにもしておきました。この自作関数では、どこのサーフェイスでも適応されます。

使用例はこんな感じでしょうかね

1
2
3
4
5
6
//クリッピング範囲が全画面(0,0)~(639,479)の場合
SetClipArea(0,0,640,480);   //width,heightは0(ゼロ)も1ドットと考えるため639,479にはならない
 
//転送
BltClip(lpBack,-5,0,32,32,lpWork,0,0,0);
Flip();

今回は、プログラム側でクリッピングを行いました。
その理由として、DirectDraw側でクリッピングを行うと、家の環境で試したところ、速度がガタ落ちしたからです。
プログラムで処理した方がよっぽど早いですし、応用も利きます。
ただし、この方法にも欠点はあります。それは拡大縮小を行った時にクリッピング出来ないという点です。
というわけで、次回では一応DirectDrawでのクリッピング方法を説明します(私は使ってませんけど)。

サンプルコードのダウンロード

では、まずddraw_09サンプルコードをダウンロードし、解凍してください。
私はVisual C++6.0でコンパイルしているので、
お持ちの方はVisual C++でプロジェクトファイルを開いてください(「ddraw_09.dsw」をダブルクリックすれば開けます)。
圧縮ファイルに含まれる「ddraw_09.exe」をダブルクリックし、実行してみてください。
どうでしょう?画面が切り替わり、フルスクリーン化し、スライム(?)らしき絵が段々小さくなっていくのが解ると思います。

というわけで今回は、サーフェイスの全消去と、拡大縮小付きの転送について書いていきたいと思います。

サーフェイスの全消去

シューティングゲーム等では、バックバッファを一度全て消し、そこに全てキャラクターを配置して、フリップという処理を繰り返し行います(たぶん)。
このとき、もしバックバッファを消さないと、前に書き込んだ分が残ったままになってしまいます(まぁ、当たり前の話なのですが(^^;)。
DirectDrawでは、この場合矩形領域を何かの色で塗りつぶすという処理をすることになります(矩形塗りつぶし)。

では、コードを見てみましょう。

1
2
3
4
5
6
7
8
//----------[ サーフェイス全消去 ]--------------------------------------------------------------
void ClearScreen(LPDIRECTDRAWSURFACE sf){
    DDBLTFX ddbltfx;
    ZeroMemory(&ddbltfx,sizeof(DDBLTFX));
    ddbltfx.dwSize=sizeof(DDBLTFX);
    //ddbltfx.dwFillColor=0;
    sf->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&ddbltfx);
}

塗りつぶしには、サーフェイスのメンバ関数である「Blt」を呼び出します(前回作成した自作関数Bltと名前が同じですが、全く違うものなので注意してください)。
Blt関数はもともと転送用(拡大縮小にも使用)なのですが、
いろいろな機能がついていて、良いような悪いような(謎)関数です。
で、いろいろな機能を使用したい場合はDDBLTFX構造体にいろいろデータをぶち込んで、Blt関数に引き渡してやります。
ZeroMemoryという関数はデータを指定したバイト分0にするというものです。
dwSizeにはデータのサイズを入れてやります。
で、塗りつぶしたい色のパレット番号をメンバdwFillColorに入れてやるのですが、今回は黒(パレット0番)なので何もしなくていい事になります。
Bltの構文は以下の通りです。

書式 HRESULT Blt( LPRECT lpDestRect, LPDIRECTDRAWSURFACE lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx );
lpDestRect ブロック転送する転送先のサーフェス上の矩形の左上および右下の位置を定義した RECT 構造体へのポインタ。このパラメータが NULL であると、転送先の全サーフェスが使われる。
lpDDSrcSurface ブロック転送操作の転送元である DirectDraw サーフェスへのポインタ。
lpSrcRect ブロック転送される転送元サーフェス上の矩形の左上および右下の場所を定義した RECT 構造体へのポインタ。このパラメータが NULL であると、転送元の全サーフェスが使われる。
dwFlags 指定できる主なフラグは以下の通りです。

DDBLT_COLORFILL 転送先サーフェス上の転送先矩形を埋めるRGB 色として DDBLTFX構造体のdwFillColor メンバを使用する。
DDBLT_KEYSRC 転送元サーフェスと関連づけられたカラーキーを使用する(透明色)。
DDBLT_WAIT 何らかの原因で転送出来ない時に転送できるまで待つ。

これらはOR演算子(|)を使用して複数同時に指定可能です。

lpDDBltFx DDBLTFX構造体へのポインタ。
戻り値 成功するとDD_OKが返ってくるらしい。

今回は、サーフェイスの全領域を黒(パレット0番)で塗りつぶすのでlpDestRectはNULLになっていますが、RECT構造体を使用して部分的に塗りつぶす事も出来ます。

拡大縮小付きデータ転送

拡大縮小付きデータ転送は自作関数「BltStretch」にまとめました。

1
2
3
4
5
6
7
8
//----------[ 拡大縮小付きデータ転送 ]----------------------------------------------------------
void BltStretch(LPDIRECTDRAWSURFACE saki,int sx,int sy,int swidth,int sheight,
                LPDIRECTDRAWSURFACE moto,int mx,int my,int mwidth,int mheight,char trans){
    DWORD key[2]={0,DDBLT_KEYSRC};
    RECT r_saki={sx,sy,sx+swidth,sy+sheight};
    RECT r_moto={mx,my,mx+mwidth,my+mheight};
    saki->Blt(&r_saki,moto,&r_moto,key[trans] | DDBLT_WAIT,NULL);
}

Bltの構文については上で説明した通りです。
転送先のサーフェイスがBlt関数を呼び出すことになります。
BltFast関数(第8回参照)と似てる部分もあるのですが、転送先の指定にRECT構造体を使用していたり、透明色を使用しない時に特に指定が無いという事が違う点ですかね。
転送元と転送先のサイズが違うときに拡大縮小を行います。
サイズが同じ時はそのまま転送するのですが、ハードウェアの転送であればBltFastと速度は同じらしいです。

まぁ今回は、特別な機能を使用していないのでDDBLTFX構造体へのポインタはNULLを指定してます。
拡大縮小機能は普通のビデオカードではまず対応していないでしょう。
その場合、ソフトウェアでエミュレートするのですが、ビデオメモリ内に作成されたサーフェイス間のデータ転送は非常にたるいです。
ですからシステムメモリ内のサーフェイス間で拡大縮小をした後でビデオメモリ内のサーフェイスへ転送する方がいいかもしれませんね。

サンプルコードのダウンロード

では、まずサンプルコードをダウンロードし、解凍してください。
私はVisual C++6.0でコンパイルしているので、
お持ちの方はVisual C++でプロジェクトファイルを開いてください(「ddraw_08.dsw」をダブルクリックすれば開けます)。
圧縮ファイルに含まれる「ddraw_08.exe」をダブルクリックし、実行してみてください。
どうでしょう?画面が切り替わり、フルスクリーン化し、
slimeが作業用サーフェイスに読み込まれ、そこから部分的にバックバッファにデータが転送されていると思います(謎

というわけで、今回は作業用サーフェイスを作成し、データ転送をしたいと思います。

作業用サーフェイスの作成

普段、ゲームを作成する場合は、オフスクリーンサーフェイス(作業用サーフェイス)と呼ばれる目に見えないサーフェイスを作成し、そこへビットマップを読み込み、そこからバックバッファへデータ転送し、フリップし、画面に表示させます・・・というのは何度も書いているので既に解っていると思います。
んでは、まずオフスクリーンサーフェイスの作成方法を記しておきます。

オフスクリーンサーフェイスの作成は自作関数「StartDirectDraw」に追加してあります。

1
2
3
4
5
6
7
8
9
10
11
12
13
LPDIRECTDRAWSURFACE lpWork=NULL;    //オフスクリーンサーフェイス(作業用バッファ)
 
    
 
    //オフスクリーンサーフェイス作成
    ZeroMemory(&ddsd,sizeof(ddsd));
    ddsd.dwSize=sizeof(ddsd);
    ddsd.dwFlags=DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
    ddsd.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN;
    ddsd.dwWidth=640;
    ddsd.dwHeight=480;
    if((lpDD->CreateSurface(&ddsd,&lpWork,NULL))!=DD_OK)
        return FALSE;

DirectDrawオブジェクトのメンバである「CreateSurface」については第3回のプライマリサーフェイス作成時に多少説明しました。
今回はオフスクリーンサーフェイスを作成するという事で、dwFlagsには更に DDSD_WIDTH | DDSD_HEIGHT を追加指定し、サーフェイスの縦幅、横幅を指定出来るようにします。
そうしたら、dwWidthとdwHeightに縦幅、横幅を指定(今回はプライマリサーフェイスと同じ640×480)します。
んで、ddsCaps.dwCapsにDDSCAPS_OFFSCREENPLAINを指定し、「CreateSurface」を呼び出せば、サーフェイスが作成されます。
作成したいサーフェイスのサイズは自由に変更可能ですが、プライマリサーフェイスよりも大きなサーフェイスを作成しようとするとDirectXのバージョンによっては失敗する可能性があるのでやめた方が良いですね。

ちなみに、オフスクリーンサーフェイスはビデオメモリに作成されますが、ビデオメモリが足りなくなると、自動的にシステムメモリ内に作成されます。
また、意図的にシステムメモリ内へサーフェイスを作成したい場合はddsd.ddsCaps.dwCapsにDDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;を指定してやります。

例によって、オフスクリーンサーフェイス(lpWork)は、ビデオメモリだろうがシステムメモリだろうが関係なしに、フロントバッファやバックバッファと同じように扱う事が出来ます(GDIの描画等含む)。

サーフェイス間のデータ転送

では、オフスクリーンサーフェイスにキャラクターイメージを読み込んだ後、このイメージを他の場所へ転送します。
で、ただ転送するだけでなく、透明色を使用した転送が出来ます。その場合、どの色を透明色にするかをサーフェイス毎に設定しておかなければなりません。
では、透明色の指定方法を以下に記します。

1
2
3
4
5
6
7
8
9
10
//----------[ 透明色の設定 ]--------------------------------------------------------------------
void SetTransColor(LPDIRECTDRAWSURFACE sf,int palette_number){
    DDCOLORKEY ddck={palette_number,palette_number};
    sf->SetColorKey(DDCKEY_SRCBLT,&ddck);
}
 
    //呼び出し
    //透明色の指定(パレット1番の色を透明色にする)
    SetTransColor(lpWork,1);

まず、DDCOLORKEY構造体のメンバに透明色にしたい色を指定します。DDCOLORKEY構造体は以下のようになっています。

1
2
3
4
typedef struct _DDCOLORKEY{
    DWORD dwColorSpaceLowValue;
    DWORD dwColorSpaceHighValue;
} DDCOLORKEY;

メンバ変数は2つあります。パレット番号のLowValueからHighValueの範囲を透明色とするらしいのですが(未確認)、普段は1色しか透明色として使用しないので、LowもHighも同じ値にします。
そうしたら、DirectDrawサーフェイスのメンバである「SetColorKey」を呼び出します。構文は以下の通りです。

書式 HRESULT SetColorKey( DWORD dwFlags, LPDDCOLORKEY lpDDColorKey );
dwFlags 透明色を指定する場合はDDCKEY_SRCBLTを指定する。これは、転送元サーフェイスの指定した色以外を転送する事を意味する。転送元カラーキーとも言う。

他にもDDCKEY_DESTBLTというのがあるが、これは転送先の指定した色の部分にのみ転送する。転送先カラーキーとも言う。こっちは滅多に使用しないので解説省略だぃ(いつか解説するかも)。

lpDDColorKey DirectDrawSurface オブジェクトの新しいカラーキー値を含んでいる DDCOLORKEY 構造体へのポインタ。
戻り値 成功するとDD_OKが返ってくるらしい。

さて、今回はパレット1番の紫色(パレット番号は0から始まります)を透明色として使用する事にします。
そうしたら、今度はデータ転送です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//----------[ データ転送 ]----------------------------------------------------------------------
void Blt(LPDIRECTDRAWSURFACE saki,int sx,int sy,int width,int height,
         LPDIRECTDRAWSURFACE moto,int mx,int my,char trans){
    DWORD key[2]={DDBLTFAST_NOCOLORKEY,DDBLTFAST_SRCCOLORKEY};
    RECT rec={mx,my,mx+width,my+height};
    saki->BltFast(sx,sy,moto,&rec,key[trans] | DDBLTFAST_WAIT);
}
 
//この自作関数はややこしいので、自分なりに使いやすい関数を作成してください(^^;
 
    
 
    Blt(lpBack,32  ,64,32,32,lpWork,32,0,0);    //透明色無し
    Blt(lpBack,32*5,64,32,32,lpWork,32,0,1);    //透明色有り

データ転送をするには、転送先のサーフェースがメンバ関数「BltFast」を呼び出します。
「BltFast」の構文は以下の通りです。

書式 HRESULT BltFast( DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans );
dwX,dwY 転送先のXおよびY座標
lpDDSrcSurface 転送元のサーフェイスへのポインタ。言い忘れてたかもしれませんがLPDIRECTDRAWSURFACE型は、これだけでポインタなので&はいりません。
lpSrcRect 転送元の領域を指定したRECT構造体へのポインタ。RECT構造体については下で詳しく解説しています。
dwTrans 転送タイプ。

DDBLTFAST_NOCOLORKEYは、透明色無しの転送。

DDBLTFAST_SRCCOLORKEYは、透明色有り(転送元カラーキーを使用)の転送

DDBLTFAST_WAITは、何らかの原因で転送出来ない時に、転送できるまで待つという指定。

これらはOR演算(|)で同時に複数指定できます。

戻り値 成功するとDD_OKが返ってくるらしい。

BltFastを使用するには、RECTと呼ばれる構造体のメンバに転送元の領域を指定する必要があります。RECT構造体は
以下のように定義されています。

1
2
3
4
5
6
7
8
9
10
11
typedef struct tagRECT {
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
} RECT;
 
left    四角形の左上隅の x 座標を指定します。
top 四角形の左上隅の y 座標を指定します。
right   四角形の右下隅の x 座標を指定します。
bottom  四角形の右下隅の y 座標を指定します。

例えば、転送元の画像のX,Y座標が32,0から64,32だとする場合は

1
2
3
4
5
6
7
8
9
//初心者用
RECT rec;
rec.left=32;
rec.top=0;
rec.right=64;
rec.bottom=32;
 
//中級者用
RECT rec={32,0,64,32};

というように指定します。
こうして転送元の範囲を指定したらBltFastを呼び出します。
転送先がバックバッファ(lpBack)、転送先のX、Y座標32,32、転送元がオフスクリーンサーフェイス(lpWork)とした場合の転送(透明色を使用 する/しない)の書き方は以下の通りです。

1
2
3
4
5
//透明色無しの転送(そのまま転送)
lpBack->BltFast(32,32,lpWork,&rec,DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT);
 
//透明色有りの転送
lpBack->BltFast(32,32,lpWork,&rec,DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);

ただ、転送タイプの指定「DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT」というのが非常に長いため、ソースの無駄遣いのような気になります(爆)。
このフラグをテーブル化し、数値で指定するようにしたのが自作関数「Blt」というわけです。

後始末

作成したオフスクリーンサーフェイス(lpWork)は解放してやらなきゃいけません。
「lpWork->Release();」というふうにしてやります。
いつも通り、マクロを使用するならば「RELEASE(lpWork);」ですかね。

記事検索

コミュニティ

Banner designed by 真帆様
ドット絵掲示板
ドット絵掲示板
自作の「ドット絵」を投稿することができます。ドッターさん同士の交流の場としてご利用ください。
TakaboSoft Wiki
TakaboSoft Wiki
ソフトウェアに関する質問・不具合報告・要望などを書き込むことができます。