■ 新・ゲーム開発講座




■ へっぽこプログラミング入門♪


■第54夜:アルファブレンド

今回は、Windows98以降限定ということで、アルファブレンドをご紹介します。
これ、私のVC++6.0のHELPでは暫定仕様しか載っていない(英語ドキュメントのみ。しかも定数の説明が違ってる ^^;)のですが、ちゃんと動作はするようですし使い方も意外に簡単なので、お手軽に活用する式に逝っちゃいましょう。ソースは
こちらです。

■AlphaBlend() について

アルファブレンドは、Windows98以降のGDI関数に AlphaBlend() として追加されています。これを使うには、ソースファイルにヘッダとして wingdi.h をインクルードし、プロジェク
トに msimg32.lib を追加する必要があります。ライブラリの追加はVC++の統合環境のメニューから、プロジェクト → 設定 でダイアログを開き、リンクのタブをクリックして オブジェ
クト/ライブラリモジュールと書いてあるフィールドに msimg32.lib を追加すればOKです。
関数のプロトタイプは次のとおりです。仕様上は StretchBlt() のように縦横伸縮可能なのですが、速度が遅いのでくににん的には等幅転送で使ったほうがよさそうかな〜と思っています。

AlphaBlend(
HDC hdcDest,
int nXOriginDest,
int nYOriginDest,
int nWidthDest,
int hHeightDest, HDC hdcSrc,
int nXOriginSrc,
int nYOriginSrc,
int nWidthSrc,
int nHeightSrc,
//転送先DC
//転送先X
//転送先Y
//転送先の幅
//転送先の高さ
//転送元DC
//転送元X
//転送元Y
//転送元の幅
//転送元の高さ
BLENDFUNCTION blendFunction //ブレンドファンクション
);

ここで、ブレンドファンクションとは BLENDFUNCTION 型の構造体で、本来ならここにいろいろな設定をする筈…なのです、きっと。でも実際には透過率以外は選択の余地はありません。設定は、こんなふうに行います。


BLENDFUNCTION bf;

bf.BlendOp = AC_SRC_OVER; //選択の余地なし(これしかない)
bf.BlendFlags = 0; //選択の余地なし(これしかない)
bf.AlphaFormat = 0; //転送元画像にはアルファチャンネルは無い
bf.SourceConstantAlpha = rate*255/100; //0〜255で表すブレンド値


この bf をブレンドファンクションとして渡してやればOKです。透過率は 0〜255 で設定するのですが、くににん的には rate = 0〜100 で示したほうが分かりやすかな…と思っているので、これを *255/1000〜255 に変換して渡すようにしています。

■仕様

さて、これをスクリプトコマンドとして実装する訳ですが、パラメータが多いのが微妙にいや〜んなので、第22夜#g_copy コマンドと仕様を合わせます。コマンド名は…アルファブレンドだから #alpha_copy とでもしましょうか(安直 ^^;)


書式:#alpha_copy dst_vram,dx,dy,src_vram,sx,sy,width,hight,rate

仕様: サーフェイス src_vram の座標 sx,sy 幅 width 高さ hight
のパターンをサーフェイス dst_vram の 座標 dx,dy に
コピーする。

dst_vram :バックサーフェイス=BS 背景画面=BG パーツ画面=PT
dx,dy :コピー先座標
src_vram :バックサーフェイス=BS 背景画面=BG パーツ画面=PT
sx,sy :コピー元座標
width,hight:幅、高さ
rate :アルファ値(0〜100)

■Mode_stat

一発完了型のコマンドなのでフラグ構造体は変更ありません。

■コマンド解釈部

コマンド名と処理関数名を TextEngine.cppCommand_call() に追加します。


void Command_call()
{

char com_name[256];
int i=0;
int flag=OFF;

//コメント文対応
if( *(TEXT+1)=='#' || *(TEXT-1)=='#' ){

//#が2回連続したらコメント文なので行末までテキストポインタを飛ばす

while( *TEXT!=0x0d ) TEXT++;
return;

}

TEXT++; //#を飛ばす

//コマンド文字列の切り出し
while( *TEXT>0x20 ){
com_name[i] = *TEXT;
TEXT++;
i++;
}
TEXT--;
com_name[i]=0; //これはNULL文字

//コマンド名の評価 → 処理関数呼び出し
if( strcmp(com_name,"delay" )==0 ){ Com_delay(); flag=ON;} //遅延
if( strcmp(com_name,"wait" )==0 ){ Com_cursor_blink(); flag=ON;} //カーソルブリンク
if( strcmp(com_name,"w" )==0 ){ Com_cursor_blink(); flag=ON;} //カーソルブリンク
if( strcmp(com_name,"halt" )==0 ){ Com_halt(); flag=ON;} //終了
if( strcmp(com_name,"page" )==0 ){ Com_page(); flag=ON;} //改ページ
if( strcmp(com_name,"g_load" )==0 ){ Com_g_load(); flag=ON;} //BMPのLOAD
if( strcmp(com_name,"g_copy" )==0 ){ Com_g_copy(); flag=ON;} //BMPのCOPY
if( strcmp(com_name,"g_change")==0 ){ Com_g_change(); flag=ON;} //画面切替
if( strcmp(com_name,"flag_on" )==0 ){ Com_flag_on(); flag=ON;} //イベントフラグON
if( strcmp(com_name,"flag_off")==0 ){ Com_flag_off(); flag=ON;} //イベントフラグOFF
if( strcmp(com_name,"disp_flag")==0 ){ Com_disp_flag(); flag=ON;} //イベントフラグ表示
if( strcmp(com_name,"if_flag" )==0 ){ Com_if_flag(); flag=ON;} //フラグによる分岐
if( strcmp(com_name,"jump" )==0 ){ Com_jump(); flag=ON;} //ラベルジャンプ
if( strcmp(com_name,"file_change")==0 ){Com_file_change(); flag=ON;} //ファイル間ジャンプ
if( strcmp(com_name,"select3" )==0 ){ Com_select3(); flag=ON;} //3択
if( strcmp(com_name,"play_wav")==0 ){ Com_play_wav(); flag=ON;} //WAV演奏開始
if( strcmp(com_name,"stop_wav")==0 ){ Com_stop_wav(); flag=ON;} //WAV演奏停止
if( strcmp(com_name,"play_midi")==0 ){ Com_play_midi(); flag=ON;} //MIDI演奏開始
if( strcmp(com_name,"stop_midi")==0 ){ Com_stop_midi(); flag=ON;} //MIDI演奏停止
if( strcmp(com_name,"play_mp3")==0 ){ Com_play_mp3(); flag=ON;} //MP3演奏開始
if( strcmp(com_name,"stop_mp3")==0 ){ Com_stop_mp3(); flag=ON;} //MP3演奏停止
if( strcmp(com_name,"set_font_size")==0 ){ Com_set_font_size(); flag=ON;} //フォントサイズ変更
if( strcmp(com_name,"set_text_area")==0 ){ Com_set_text_area(); flag=ON;} //テキストエリア変更
if( strcmp(com_name,"set_text_wait")==0 ){ Com_set_text_wait(); flag=ON;} //テキストウェイト変更
if( strcmp(com_name,"set_text_color")==0 ){ Com_set_text_color(); flag=ON;} //テキストカラー変更
if( strcmp(com_name,"set_str")==0 ){ Com_set_str(); flag=ON;} //文字列変数に値をセット
if( strcmp(com_name,"text_input")==0 ){ Com_text_input(); flag=ON;} //ダイアログを開いてテキスト入力
if( strcmp(com_name,"save_point")==0 ){ Com_save_point(); flag=ON;} //セーブポイント設定
if( strcmp(com_name,"sp")==0 ) { Com_save_point(); flag=ON;} //セーブポイント設定(短縮表記)
if( strcmp(com_name,"save_point_mode")==0 ){Com_save_point_mode(); flag=ON;}//セーブポイントモード
if( strcmp(com_name,"disp_save_point_mode")==0 ){Com_disp_save_point_mode();flag=ON;} //セーブポイントDEBUG用
if( strcmp(com_name,"shake")==0 ) {Com_shake(); flag=ON;} //揺れる効果
if( strcmp(com_name,"fade_out")==0 ) {Com_fade_out(); flag=ON;} //フェードアウト
if( strcmp(com_name,"fade_in")==0 ) {Com_fade_in(); flag=ON;} //フェードイン
if( strcmp(com_name,"alpha_copy")==0 ) {Com_alpha_copy(); flag=ON;} //アルファブレンド

// ↑
//そのうちここに他のコマンドも追加していきましょう
// ↓

//フラグを見て、切り出されたコマンド名が有効なコマンドだったかどうか確認する
if( flag == OFF ){

char str[256];

sprintf( str,"警告:無効なコマンド [#%s] が記述されています",com_name);
MessageBox(NULL,str,"Command_call()",MB_OK);

}

}

■コマンド処理部

やっていることは至極簡単です。
パラメータを解釈して AlphaBlend() に渡しているだけ…♪(^0^) このように、あらゆるAPIはスクリプトコマンド化できます。でもやりすぎるとスクリプターさんがプログラマ並みの知識を要求されるようになったりして「スクリプトの意味がないじゃん!(爆)」になってしまうのでほどほどにするのが吉でしょうか(ぉ


int Com_alpha_copy()
{

char dst_vram[256],src_vram[256];
int dx,dy,sx,sy,width,hight,rate;

TEXT++;

//コピー先画面
strcpy( dst_vram, Kaiseki_TextStr() );
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

//dx
dx = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

//dy
dy = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

//コピー元画面
strcpy( src_vram, Kaiseki_TextStr() );
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

//sx
sx = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

//sy
sy = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

//width
width = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

//hight
hight = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

//rate
rate = kaiseki_10();

//アルファブレンド
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER; //選択の余地なし(これしかない)
bf.BlendFlags = 0; //選択の余地なし(これしかない)
bf.AlphaFormat = 0; //転送元画像にはアルファチャンネルは無い
bf.SourceConstantAlpha = rate*255/100; //0〜255で表すブレンド値

AlphaBlend( Get_game_DC(dst_vram),dx,dy,width,hight,
Get_game_DC(src_vram),sx,sy,width,hight,bf );

return 0;

}

さて、それでは実行してみましょう。さすがにドットマスクに比べると比較にならないほど綺麗に合成されていますね。速度が遅いのが難点といえば難点ですが、激しいアクションゲームなどで使わないかぎりそれほど致命的でもないだろうと思います。

ともあれ、半透明が使えるというのは見た目にも非常にカッコイイので、ぜひとも活用したいものです(・ω・)ノ