Fog of War/LoS
by Derelict · in Torque Game Builder · 04/08/2008 (3:38 pm) · 9 replies
I'm trying to come up with a quick and dirty solution for a "fog of war" styled effect in TGB and I can't seem to come up with a satisfactory solution. Does anyone have a direction or existing resource to point me toward?
Mind you, I don't need anything sophisticated or particularly refined--just enough to get by in a prototype situation.
To provide a little more info, basically I have a tilemap "playmat" area that I want to limit a players sight range relative to his or her units on the map. At this point I don't need a tiered fog (i.e., black, explored, los) though I wouldn't complain if it could be quickly accomplished *grin*.
So far, I've mostly experimented with the use of blending and I figure there is a solution in it somewhere, I just can't seem to come to the right method. My current version does produce a LoS effect but only for a single unit (i.e., the view range does not overlap for units--only one unit can actually "see"). It uses a black graphic with a small white circle (representing the view area), and I blend that using Source: DST_COLOR, Destination: ZERO.
As mentioned, this does create a limited sight range around a unit but does not overlap with other such units/fog graphics.
I'm thinking that a single solid black graphic that covers the play area is probably the right start. Then I would use another graphic mounted to each unit object (sized according to their view radius) that, properly blended, would leave the map black except for those areas blended by the "view" graphic to reveal the surrounding tilemap.
Any suggestions or pointers are appreciated!
Mind you, I don't need anything sophisticated or particularly refined--just enough to get by in a prototype situation.
To provide a little more info, basically I have a tilemap "playmat" area that I want to limit a players sight range relative to his or her units on the map. At this point I don't need a tiered fog (i.e., black, explored, los) though I wouldn't complain if it could be quickly accomplished *grin*.
So far, I've mostly experimented with the use of blending and I figure there is a solution in it somewhere, I just can't seem to come to the right method. My current version does produce a LoS effect but only for a single unit (i.e., the view range does not overlap for units--only one unit can actually "see"). It uses a black graphic with a small white circle (representing the view area), and I blend that using Source: DST_COLOR, Destination: ZERO.
As mentioned, this does create a limited sight range around a unit but does not overlap with other such units/fog graphics.
I'm thinking that a single solid black graphic that covers the play area is probably the right start. Then I would use another graphic mounted to each unit object (sized according to their view radius) that, properly blended, would leave the map black except for those areas blended by the "view" graphic to reveal the surrounding tilemap.
Any suggestions or pointers are appreciated!
#2
I have considered that and it will probably work for my purposes right now. However, the reason I haven't gone that route yet is because of a concern regarding performance. Given enough units moving about, it could become fairly intensive to do. I suppose actually doing it will answer the question. :)
You don't think some sort of blending solution would be easier, both in terms of processing and performance? Granted, this is just for a prototype but I don't want to find myself suddenly dealing with massive slow downs or something that'll cause me to scrap it entirely and have to head back to the drawing board.
A thought did just strike me, however, and I think it could turn out better than I might expect. It would use a mixture of the two, except no blending occurs. Additionally, what I'm about to suggest would allow for graded levels of sight (i.e., the black, explored, and visible variants).
Basically, a solid black tile map would exist over the play area. Then, I mount an invisible scene object the size of a units sight range to it. The tiles on the black tile map would all be collision sensitive, but only to the invisible scene object. If they are in direct contact with it they would be fully translucent.
With a little work, those that have been "visited" would only return to partial opacity (gray/explored) and a little further work would ensure that enemy units within such a grey area would not be visible to the player.
At least it is a start for how it might work, and shouldn't take too long to implement. I suspect that a lot of processing could be reduced by a method like that, as opposed to having to loop through units and loop through surrounding tiles to handle the opacities.
04/08/2008 (5:35 pm)
Thanks for the response, James.I have considered that and it will probably work for my purposes right now. However, the reason I haven't gone that route yet is because of a concern regarding performance. Given enough units moving about, it could become fairly intensive to do. I suppose actually doing it will answer the question. :)
You don't think some sort of blending solution would be easier, both in terms of processing and performance? Granted, this is just for a prototype but I don't want to find myself suddenly dealing with massive slow downs or something that'll cause me to scrap it entirely and have to head back to the drawing board.
A thought did just strike me, however, and I think it could turn out better than I might expect. It would use a mixture of the two, except no blending occurs. Additionally, what I'm about to suggest would allow for graded levels of sight (i.e., the black, explored, and visible variants).
Basically, a solid black tile map would exist over the play area. Then, I mount an invisible scene object the size of a units sight range to it. The tiles on the black tile map would all be collision sensitive, but only to the invisible scene object. If they are in direct contact with it they would be fully translucent.
With a little work, those that have been "visited" would only return to partial opacity (gray/explored) and a little further work would ensure that enemy units within such a grey area would not be visible to the player.
At least it is a start for how it might work, and shouldn't take too long to implement. I suspect that a lot of processing could be reduced by a method like that, as opposed to having to loop through units and loop through surrounding tiles to handle the opacities.
#3
In all, it works very well with the exception that the collisions have a notable lag and the map is revealed inefficiently. Naturally, reducing the number of tiles in the tile map speeds it up but it also has a less desirable appearance.
Beyond that, ignoring the looks, it simply may not be practical because of the collision-lag. I'm yet to test it with more than a couple units at a time (this on a fairly small grid--32x32 tiles, 50x38 in size). Oddly, it is not an actual slow down in the game performance. Just a visible lag occurring between the "vision" object and the tiles.
04/08/2008 (9:40 pm)
Just wanted to drop back in and remark that I do have it working as mentioned above (though only FOW and no FOW--I haven't yet turned my attention to the "explored" bit yet).In all, it works very well with the exception that the collisions have a notable lag and the map is revealed inefficiently. Naturally, reducing the number of tiles in the tile map speeds it up but it also has a less desirable appearance.
Beyond that, ignoring the looks, it simply may not be practical because of the collision-lag. I'm yet to test it with more than a couple units at a time (this on a fairly small grid--32x32 tiles, 50x38 in size). Oddly, it is not an actual slow down in the game performance. Just a visible lag occurring between the "vision" object and the tiles.
#4
What if you keep all objects that affect FOW in a simset, then each tick:
The only inefficiency is having to set every tile black to start with, but you can make it more efficient
by storing the last-tile-coord for each unit. Then only setting that tile ( and the sight radius around it ), black each tick.
04/09/2008 (10:24 am)
If you use collisions it will definitely slow this down ( and more so for each unit you add ).What if you keep all objects that affect FOW in a simset, then each tick:
function updateFOW()
{
// loop through all tilelayer tiles...
// for each one: tilelayer.setStaticTile( tilex, tiley, blacktileimagemap );
// now set ones with units nearby to be transparent
for ( %i = 0; %i < fowset.getCount(); %i++ )
{
%obj = foxset.getObject(%i);
%pos = %obj.getPosition();
%tile = tilelayer.pickTile( getWord(%pos,0), getWord(%pos,1) );
tilelayer.setStaticTile( getWord(%tile,0), getWord(%tile,1), transparentTileImageMap );
// assuming units have a radius on sight not just the tile they are on
// use a loop to set tiles within that sightradius also transparent
}The only inefficiency is having to set every tile black to start with, but you can make it more efficient
by storing the last-tile-coord for each unit. Then only setting that tile ( and the sight radius around it ), black each tick.
#5
Consider a small map area of 40x40 tiles. Imagine also that each unit has a sight radius of 4x4 tiles. For argument and simplicity, presume that a unit occupies one of those tiles as well so the radius isn't 1+4 tiles in each direction.
The "reset to black" part would entail 1,600 tile updates each tick. Each unit in the play area would cause an additional 16 tile updates per tick. In all, a unit represents roughly 1% of the processing compared to the "reset to black" operation, given a 4x4 sight radius. So 10 units would equate to some 10% increase in overhead and 100 units would double it. This, on a map that isn't particularly large.
I considered this sort of thing when I first came to the problem but decided, at the least, it would take more work than some other possibilities and that I wouldn't mess with that idea too much until later, if at all. I figured that another approach might be easier to implement while proving to be a better solution.
Even with all that in mind, what you suggest does have merit and could be optimized quite a bit. For example, it is really only necessary to track which tiles have been set to transparent and then iterate over those, instead of the entire map. Using a SimGroup could be nice as you would be sure that only one instance of a tile "marker" is stored in the group.
Doing it that way has another advantage--it is very easy to simply reset tiles to half opacity, creating an "explored" effect. Black tiles are never dealt with at all unless a unit is exposing them.
I'll probably give this approach a whirl once I'm moving into a real implementation (i.e., post-prototype) as I suspect it'll take more time to work through properly than I'm willing to dedicate right now.
It is really a shame that the collision method is so inefficient because it works very well and some other nifty things could be done along the same line. I think the actual issue with it is due to the rate for which a "tick" occurs and that a collision is only figured for the most immediate tiles. If the collision had "depth" into the tiles (and not just the ones closest to the unit), it would swipe all of the necessary tiles away in one shot, making it a perfect effect.
04/09/2008 (5:04 pm)
That seems like it could be a very expensive approach. Consider a small map area of 40x40 tiles. Imagine also that each unit has a sight radius of 4x4 tiles. For argument and simplicity, presume that a unit occupies one of those tiles as well so the radius isn't 1+4 tiles in each direction.
The "reset to black" part would entail 1,600 tile updates each tick. Each unit in the play area would cause an additional 16 tile updates per tick. In all, a unit represents roughly 1% of the processing compared to the "reset to black" operation, given a 4x4 sight radius. So 10 units would equate to some 10% increase in overhead and 100 units would double it. This, on a map that isn't particularly large.
I considered this sort of thing when I first came to the problem but decided, at the least, it would take more work than some other possibilities and that I wouldn't mess with that idea too much until later, if at all. I figured that another approach might be easier to implement while proving to be a better solution.
Even with all that in mind, what you suggest does have merit and could be optimized quite a bit. For example, it is really only necessary to track which tiles have been set to transparent and then iterate over those, instead of the entire map. Using a SimGroup could be nice as you would be sure that only one instance of a tile "marker" is stored in the group.
Doing it that way has another advantage--it is very easy to simply reset tiles to half opacity, creating an "explored" effect. Black tiles are never dealt with at all unless a unit is exposing them.
I'll probably give this approach a whirl once I'm moving into a real implementation (i.e., post-prototype) as I suspect it'll take more time to work through properly than I'm willing to dedicate right now.
It is really a shame that the collision method is so inefficient because it works very well and some other nifty things could be done along the same line. I think the actual issue with it is due to the rate for which a "tick" occurs and that a collision is only figured for the most immediate tiles. If the collision had "depth" into the tiles (and not just the ones closest to the unit), it would swipe all of the necessary tiles away in one shot, making it a perfect effect.
#6
It wouldn't be hard to move this into C++ though, which would likely yield a lot better performance.
eg..
Anyway I haven't experimented with the collision method you mentioned, this is just the first idea for how I would do a really really simple FOW effect.
Actually, to do this the right way... You might derive a new guiControl like... guiFOWControl, which would hold a texture which gets rendered to the screen. Then you actually modify the texture each tick. You might look at how bitmapCtrls make/hold-onto textures.
04/09/2008 (5:36 pm)
Switching a tile from one mode to another shouldn't be very performance intensive. But since this is happening in script, any loop with a lot of iterations could be doing a ton of string operations. It wouldn't be hard to move this into C++ though, which would likely yield a lot better performance.
eg..
// In script:
$fowlayer.updatefow();
// In C++:
ConsoleMethod( t2dTileLayer, updateFOW, void, 2, 2, "" )
{
SimSet fowset;
if ( !Sim::findObject( "fowset", fowset ) )
return;
// loop through set doing the same thing, just faster now '-)
}Anyway I haven't experimented with the collision method you mentioned, this is just the first idea for how I would do a really really simple FOW effect.
Actually, to do this the right way... You might derive a new guiControl like... guiFOWControl, which would hold a texture which gets rendered to the screen. Then you actually modify the texture each tick. You might look at how bitmapCtrls make/hold-onto textures.
#7
Your GUI control idea is a nifty one. I was wondering a little while back if some sort of method involving "painting" and "erasing" might be a good solution. I'm not familiar enough with anything in that direction to know where to start--not without a decent bit of research first. I figured that such a thing was so similar to blending/translucency that it would end up being a lot of time spent for the same effect.
Still, a GUI control ... hmm ...
Well, if nothing else you've certainly help stir the mental pot, so to speak. I think I have got enough to work with at the moment and you've helped me come to a few likely directions to pursue when the time comes. Thanks for that!
04/09/2008 (10:11 pm)
*nods* I agree. It could be that doing this engine side will end up lending the best result. Maybe that is where I'll end up, no less. For the moment I'm trying to avoid anything like that--for all I know, the FoW might not even make it into an actual implementation! (Okay, okay ... it probably will, lol).Your GUI control idea is a nifty one. I was wondering a little while back if some sort of method involving "painting" and "erasing" might be a good solution. I'm not familiar enough with anything in that direction to know where to start--not without a decent bit of research first. I figured that such a thing was so similar to blending/translucency that it would end up being a lot of time spent for the same effect.
Still, a GUI control ... hmm ...
Well, if nothing else you've certainly help stir the mental pot, so to speak. I think I have got enough to work with at the moment and you've helped me come to a few likely directions to pursue when the time comes. Thanks for that!
#8
Heres an interesting idea, which would eliminate the blocky edges of Fog while using the tile approach...
Just use more than two types of tiles! Have a top-left rounded tile, a top-right rounded tile, etc... Then the FoW will still expand and contract as a unit moves in discrete units, BUT it will at least look a ton better while stationary.
Thinking back to playing WC2, I believe this is exactly what they did. I remember how the FoW would "creep" forward/back as you moved units in discrete units ( not too fluidly ), but, it still looked pretty nice with rounded edges on the boarders of the Fog.
This could easily be achieved, even without too dense a tilelayer, by adding some logic for placing the appropriately "rounded" tile on the boarders between Visible/Fog.
But, I am also very curious to see how creating/modifying textures in C++ is done -- and what type of effect this could achieve for FoW. So, if I have some time I may try to implement the aforementioned control.
One benefit of using a GUI control rather than a tilelayer, is you have a much smaller object to update all the time. If your total map size is larger than one screen, to cover it with a tilelayer could require a very large tilelayer. ( of course theres other ideas - like have a separate scenewindow for the FoW tilelayer - which does not pan with your in-game camera, and stays directly over the normal scenewindow ).
04/10/2008 (9:49 am)
Glad to be of help!Heres an interesting idea, which would eliminate the blocky edges of Fog while using the tile approach...
Just use more than two types of tiles! Have a top-left rounded tile, a top-right rounded tile, etc... Then the FoW will still expand and contract as a unit moves in discrete units, BUT it will at least look a ton better while stationary.
Thinking back to playing WC2, I believe this is exactly what they did. I remember how the FoW would "creep" forward/back as you moved units in discrete units ( not too fluidly ), but, it still looked pretty nice with rounded edges on the boarders of the Fog.
This could easily be achieved, even without too dense a tilelayer, by adding some logic for placing the appropriately "rounded" tile on the boarders between Visible/Fog.
But, I am also very curious to see how creating/modifying textures in C++ is done -- and what type of effect this could achieve for FoW. So, if I have some time I may try to implement the aforementioned control.
One benefit of using a GUI control rather than a tilelayer, is you have a much smaller object to update all the time. If your total map size is larger than one screen, to cover it with a tilelayer could require a very large tilelayer. ( of course theres other ideas - like have a separate scenewindow for the FoW tilelayer - which does not pan with your in-game camera, and stays directly over the normal scenewindow ).
#9
Use of a GUI control does sound like an interesting and perhaps quite effective approach. Still, I'm forced to think that a truly elegant solution exists somewhere in a blending approach that should be, hypothetically, a very easy and fast to implement solution while remaining performance friendly even on large maps containing numerous units.
It would essentially be a "painting" solution and would require only that you mount the view radius objects to any unit in question. Consider it this way, imagine you have two view radius objects. One turns tiles it contacts completely transparent while the second turns tiles to 50% opacity. The second (50%) view object is larger than the former, and you mount both to any unit in question. It would produce a pair of "brushes" (if you will) that look like this:
5 = 50% opacity "brush"
0 = 0% opacity "brush"
x = Standard black tile
u = unit
Tiles within the units line of sight ("0" brush area) would be "clear" and completely visible. As the unit moves, the 50% brush would re-paint previously visible tiles as 50% opacity ones, making a trail of "explored" but no longer actively visible areas. Make sense?
Of course, it doesn't specifically have to be tiles that the brushes deal with. It could be that they literally are erasing and painting the opacity level of a "paint-able" texture area. It makes me wonder if the use of two new types of controls--one representing the "paint-able" surface and another that is merely the brush control that can be set to modify opacity on the other. Slap the former surface over your entire play area and one or more brush controls onto your units.
That method would produce a neat effect for which tiles that a unit is approaching wouldn't be exposed outright, but only begin to become transparent. The same with tiles it is moving away from. So, some idea as to the terrain just beyond direct LOS is revealed but not, per se, the presence of enemy or neutral units that may reside there. Sort of a "horizon" effect existing with the units LOS.
Ah, well, FOW has dropped down on my priority list quite a bit, for the moment, as even a blocky and somewhat cludgy version is adequate for me to get by in the prototype I'm working on. Not sure when I'll return to it but I'm quite sure I will at some point. In the meantime, if you do end up pursuing your ideas on FOW I'd be very interested in seeing what you cook up!
04/11/2008 (4:18 am)
It has been a while since I've played WC2 but you have me curious, so I'll have to see if I can find my copy (wherever that may be at this point!). It would be interesting to survey a few other games to see variations in FOW implementations that may exist. Use of a GUI control does sound like an interesting and perhaps quite effective approach. Still, I'm forced to think that a truly elegant solution exists somewhere in a blending approach that should be, hypothetically, a very easy and fast to implement solution while remaining performance friendly even on large maps containing numerous units.
It would essentially be a "painting" solution and would require only that you mount the view radius objects to any unit in question. Consider it this way, imagine you have two view radius objects. One turns tiles it contacts completely transparent while the second turns tiles to 50% opacity. The second (50%) view object is larger than the former, and you mount both to any unit in question. It would produce a pair of "brushes" (if you will) that look like this:
5 = 50% opacity "brush"
0 = 0% opacity "brush"
x = Standard black tile
u = unit
x x x x x x x x 5 5 5 5 5 x x 5 0 0 0 5 x x 5 0 u 0 5 x x 5 0 0 0 5 x x 5 5 5 5 5 x x x x x x x x
Tiles within the units line of sight ("0" brush area) would be "clear" and completely visible. As the unit moves, the 50% brush would re-paint previously visible tiles as 50% opacity ones, making a trail of "explored" but no longer actively visible areas. Make sense?
Of course, it doesn't specifically have to be tiles that the brushes deal with. It could be that they literally are erasing and painting the opacity level of a "paint-able" texture area. It makes me wonder if the use of two new types of controls--one representing the "paint-able" surface and another that is merely the brush control that can be set to modify opacity on the other. Slap the former surface over your entire play area and one or more brush controls onto your units.
That method would produce a neat effect for which tiles that a unit is approaching wouldn't be exposed outright, but only begin to become transparent. The same with tiles it is moving away from. So, some idea as to the terrain just beyond direct LOS is revealed but not, per se, the presence of enemy or neutral units that may reside there. Sort of a "horizon" effect existing with the units LOS.
Ah, well, FOW has dropped down on my priority list quite a bit, for the moment, as even a blocky and somewhat cludgy version is adequate for me to get by in the prototype I'm working on. Not sure when I'll return to it but I'm quite sure I will at some point. In the meantime, if you do end up pursuing your ideas on FOW I'd be very interested in seeing what you cook up!
Associate James Ford
Sickhead Games
Each update, start it as all black, then loop through each unit that is capable "seeing". You probably will turn the tile that unit is on transparent, and some radius of tiles around that tile.