8

画面に描画したいこの800x600squareがあります。その中の円を「カット」したい(アルファは0になります)。基本的に、私はこの長方形全体を地図上に描いているので、私が描いたこれらの「円」では、地図を見ることができます。そうでない場合は、灰色の正方形が見えます。

4

1 に答える 1

27

だから、あなたはあなたのゲームの1つに戦争の霧を追加しようとしていると思いますか?

数週間前に地元の大学でA*パスファインディングを示すために作成した小さなデモがあったので、戦場の霧を追加できると思いました。結果は次のとおりです。

初期マップ

まず、完全に見える完全な地図から始めます

フルマップ

次に、画面全体をカバーするサーフェスを追加しました(マップが画面よりも小さいことに注意してください。この場合、画面に戦場の霧を追加しましたが、スクロールする場合は、各マップピクセルをカバーするようにしてください。 1:1)

mFogOfWar = SDL_CreateRGBSurface(SDL_HWSURFACE, in_Width, in_Height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
SDL_Rect screenRect = {0, 0, in_Width, in_Height};
SDL_FillRect(mFogOfWar, &screenRect, 0xFF202020);

次に、それを描画する必要があります...ゲームオブジェクトを描画した後、UIを描画する前にこの呼び出しを追加しました

DrawSurface(mFogOfWar, 0, 0);

どこ

void RenderingManager::DrawSurface(SDL_Surface* in_Surface, int in_X, int in_Y)
{
    SDL_Rect Dest = { in_X, in_Y, 0, 0 };
    SDL_BlitSurface(in_Surface, NULL, mScreen, &Dest);
}

これにより、次の結果が得られるはずです。

すべてが曇った

「パンチ面」

次に、次のような32ビット.pngを作成しました(チェッカーボードにはアルファが表示されます)

パンチ

主人公をレンダリングするときに、次の呼び出しを追加しました。

gRenderingManager.RemoveFogOfWar(int(mX) + SPRITE_X_OFFSET, int(mY) + SPRITE_Y_OFFSET);

オフセットは、パンチをスプライトの中心に配置するためだけにあります。基本的に、通過するRemoveFogOfWarのはスプライトの中心です。

戦場の霧を取り除く

今、戦争の霧の肉。私は2つのバージョンを作成しました。1つはFogofWarが完全に削除され、もう1つはFogofWarがリセットされます。punch戦場の霧のリセットは、アルファがリセットされる輪郭0と、フレームごとに輪郭に含まれるピクセルよりも少ないピクセルでキャラクターが移動するという事実に依存しています。そうでない場合Rectは、パンチが適用された場所を維持して補充します。新しいパンチを再度描画する前に。

SDLとの「乗算」ブレンドが見つからなかったため、パンチサーフェスで反​​復し、戦場の霧のアルファを更新する単純な関数を作成することにしました。最も重要な部分は、サーフェスの境界内にとどまるようにすることです。これにより、コードの大部分を占めるようになります...いくつかの切り抜き関数があるかもしれませんが、私はわざわざチェックしませんでした。

void RenderingManager::RemoveFogOfWar(int in_X, int in_Y)
{
    const int halfWidth = mFogOfWarPunch->w / 2;
    const int halfHeight = mFogOfWarPunch->h / 2;

    SDL_Rect sourceRect = { 0, 0, mFogOfWarPunch->w, mFogOfWarPunch->h };
    SDL_Rect destRect = { in_X - halfWidth, in_Y - halfHeight, mFogOfWarPunch->w, mFogOfWarPunch->h };

    // Make sure our rects stays within bounds
    if(destRect.x < 0)
    {
        sourceRect.x -= destRect.x; // remove the pixels outside of the surface
        sourceRect.w -= sourceRect.x; // shrink to the surface, not to offset fog
        destRect.x = 0;
        destRect.w -= sourceRect.x; // shrink the width to stay within bounds
    }
    if(destRect.y < 0)
    {
        sourceRect.y -= destRect.y; // remove the pixels outside
        sourceRect.h -= sourceRect.y; // shrink to the surface, not to offset fog
        destRect.y = 0;
        destRect.h -= sourceRect.y; // shrink the height to stay within bounds
    }

    int xDistanceFromEdge = (destRect.x + destRect.w) - mFogOfWar->w;
    if(xDistanceFromEdge > 0) // we're busting
    {
        sourceRect.w -= xDistanceFromEdge;
        destRect.w -= xDistanceFromEdge;
    }
    int yDistanceFromEdge = (destRect.y + destRect.h) - mFogOfWar->h;
    if(yDistanceFromEdge > 0) // we're busting
    {
        sourceRect.h -= yDistanceFromEdge;
        destRect.h -= yDistanceFromEdge;
    }

    SDL_LockSurface(mFogOfWar);

    Uint32* destPixels = (Uint32*)mFogOfWar->pixels;
    Uint32* srcPixels = (Uint32*)mFogOfWarPunch->pixels;

    static bool keepFogRemoved = false;

    for(int x = 0; x < destRect.w; ++x)
    {
        for(int y = 0; y < destRect.h; ++y)
        {
            Uint32* destPixel = destPixels + (y + destRect.y) * mFogOfWar->w + destRect.x + x;
            Uint32* srcPixel = srcPixels + (y + sourceRect.y) * mFogOfWarPunch->w + sourceRect.x + x;

            unsigned char* destAlpha = (unsigned char*)destPixel + 3; // fetch alpha channel
            unsigned char* srcAlpha = (unsigned char*)srcPixel + 3; // fetch alpha channel
            if(keepFogRemoved == true && *srcAlpha > 0)
            {
                continue; // skip this pixel
            }

            *destAlpha = *srcAlpha;
        }
    }

    SDL_UnlockSurface(mFogOfWar);
}

keepFogRemoved = falseキャラクターが動き回った後でも、これは私にこれを与えました

戦場の霧

そしてこれでkeepFogRemoved = true

戦場の霧パーマネント

検証

重要なのは、ピクセルバッファの外側に書き込まないようにすることです。そのため、負のオフセットまたは幅や高さから外れるオフセットに注意してください。コードを検証するために、マウスがクリックされたときの簡単な呼び出しを追加し、RemoveFogOfWarコーナーとエッジを試して、「1つずつずれている」問題がないことを確認しました。

case SDL_MOUSEBUTTONDOWN:
    {
        if(Event.button.button == SDL_BUTTON_LEFT)
        {
            gRenderingManager.RemoveFogOfWar(Event.button.x, Event.button.y);
        }
        break;
    }

ノート

明らかに、「パンチ」に32ビットのテクスチャは必要ありませんが、それを行う方法を示すために私が考えることができる最も明確な方法でした。ピクセルあたりわずか1ビット(オン/オフ)を使用して実行できます。グラデーションを追加して、

if(keepFogRemoved == true && *srcAlpha > 0)
{
    continue; // skip this pixel
}

のようなものに

if(*srcAlpha > *destAlpha)
{
    continue;
}

このようなスムーズなブレンドを維持するには:

ここに画像の説明を入力してください

3戦場の霧

これを追加する必要があると思いました...戦争の3つの状態の霧を作成する方法を追加しました:visible、、。seenfogged

これを行うにはSDL_Rect、戦争の霧を最後に「パンチ」した場所を維持し、アルファが特定の値よりも低い場合は、その値にクランプします。

だから、単に追加することによって

for(int x = 0; x < mLastFogOfWarPunchPosition.w; ++x)
{
    for(int y = 0; y < mLastFogOfWarPunchPosition.h; ++y)
    {
        Uint32* destPixel = destPixels + (y + mLastFogOfWarPunchPosition.y) * mFogOfWar->w + mLastFogOfWarPunchPosition.x + x;
        unsigned char* destAlpha = (unsigned char*)destPixel + 3;

        if(*destAlpha < 0x60)
        {
            *destAlpha = 0x60;
        }
    }
}
mLastFogOfWarPunchPosition = destRect;

戦場の霧が「パンチ」されるループの直前に、StarCraftのようなゲームで発生する可能性のあるものと同様の戦場の霧が発生します。

3状態

さて、「見た」戦争の霧は半透明なので、霧の中にある「敵」を適切にクリップするようにレンダリング方法を微調整する必要があります。そうすれば、それらは見えませんが、地形は見えます。

お役に立てれば!

于 2012-12-01T18:03:54.763 に答える