First of all, I’m working on a 2D strategy game, using XNA framework.
I’m implementing a 2D fog of war for my game. The graphic part is already done and works pretty nicely, but i’m now trying to implement the “logic” part of this fog of war.
I created a 2D grid representing my level. Each frame, each unit updates cells in a circle around it, using Bresenham’s algorithm (which seems to be the best way to figure out which cells are in a given circle).
This is actually working… When I want to know if a given position is visible or not, I just have to get the cell’s state…
The problem is, when I have large amount of spawned units, my game is running so slowly…
The first reason for this performance issue is that since every unit update the cells around it, lots of cells are updated multiple time… But I can’t see any solution for this…
So… Maybe I was wrong implement it this way, or maybe am I missing an obvious optimization, but I’m kind of stuck…
Here is the code:
class LevelGridCell
{
public void SetVisible(float a_time)
{
if (m_visibleTime < a_time)
m_visibleTime = a_time;
}
public bool IsVisible(float a_time)
{
return (m_visibleTime != 0f && m_visibleTime >= a_time);
}
float m_visibleTime = 0;
}
class LevelGrid
{
public LevelGridCell GetAt(int a_x, int a_y)
{
return m_grid[a_x + a_y * m_width];
}
public void SetVisible(float a_time, int a_x, int a_y, float a_radius)
{
GetAt(a_x, a_y).SetVisible(a_time);
int intRadius = (int)(a_radius / m_cellSize);
int x = 0, y = intRadius, p = 1 - intRadius;
PlotSetVisible(a_x, a_y, x, y, a_time);
while (x < y)
{
x++;
if (p < 0)
p += 2 * x + 1;
else
{
y--;
p += 2 * (x - y) + 1;
}
PlotSetVisible(a_x, a_y, x, y, a_time);
}
}
private void SafeSetVisible(int a_x, int a_y, float a_time)
{
if (a_x >= 0 && a_x < m_width && a_y >= 0 && a_y < m_height)
{
GetAt(a_x, a_y).SetVisible(a_time);
}
}
private void PlotSetVisible(int xctr, int yctr, int x, int y, float a_time)
{
for (int i = xctr - x; i <= xctr + x; ++i)
{
SafeSetVisible(i, yctr + y, a_time);
SafeSetVisible(i, yctr - y, a_time);
}
for (int i = xctr - y; i <= xctr + y; ++i)
{
SafeSetVisible(i, yctr + x, a_time);
SafeSetVisible(i, yctr - x, a_time);
}
}
List<LevelGridCell> m_grid = new List<LevelGridCell>();
float m_cellSize;
int m_width;
int m_height;
}
Without seeing your code, we’re obliged to guess at what the problem might be. If you have profiled your code, you should be able to figure out what part is specifically slow; given that information, you can attack the problem quite directly.
Here are a couple of guesses about which bits might be slow:
A circle is a circle. Are you following Bresenham’s Circle Algorithm for each unit? It seems like you could compute a circle just once, relative to (0,0). Then, for a unit at (x,y), you can simply look up the circle and offset the points in the circle to (x,y), then apply your fog of war logic to that unit.
Fog of war only changes for units that have recently moved. If a unit is stationary, then you might not need to compute its visibility again. Are you able to apply such an optimization to your rules for fog of war visibility?
Bresenham’s Circle Algorithm helps you plot the edge of the circle. How are you filling the inside of the circle? Should you find a better algorithm for filling the interior of the range?
A comment asked for more details about using one generated circle, so here I add some notes about that. (Is editing the answer the way to do that? Sorry, I’m relatively new to Stack Exchange.)
“Fog of war” usually means that a unit can see some radius around it. What’s that radius for your units? Does each unit type have a different radius? How many unit types are there?
Let’s say one unit type has a radius of 5 squares for its visibility range. That leaves us with a circle that looks like this:
Since we have a circle, we know that we don’t have to do anything too difficult to fill it. A simple algorithm would move through each row filling between the rightmost 1 and the leftmost 1. That’s going to be much faster than resolving Breshenham’s algorithm for all the interior points.
With the filled circle, we find this array, then:
Now, if we have a unit with a radius of 5 squares visibility, applying the fog of war to that unit just means applying this precomputed array to the fog of war array such that the center of this precomputed array is on the unit we’re processing. You could do that with a simple nested loop, once you compute the offsets from the center and clip the bounds of the array to the edge of your map.
If you have several different radii for fog of war visibility, you might pre-compute several different arrays. If you have rules that say your fog of war varies because of obstacles (like terrain or buildings or landscape) then you have to do a bit more processing, but the idea of pre-computation can still be applied.