1

在我当前的项目(生活游戏)中,我需要重新绘制屏幕,​​因为大约有 200 个物体在移动。我可以想到两种方法,但不知道哪种方法会更快:

我可以:
1) 为整个屏幕调用 Invalidate() 并在 Paint 处理程序中具有以下内容:

void Paint(object sender, PaintEventArgs e)
{
   foreach(Cell c in ListOfCells)
          {
             e.DrawImage(c,c.x,c.y);
          }
}

2)或者我可以使每个单元格的屏幕的每个部分无效:

public void MyInvalidate()
{
  foreach(Cell c in ListOfCells)
              {
                 Invalidate(c.X,c.Y,c.Width,c.Height);
              }


}

并具有与上述相同的处理程序

4

2 回答 2

4

现在,第一条规则始终是“不要过早优化”。您需要确保您需要对更简洁的代码进行优化。

现在,在人生游戏中,您最初看到的是一个相当空的屏幕。然而,随着游戏的进行,越来越多的单元格会被填满,最终会填满整个棋盘。现在 GOL 规则规定这些单元中的大多数将从一个周期变为另一个周期。

您还需要了解“使”区域“无效”的含义。在 Windows 中,无效区域“加起来”形成“更新区域”,因此 WM_PAINT 消息可以告诉程序要绘制屏幕的哪些部分。在您的 Paint 事件处理程序中,您使用RectVisible来确定是否要刷新单元格。

换句话说,执行方法 1 的“成本”(假设板尺寸 n):

(n x n) x (redraw cell + update cell on screen)

请注意,我假设您在绘制单元格之前记得做一个清晰的屏幕,否则在最后一个周期中曾经“活着”的单元格将留在屏幕上。因此,这假设您在整个屏幕上绘制一个活单元格或一个空白单元格。

做方法2的“成本”:

(v) x (redraw cell + update cell on screen) + (n x n) x (RectVisible call)

其中 v = 更改的单元格数(请参阅下面的警告说明)。

因此,在以下情况下,方法 1 比方法 2 更快:

(n x n) x redraw < v x redraw + (n x n) x RectVisible
n2 x (redraw - RectVisible) < v x redraw
v > ((redraw - RectVisible) / redraw) x n2
v > (1 - RectVisible/redraw) x n2

换句话说,更改单元格的数量必须大于一减去(RectVisible 成本/重绘成本)的比率。现在,RectVisible与在屏幕上绘制位图相比,它通常非常快,尤其是在您的单元格具有高分辨率的情况下。因此,Rectvisible/redraw 通常是一个非常小的数字,只有当有超过 99% 的板同时发生变化时,方法 1 才比方法 2 更快,这是不太可能的。

换句话说,您会发现方法 2 通常会产生更高的性能,因为DrawImage如果要在剪切区域之外(即不在更新区域内)绘制图像,通常会跳过整个操作。

警告 - 您的代码无法正常工作

但是,您在方法 2 中的代码将不起作用。您必须记住在上一个周期中“清除”曾经有“活”细胞的“死”细胞。换句话说,您使“更改”的单元格无效,而不仅仅是那些“活着”的单元格。多个周期“活着”的细胞不需要失效。因此,您的方法中的逻辑MyInvalidate是有缺陷的。

于 2011-04-17T08:24:21.570 回答
0

如果您坚持使用与第一个相同的 Paint 事件处理程序,第二个代码将没有任何好处,因为基本上您将多次重绘所有单元格(因为对于每个单元格,您将重绘所有其他单元格)细胞也是如此)。要解决此问题,您可以检查e.ClipRectangle,并仅重绘该矩形内的单元格。

但是,如果您的屏幕上只有单元格(因此您没有大量其他显示元素,例如 UI 控件),那么第一种方法是您将获得的最佳方法(即 Invalidate( ) 整个屏幕)。仅当您使屏幕上许多其他未更改的区域无效时,才会发生性能损失。

于 2011-04-17T07:44:40.127 回答