SDL - drawing negative circles (Fog of War)

I have this 800x600square that I want to draw on the screen. I want to "crop" the circles in it (where alpha will be 0). Basically, I draw this whole rectangle above the map, so in these "circles" that I painted, you can see the map, otherwise you will see a gray square

+7
source share
1 answer

So, I suppose, you are trying to add the fog of war to one of you?

I had a small demo that I did for a local university a few weeks ago to show the way A *, so I thought I could add the fog of war to you. Here are the results:

Starting card

First you start with a full map, fully visible

Full map

Fog

Then I added a surface to cover the entire screen (note that my map is smaller than the screen, so for this case I just added the fog of war on the screen, but if you have scrolling, make sure it covers every pixel of the 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); 

Then you need to draw it ... I added this call after drawing the game objects and before drawing the user interface

 DrawSurface(mFogOfWar, 0, 0); 

Where

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

Which should give you the following result:

Everything Fogged

Dotted surface

Then I created 32 bits .png that looks like this (checkerboard shows alpha)

Punch

When rendering my main character, I added this call:

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

The offset is only there to center the stroke with the sprite, basically what I pass to RemoveFogOfWar is the center of my sprite.

Remove the fog of war

Now the meat of the fog of war. I made two versions: the fog of war is finally removed, and the fog of war is reset. My war fog reset relies on my punch surface to have a path where alpha reset is 0 , and the fact that my character moves less pixels than the path contained in the frame, otherwise I would continue to Rect where my hit was applied, and I would replenish it before drawing another blow again.

Since I could not find a “multiple” mix with SDL, I decided to write a simple function that iterates on the surface of the punch and updates the alpha on the fog of the military surface. The most important part is to make sure that you stay within your surfaces, so it takes up most of the code ... there may be some processing functions, but I did not check:

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

What then gave me this with keepFogRemoved = false even after the character moved around

fog of war

And this is with keepFogRemoved = true

fog of war permanent

Validation

The important part is to really make sure that you are not writing outside your pixel buffer, so watch out for negative offsets or offsets that would take you out of width or height. To test my code, I added a simple call to RemoveFogOfWar when the mouse is pressed, and tried corners and edges to make sure I did not have a one-on-one problem

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

Notes

Obviously, you don't need a 32-bit texture for punch, but it was the clearest way I could imagine to show you how to do it. This can be done using only 1 bit per pixel (on / off). You can also add some gradient and change

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

To something like

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

To maintain a smooth mixture as follows:

enter image description here

3 State Fog of War

I thought I should add this ... I added a way to create a 3rd state fog of war: visible , seen and fogged .

To do this, I just keep SDL_Rect , where I last "pierced" the fog of war, and if alpha is below a certain value, I clamp it at that value.

So just adding

 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; 

right in front of the loop where the fog of war "breaks through", I get a fog of war, similar to what you might have in games like StarCraft:

3 state

Now, since the “visible” fog of war is translucent, you will need to set up a rendering method to properly fix the “enemies” that were in the fog, so you won’t see them, but you still see the terrain.

Hope this helps!

+26
source

All Articles