战争迷雾和2D网格

时间:2012-12-18 15:37:56

标签: optimization

首先,我正在使用XNA框架开发2D策略游戏。

我正在为我的游戏实施2D战争迷雾。图形部分已经完成并且运行得非常好,但我现在正试图实现这场战争迷雾的“逻辑”部分。

我创建了一个代表我的关卡的2D网格。每个框架,每个单元使用Bresenham的算法(它似乎是确定给定圆中哪些单元格的最佳方法)更新围绕它的圆圈。 这实际上是有效的...当我想知道给定的位置是否可见时,我只需得到单元的状态...

问题是,当我有大量的衍生单位时,我的游戏运行得如此之慢...... 这个性能问题的第一个原因是,由于每个单元都更新了它周围的单元,很多单元都会多次更新......但我看不出任何解决方案...

所以...也许我错了以这种方式实现它,或者我错过了一个明显的优化,但我有点被卡住了......

以下是代码:

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

2 个答案:

答案 0 :(得分:5)

如果没有看到您的代码,我们就不得不猜测问题可能是什么。如果您已经分析了代码,那么您应该能够找出哪个部分特别慢;鉴于这些信息,您可以直接解决问题。

以下是关于哪些位可能很慢的几个猜测:

  • 圆圈是圆圈。你是否关注每个单位的Bresenham圆圈算法?看起来你可以只计算一次圆,相对于(0,0)。然后,对于(x,y)处的单位,您可以简单地查找圆圈并将圆中的点偏移到(x,y),然后将战争逻辑雾应用于该单位。

  • 战争迷雾只会改变最近移动过的单位。如果一个单位是静止的,那么您可能不需要再次计算其可见性。您是否能够将此类优化应用于战争迷雾可见性规则?

  • Bresenham的圆形算法可以帮助您绘制圆的边缘。你是如何填充圆圈内部的?您是否应该找到更好的算法来填充范围内部?

评论要求提供有关使用一个生成的圈子的更多详细信息,因此我在这里添加一些关于此的说明。(编辑答案的方式是这样的吗?抱歉,我相对较新Stack Exchange。)

“战争迷雾”通常意味着一个单位可以看到它周围的一些半径。你单位的半径是多少?每种单位类型的半径是否不同?有多少单位类型?

假设一个单位类型的可见范围半径为5个方格。这给我们留下了一个如下所示的圆圈:

00001110000
00010001000
00100000100
01000000010
10000000001
10000x00001
10000000001
01000000010
00100000100
00010001000
00001110000

由于我们有一个圆圈,我们知道我们不必做任何难以填充的事情。一个简单的算法将遍历最右边1和最左边1之间的每一行。这将比解决所有内部点的Breshenham算法快得多。

使用实心圆圈,我们找到这个数组,然后:

00001110000
00011111000
00111111100
01111111110
11111111111
11111x11111
11111111111
01111111110
00111111100
00011111000
00001110000

现在,如果我们有一个半径为5个方格可见度的单位,将战争雾应用于该单位只意味着将此预先计算的数组应用于战争数组的雾,以便此预先计算的数组的中心位于单位上我们正在处理。一旦从中心计算偏移量并将数组的边界剪切到地图的边缘,就可以使用简单的嵌套循环执行此操作。

如果您有几个不同的半径用于战争迷雾,您可以预先计算几个不同的阵列。如果你有规则说你的战争迷雾因障碍物(如地形或建筑物或风景)而变化,那么你必须做更多的处理,但仍然可以应用预计算的想法。

答案 1 :(得分:1)

K.I.S.S

为什么每次都需要计算一个圆圈?那很贵。

这样想。当您在paint.exe中编辑图片时,如果选择了大笔刷,那么认为每次计算该圈子?如何在Photoshop中选择花哨或自定义形状的画笔时,它是如何工作的?

你只需要计算一次圆圈,如果是偶数(我会硬编码,那里的伙伴已经给你一个快速通道)

const char circle[] = {
00001110000
00011111000
00111111100
01111111110
11111111111
11111x11111         <-- put this into a static C array
11111111111
01111111110
00111111100
00011111000
00001110000   
}

在那里,现在你可以使用该数组来比较整个战争阵列的雾。如果你遇到0,那么什么都不做(你可能有重叠)如果你有一个1,然后在你的战争阵列雾上设置一个。

如果您需要具有不同站点范围的单元,那么您可以为此计算或硬编码阵列。最多你最终会耗尽一千万字节的内存,但是你会节省很多周期。

但如果有障碍怎么办?那么你可能需要制作另一个包含高度数组的地图(你已经有了吗?)或视图阻挡区域。让我们说它是一个视图阻挡区域(高度无关紧要,它只是阻挡视图)。 而不是直接比较您的视图圆阵列(上面列出)。您需要将视图阻塞数组(也是1和0)进行比较。如果遇到一个表示视图被阻止的那个,那么您需要使用Bresenham函数从单元中射出一条直线您在视图阻塞数组中遇到一个点的位置,并且您将需要将该区域设置为可见。

我在那里沟通工作很差,如果你不明白我会回复你。

话虽如此,无论如何这可能是一种糟糕的方法。如果你每次移动一个单元时重新计算整个网格就很容易实现,但这将非常缓慢且效率低下。 要做到这一点,你必须清理FOW阵列移动后的单位标记。 (想想2d动画而不清除屏幕或双缓冲)