Fog-of-War in a side scrolling game?

Started by
7 comments, last by Peon 18 years, 2 months ago
I've been trying to figure out how I might be able to do this. I'm working on a sidescrolling game that mimics the gameplay of 'Thief', so having darkness/stealth is a major theme of the game. I used the search feature to look for similar problems, but couldn't find anything that I immediately thought was applicable. My question is about fog of war. For starters, I draw my entire tile-based map to the background, which is not too bad since the maps themselves are not very large. Each frame, I go to the backbuffer and grab the currently visible part of the map, do all my drawing, and then transfer it to the screen -- so far, so good. Over the map however, I'd like to apply a "fog of war" that should initially be black and later, semi-transparent. I started by creating a huge boolean array that had as many pixels as the map, and then grabbing the section I need and drawing black pixels. Unfortunately this was too slow, since I could draw up to 1024x768 = a lot of pixels each frame. I then caved in and tried to just have individual tiles blacked out, so I'd be drawing 1024*768/(tile size) rectangles, but unfortunately, this is also too slow. EDIT: Forgot to mention, I'm doing this in Java. Does anyone have any suggestions? I know this should be fairly simple, but I feel like I'm missing something. Here is a mock-up I made of my game, in Photoshop, in case my description wasn't clear:
Peon
Advertisement
So if you draw two tiles at a location, vs one, your performance falls through the floor?
Well, when I'm drawing the map, I'm grabbing the portion of the map from one of the backbuffers, and then rendering it all in a big chunk -- this is done with Java's drawImage function I believe, so I assumed it was more efficient than me using a simple for-loop to go through and check to see if I should draw a rectangle.

The map never changes, so I can grab it in one big chunk, but it seems like the constant looping, and checking, to see if a tile should be masked and then drawing individual rectangles (or pixels) is what's killing performance. Without any fog of war, it runs fine.
Peon
Are you checking Fog for ALL the tiles in the map?
Or just the ones that are visible on screen?
Just ones that are on the screen. I ask my Map object for a reference to the array that is holding the fog of war data (a boolean array), and then I loop over the portion that is currently visible, and then drawRect to screen over the map I've drawn, for each tile that is visible. Since my tiles are 32x32, I'm drawing approximately 768 rectangles per frame, which is quite a lot, but still quite a bit less than the pixel drawing I tried to do earlier :)
Peon
Ok, I dont use Java
But Im going to say that For Any language, 768 individual function calls to draw 768 separate Rectangles is waaay too much.
All the overhead of that function is killing your performance.

You need to cut it down even further
Your map is made of tiles right? Can you just have a fog for each tile?
Currently it sounds like your Rectangles are smaller than your tiles and you have many of the per tile.
Maybe the reason you are doing it that way is so that you can have rounded boundaries for the fog? Can you do some other method, like having a round fog bitmap?

Also consider, that if half the screen is fogged, it doesnt make sense to have 768/2 separate rectangles drawn to cover that part of the screen. Come up with a way to draw just one big rectangle that covers that area.



Heres another fun idea:
rather than draw the world, then draw fog on top of it
why not start with a black screen
and draw only the sections of world with no fog on them?
This way you're not drawing on top of yourself, cuts down on wasted time drawing stuff that's goign to be painted black afterwards.
Maybe you could have a dynamic stencil buffer that is eaten away as the character progresses. The shaded area can just be rendered on a non-white plane.
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)
Ok, here's a quick way to get you some performance with Minimal modification to your code.
This is Not the best approach, but you can get it done in 5mins and it will Help.

Right now, I think your code probably is something like this:
for(y=start to finish){   for(x=start to finish){       if(fogmap[x][y]==TRUE){            drawRectangle(x,y, x2,y2);       }   }}


Change it to do this instead:
This code Is PseuedoCode, not Java, you need to figure it out and debug it
int Fogmode=0;float Fogstart=0;for(y=start to finish){   for(x=start to finish){       if(fogmap[x][y]==TRUE){            if(Fogmode==0){                      Fogmode=1;                Fogstart=x;            }       }else{            if(Fogmode==1){                Fogmode=0;                drawRectangle(Fogstart,y, x,y);            }       }   }   if(Fogmode==1){         Fogmode=0;         drawRectangle(Fogstart,y, x,y);   }}



So the idea is, rather than draw Separate Fog Rectangles
for each row it keeps track of how many continous Fog Rectangles there are supposed to be, then draws them all at once with a big stretched Rectangle.
You were actually right on with how my code is right now -- the loop is almost identical to that.

I'll definitely give your contiguous rectangle idea a try and see how that speeds things up. One reason I didn't want to draw the fog first, and then the world is that ideally, I'd like "explored" tiles have a transparent fog over them, so that if you've already explored an area, you remember what its geography looks like.

I'll let you know how it goes :)
Peon

This topic is closed to new replies.

Advertisement