4

首先,我正在使用 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;
}
4

2 回答 2

6

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

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

  • 一个圆圈就是一个圆圈。您是否对每个单元都遵循 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 平方可见度的单位,将战争迷雾应用到该单位只是意味着将这个预先计算的数组应用到战争迷雾数组,这样这个预先计算的数组的中心就在我们所在的单位上加工。您可以使用简单的嵌套循环来做到这一点,一旦您计算了中心的偏移量并将数组的边界剪辑到地图的边缘。

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

于 2012-12-18T15:45:58.527 回答
1

为什么每次都需要计算一个圆?这太贵了。

像这样想。当您在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)进行比较在视图阻塞数组中遇到一个的点,然后您将拥有需要设置为可见的区域。

我在那里的沟通工作有点糟糕,如果你不明白,我会回复你。

话虽如此,无论如何这可能是一个糟糕的方法。如果您每次移动一个单元时重新计算整个网格,这将很容易实现,但这将非常缓慢且效率低下。为此,您必须在 FOW 阵列移动后清理其上的单位标记。(在不清屏或双缓冲的情况下思考 2d 动画)

于 2013-04-29T03:49:08.407 回答