2012-11-30 10 views
7

Mam to 800x600square Chcę narysować na ekranie. Chcę "wyciąć" kółka (gdzie alfa będzie wynosić 0). Zasadniczo rysuję cały prostokąt na mapie, więc w tych "okręgach", które narysowałem, możesz zobaczyć mapę, w przeciwnym razie zobaczysz szary kwadratSDL - rysowanie kół "negatywnych" (Mgła wojny)

+0

Chcesz nadrukować obrazek koła bezpośrednio w kanale alfa mapy? – Tommy

+0

Chcę mieć szare pudełko, z kółkiem "wymazanym" z niego, dzięki czemu mogę dokładnie przejrzeć warstwę, na której przeciągane jest szare pole. – will

Odpowiedz

24

Zakładam więc, że próbujesz dodać mgłę wojny do jedna z was gra?

Miałem małe demo, które zrobiłem dla lokalnego Uniwersytetu kilka tygodni temu, aby pokazać ścieżkę A *, więc pomyślałem, że mógłbym dodać do ciebie mgłę wojny. Oto wyniki:

Początkowa mapa

pierwsze, należy uruchomić z pełną mapą, całkowicie widocznym

Full Map

mgła

Następnie dodałem powierzchnię zakryj cały ekran (zauważ, że moja mapa jest mniejsza niż ekran, więc w tym przypadku dodałem tylko mgłę wojny na sc reen, ale jeśli przewijanie, upewnij się, że obejmuje każdy piksel map 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); 

Następnie trzeba go wyciągnąć ... dodałem tego połączenia po pobraniu gry i obiektów przed wyciągnięciem UI

DrawSurface(mFogOfWar, 0, 0); 

Gdzie

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

które powinno dać następujący wynik:

Everything Fogged

"Poncz Surface"

Potem stworzył 32 bitów .png że wygląda następująco (szachownica pokazuje alfa)

Punch

Podczas renderowania moją bohaterkę, dodałem do tej rozmowy :

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

Przesunięcie jest tylko tam, aby wycentrować cios duszkiem, w zasadzie to, co przechodzę do RemoveFogOfWar jest centrum mojego duszka.

Usuń Fog of War

Teraz mięso mgle wojny. Zrobiłem dwie wersje, z których mgła wojny została usunięta na stałe, a druga, w której mgła wojny jest resetowana. Reset mojej mgły wojennej opiera się na mojej powierzchni punch, aby uzyskać kontur, w którym alfa jest resetowany do 0, a fakt, że mój znak porusza się o mniej pikseli niż kontur zawiera na ramkę, w przeciwnym razie zachowałbym Rect, w którym zastosowano uderzenie i Uzupełniam go, zanim znów zacznę nowy cios.

Ponieważ nie mogłem znaleźć "mnogiej" mieszanki z SDL, postanowiłem napisać prostą funkcję, która iteruje na powierzchni stempla i aktualizuje alfa na mgle powierzchni wojny. Najważniejszą częścią jest, aby upewnić się pozostać w granicach swoich powierzchniach, dzięki czemu zajmuje większość kodu ... nie mogą być pewne funkcje crop ale nie przeszkadzało sprawdzanie:

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

Która następnie dał mi to z keepFogRemoved = false nawet po postać przeniosła się wokół

fog of war

a to z keepFogRemoved = true

fog of war permanent

Validation

Ważną częścią jest naprawdę, aby upewnić się, że nie napisać poza swoim buforze pikseli, więc uważaj z negatywnymi przesunięcia lub offsetu, która wyprowadzi was z tej szerokości lub wysokości. Aby potwierdzić mój kod, dodałem prostego połączenia do RemoveFogOfWar gdy mysz jest kliknął i próbował narożniki i krawędzie, aby upewnić się, że nie mają „off przez jeden” problem

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

Uwagi

Oczywiście do "uderzenia" nie potrzeba tekstury 32-bitowej, ale był to najczystszy sposób, w jaki mogłem wymyślić, jak to zrobić. Można to zrobić, używając tylko 1 bit na piksel (wł./Wył.). Można też dodać trochę gradientu i zmień

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

do czegoś

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

Aby utrzymać sprawne połączenie takiego:

enter image description here

3 State Fog of War

I pomyślałem, że powinienem dodać ... Dodałem sposób na stworzenie 3 państwowej mgły wojny: visible, seen i fogged.

Aby to zrobić, po prostu trzymam SDL_Rect miejsca, w którym ostatnio "uderzyłem" mgłę wojny, i jeśli wartość alfa jest niższa od pewnej wartości, zacisnę ją przy tej wartości.

Więc, po prostu dodając

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; 

tuż przed pętlą, gdzie mgła wojny jest „dziurkowana”, pojawia się mgła wojny podobny do tego, co można mieć w grach takich jak StarCraft:

3 state

Teraz, ponieważ "widziana" mgła wojny jest półprzezroczysta, musisz poprawić swoją metodę renderowania, aby odpowiednio przyciąć "wrogów", które będą we mgle, więc nie widzisz ich, ale ty nadal widzę teren.

Mam nadzieję, że to pomoże!

+6

Naprawdę doceniam pracę, którą włożyłeś w swoją odpowiedź. To bardziej przypomina samouczek! +1 –

+0

@BartekBanachewicz dzięki! Mam nadzieję, że odpowiedź jest wystarczająco jasna! :) – emartel

+0

woah. Człowieku, jesteś niesamowity! Nie jestem tak doświadczony w stackoverflow, czy jest coś, co mogę zrobić, niż dać odpowiedź Tobie i uprowadzeniu? : P – will