2012-11-30 14 views
7

私はこれを800x600square持っています。私はスクリーンに描きたいです。私はその中の円を「カット」したいです(アルファは0になります)。基本的に私はこの四角形を地図上に描画していますので、描いたこれらの「円」では地図を見ることができます。そうでなければ灰色の四角形が表示されますSDL - 「ネガティブ」サークルを描く(霧の戦い)

+0

円の画像をマップのアルファチャンネルに直接スタンプしたいですか? – Tommy

+0

私は灰色のボックスを持っています。それから円が「消去」されているので、灰色のボックスが描画されているレイヤーに完全に透けて見えます。 – will

答えて

24

だから、あなたは戦争の霧あなたの1人のゲーム?

私は数週間前にA *の探索を示すために地元の大学のために作った小さなデモを持っていたので、私はあなたに戦争の霧を加えることができると思った。ここでの結果です:

初期マップ

まず、あなたは完全なマップで始まり、完全に目に見える

Full Map

その後は、私はに表面を追加しました画面全体をカバーする(私の地図は画面よりも小さいので、この場合はscの戦争の霧を追加したREEN、しかし、あなたがスクロールしている場合、それは各マップの画素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を描画する前にこの呼び出しを追加しましたが

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); 
} 

あなたに次のような結果与えるべき

DrawSurface(mFogOfWar, 0, 0); 

を私はこのようになります。その32ビット.pngを作成

"パンチ面"

(チェッカーボードがアルファを示して)

Punch

私のメインキャラクターをレンダリングするとき、私はこの呼び出しを追加:

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

オフセット基本的にスプライトでパンチを中心にするには、RemoveFogOfWarに渡すものが私のスプライトの中心です。

戦争の霧の肉戦争かぶりを削除します。私は、戦争の霧が永久に取り除かれ、戦争の霧がリセットされる2つのバージョンを行った。私の霧の戦いのリセットは私の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 = true

fog of war permanent

fog of war

そして、この周りを移動した後でさえkeepFogRemoved = falseで私にこれを与えました

検証

重要な部分は、あなたが外であなたのピクセルバッファの書き込み、その幅または高さのあなたをもたらす負のオフセットまたはオフセットと一緒に見ていないことを確認することが本当にあります。マウスをクリックすると、私は「オフずつ」問題

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

ノートを持っていなかったことを確認するコーナーとエッジをしようとすると私のコードを検証するために、私はRemoveFogOfWarに、単純な呼び出しを追加

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

enter image description here

3州霧:あなたはまた、このようなスムーズなブレンドを保つために、いくつかのグラデーションを追加し、

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

のようなものに

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

を変更することができます

I私はこれを追加すべきだと思った...私は戦争の3つの状態の霧を作成する方法を追加:visibleseenfogged

これを行うには、私は戦争のかぶりを最後に「打ち破った」場所の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; 

を追加することによって、私はあなたがスタークラフトのようなゲームを持っている可能性が何に似て戦争の霧を取得します

3 state

戦争の「見えた」霧は半透明であるため、霧の中にある「敵」を適切にクリップするためにレンダリング方法を微調整する必要があります。まだ地形が見えます。

希望すると便利です。

+6

私は本当にあなたの答えに入れていただきありがとうございます。それはチュートリアルのようなものです! +1 –

+0

@BartekBanachewiczありがとう!うまくいけば答えは十分明確です! :) – emartel

+0

woah。あなたは素晴らしいです!私はstackoverflowで経験はありません、私はあなたとupvoteへの答えを与えるよりもいくつかありますか? :P – will

関連する問題