第13章 パレットを直接制御する(パレットアニメーション)

13-2 フェードイン・フェードアウト

何もない(真っ黒や真っ白の)画面からだんだん背景やキャラクタが現れてくるのをフェードイン、逆をフェードアウトと呼ぶ。フェードアウトの場合、パレットに格納されている256色の色を、少しずつ黒または白にしていけば、最後には真っ黒または真っ白になる。

これをプログラムで行う方法を紹介する。

1. パレットの取得と再設定

パレットの生成はDirectDraw初期化処理で行い、プライマリ・サーフェイスにセットしている。

LPDIRECTDRAWPALETTE g_pDDPal = NULL;

g_pDDPal = DDLoadPalette(g_pDD, ビットマップファイル名又はリソース名);
g_pDDSPrimary->SetPalette(g_pDDPal);

パレット情報を好きなように加工するには、パレット情報を取得しなければならない。取得するには、GetEntries関数を使って次のように取得する。

PALETTEENTRY ape[256];
g_pDDPal->GetEntries(0, 0, 256, ape);

これにより、ape配列にパレット情報が格納される。この配列内のデータを加工し、新たなパレットとしてプライマリ・サーフェイスにセットすると、加工後のパレット情報で画像が描画される。セットするにはSetEntries関数を使って次のように行う。

g_pDDPal->SetEntries(0, 0, 256, ape);

※SetEntriesを実行すると、プライマリ・サーフェイスのパレット情報が即座に書き換わるため、Flipの直前で行うのが望ましい

2. フェードイン・フェードアウトを行うには?

GetEntries関数で読み込んだパレット情報をどのように加工するとフェードイン・フェードアウトの効果を出せるのか?
サンプルプログラムを紹介する。

《フェードインを行うサンプル》

PALETTEENTRY ape_w[256]; // 作業用
static int FadeCnt = 0; // フェードの段階
int i;

/* 加工後の情報を作業用配列に格納する */
for (i=0 ; i<256 ; i++)
{
    ape_w[i].peRed = (BYTE)(ape[i].peRed * FadeCnt >> 8);
    ape_w[i].peGreen = (BYTE)(ape[i].peGreen * FadeCnt >> 8);
    ape_w[i].peBlue = (BYTE)(ape[i].peBlue * FadeCnt >> 8);
    ape_w[i].peFlags = (BYTE)0;
}
FadeCnt++;
if (FadeCnt > 256)
{
    FadeCnt = 0;
    return 0;
}

/* パレット情報を入れ替える */
g_pDDPal->SetEntries(0, 0, 256, ape_w);

※この処理を繰り返し行うことにより、少しずつフェードインしてくる
※フェードインが終わったとき、apeとape_wの内容は同じになる

《フェードアウトを行うサンプル》

PALETTEENTRY ape_w[256]; // 作業用
static int FadeCnt = 256; // フェードの段階
int i;

/* 加工後の情報を作業用配列に格納する */
for (i=0 ; i<256 ; i++)
{
    ape_w[i].peRed = (BYTE)(ape[i].peRed * FadeCnt >> 8);
    ape_w[i].peGreen = (BYTE)(ape[i].peGreen * FadeCnt >> 8);
    ape_w[i].peBlue = (BYTE)(ape[i].peBlue * FadeCnt >> 8);
    ape_w[i].peFlags = (BYTE)0;
}
FadeCnt--;
if (FadeCnt <= 0)
{
    FadeCnt = 256;
    return 0;
}

/* パレット情報を入れ替える */
g_pDDPal->SetEntries(0, 0, 256, ape_w);

※この処理を繰り返し行うことにより、少しずつフェードアウトする
※フェードアウトが終わると、ape_wの内容はすべて黒になる

3. ゲームに組み込むには?

ゲームに組み込む場合、myDraw.cpp内にフェードイン・フェードアウトを行うための各変数と、処理を行う関数を用意するのが望ましい。
まずは変数の宣言を次のように行う。

//=============================================================================
//	DirectDraw関係の自作関数群
//=============================================================================
・
・
・
//-----------------------------------------------------------------------------
// グローバル変数
//-----------------------------------------------------------------------------
static LPDIRECTDRAWPALETTE g_pDDPal = NULL; // プライマリ・サーフェイス・パレット
static char szStrBmp[] = "STRING.BMP";      // 文字列画像ファイル名
static char szBackBmp[4][16] = {            // ゲーム画面背景画像ファイル名
    "START1.BMP",   /* スタート画面用 */
    "BACK1.BMP",    /* 1面用 */
    "BACK2.BMP",    /* 2面用 */
    "BACK3.BMP"     /* 3面用 */
};
static char szCharaBmp[] = "CHARA1.BMP";    // ゲーム画面キャラクタ画像ファイル名
/* フェードイン・フェードアウト用 */
static PALETTEENTRY ape[256]; // 保存用パレット情報
static int FadeType, FadeCnt, FadeSpeed;

次に、パレット情報を取り出す処理を追加する。どこで実行してもいいが、パレットの生成後に行うのが良いだろう。

//-----------------------------------------------------------------------------
// 関数名 : InitializeDraw() 
// 機能概要: Direct Draw オブジェクトの生成
// 戻り値 : 正常終了のとき:DD_OK、異常終了のとき:エラーコード
//-----------------------------------------------------------------------------
HRESULT InitializeDraw(HWND hWnd)
{
    ・
    ・
    ・
    // パレットの生成
    g_pDDPal = DDLoadPalette(g_pDD, szCharaBmp);
    if (g_pDDPal == NULL)
        return InitFail(hWnd, hRet, "DDLoadPalette FAILED");
    g_pDDPal->GetEntries(0, 0, 256, ape);// 現在有効となっているパレット情報の取得

    // プライマリ・サーフェイスにパレットを設定する
    hRet = g_pDDSPrimary->SetPalette(g_pDDPal);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "SetPalette FAILED"); 
    ・
    ・
    ・

次に、フェードイン・フェードアウトを行う関数をmyDraw.cpp内に作成する。

//-----------------------------------------------------------------------------
// 関数名 : FadeInit()
// 機能概要: フェードイン・フェードアウト処理の初期化を行う
// 引数  : type:0ならフェードアウト、1ならフェードイン
//             speed:フェードイン・フェードアウトのスピード
//-----------------------------------------------------------------------------
void FadeInit(int type, int speed)
{
    FadeType = type;
    if (type == 0)
        FadeCnt = 0;
    else
        FadeCnt = 256;
    FadeSpeed = speed;

}

//-----------------------------------------------------------------------------
// 関数名 : FadeInOut()
// 機能概要: フェードイン・フェードアウトを行う
// 戻り値 : 処理の途中ならtrue、処理が終わったらfalseを返す
//-----------------------------------------------------------------------------
bool FadeInOut(void)
{
    PALETTEENTRY ape_w[256];
    int i;

    /* パレット情報を直接操作する */
    for (i=0 ; i<256 ; i++)
    {
        ape_w[i].peRed = (BYTE)(ape[i].peRed * FadeCnt >> 8);
        ape_w[i].peGreen = (BYTE)(ape[i].peGreen * FadeCnt >> 8);
        ape_w[i].peBlue = (BYTE)(ape[i].peBlue * FadeCnt >> 8);
        ape_w[i].peFlags = (BYTE)0;
    }

    /* FadeTypeが0ならフェードイン、1ならフェードアウト */
    if (FadeType == 0)
    {
        FadeCnt += FadeSpeed;
        if (FadeCnt > 256)
            return false;
    }
    else
    {
        FadeCnt -= FadeSpeed;
        if (FadeCnt <= 0)
            return false;
    }

    /* パレット情報を入れ替える */
    g_pDDPal->SetEntries(0, 0, 256, ape_w);

    return true;

}

//-----------------------------------------------------------------------------
// 関数名 : PaletteRollback()
// 機能概要: パレット情報を元に戻す
//-----------------------------------------------------------------------------
void PaletteRollback(void)
{
    g_pDDPal->SetEntries(0, 0, 256, ape);

}

※これらの関数は他のソースから呼び出すため、myDraw.hにプロトタイプ宣言をしなければならない

4. 使い方

では実際に使ってみよう。第12章のプログラムを、ゲームクリア時にフェードアウトしてからスタート画面に戻るように変更する。
まず、ゲームクリア時初期化処理で、フェードアウトの初期化を行う。

//-----------------------------------------------------------------------------
// 関数名 : GameClearInit()
// 機能概要: ゲームクリア処理前初期化処理
//-----------------------------------------------------------------------------
void GameClearInit(HWND hWnd)
{
    FadeInit(1, 1); // フェードアウトさせる
    EndTime = nowTickCount;
    g_FrameNo = GAME_CLEAR;

}

※フェードアウトなので1つ目の引数を1にセット
※2つ目の引数はフェードイン・フェードアウトのスピードなので、1以上であればいくつでもよい

次に、ゲームクリア処理でフェードアウト処理を次のように実行する。

//-----------------------------------------------------------------------------
// 関数名 : GameClearFrame()
// 機能概要: ゲームクリア処理
//-----------------------------------------------------------------------------
void GameClearFrame(HWND hWnd)
{
    HRESULT hRet;
    RECT rcRect;
    char str[80];

    BackDraw();	// 背景描画処理
    CharDraw();	// キャラクタ描画処理

    /* メッセージ表示 */
    SetRect(&rcRect, 200, 0, 570, 24);
    hRet = g_pDDSBack-'gt;BltFast(100, 100, g_pDDSChara, &rcRect, DDBLTFAST_SRCCOLORKEY);
    if (hRet != DD_OK)
        return;

    /* 経過時間を表示 */
    wsprintf(str, "TIME %06d", EndTime - MyChara.StartTime);
    if (!StringDraw(str, 300, 300, 1))
        return;

    /* フェードアウトしてから、スタート画面に戻る */
    if (!FadeInOut())
    {
        PaletteRollback(); // パレット情報を元に戻す
        g_FrameNo = START_INIT;
        return;
    }

}

以上の修正を行い、プログラムを実行する。ゲームクリア時にフェードアウトが行われ、真っ黒になってからスタート画面に戻るようになる。

※この処理では、スタート画面に戻る直前の1フレームだけ、ゲームクリア画面が表示されてしまう・・・が、今は気にしないこと

第13章2練習問題1(必須)

第12章で作ったプログラムに上記修正を行い、ゲームクリア時にフェードアウトが行われることを確認しなさい。

実行結果サンプル

サンプルプログラムはexeだけなので、実行するには同一フォルダにビットマップファイルが必要!!

第13章2練習問題2(自由)

上記プログラムを改造する。スターと画面はフェードインしてから「HIT SPACE KEY」が表示されるようにプログラムを修正しなさい。

《ヒント》

状態別処理なので、START_INITとSTART_FRAMEのほかにSTART_FADE_FRAMEを追加すると、考えやすい(使わなくても可)。

実行結果サンプル

サンプルプログラムはexeだけなので、実行するには同一フォルダにビットマップファイルが必要!!


[ TOP ]