C# 如何更快地使屏幕失效?

C# 如何更快地使屏幕失效?,c#,drawing,invalidation,C#,Drawing,Invalidation,在我目前的《生活》项目游戏中,我需要重新绘制屏幕,因为大约有200个物体在移动。我可以想出两种方法,但不知道哪一种更快: 我可以: 1调用整个屏幕的Invalidate,并且在Paint handler中具有以下内容: void Paint(object sender, PaintEventArgs e) { foreach(Cell c in ListOfCells) { e.DrawImage(c,c.x,c.y); }

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

我可以: 1调用整个屏幕的Invalidate,并且在Paint handler中具有以下内容:

void Paint(object sender, PaintEventArgs e)
{
   foreach(Cell c in ListOfCells)
          {
             e.DrawImage(c,c.x,c.y);
          }
}
2或I可以使每个单元格的屏幕的每个部分无效:

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


}

并且使用与上面相同的处理程序

如果您坚持使用与第一个相同的绘制事件处理程序,那么第二个代码将不会有任何好处,因为基本上您将多次重绘所有单元格,因为对于每个单元格,您也将重绘所有其他单元格。要解决这个问题,您可以检查并只重新绘制该矩形内的单元格


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

如果您坚持使用与第一个代码相同的绘制事件处理程序,则第二个代码不会有任何好处,因为基本上您将多次重绘所有单元格,因为对于每个单元格,您还将重新绘制所有其他单元格。要解决这个问题,您可以检查并只重新绘制该矩形内的单元格


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

现在,第一条规则始终是不要过早优化。您需要确保您需要的是优化,而不是更干净的代码

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

您还需要了解使区域无效意味着什么。在Windows中,无效区域相加形成一个更新区域,以便WM_PAINT消息可以告诉程序要绘制屏幕的哪些部分。在绘制事件处理程序中,使用RectVisible确定是否刷新单元格

换言之,假设电路板尺寸n,则采用方法1的成本为:

(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=已更改的单元格数量,请参见下面的警告注释

因此,如果:

(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
换句话说,更改的单元格数必须大于1减去可见成本/重画成本的比率。现在,与在屏幕上绘制位图相比,RectVisible通常速度相当快,尤其是当您的单元格分辨率较高时。因此,Rectvisible/redraw通常是一个非常小的数字,使得方法1比方法2更快,仅当超过99%的电路板同时发生变化时,这是不可能的

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

警告-您的代码无法正常工作
但是,方法2中的代码不起作用。你必须记住,在最后一个周期中,你必须清除那些曾经有一个活细胞的死细胞。换句话说,你使改变的细胞无效,而不仅仅是那些活着的细胞。在多个循环中处于活动状态的单元不需要失效。因此,MyInvalidate方法中的逻辑是有缺陷的。

现在,第一条规则总是不要过早优化。您需要确保您需要的是优化,而不是更干净的代码

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

您还需要了解使区域无效意味着什么。在Windows中,无效区域相加形成一个更新区域,以便WM_PAINT消息可以告诉程序要绘制屏幕的哪些部分。在绘制事件处理程序中,使用RectVisible确定是否刷新单元格

换言之,假设电路板尺寸n,则采用方法1的成本为:

(n x n) x (redraw cell + update cell on screen)
请注意,我假设您记得在 e你画细胞,否则在最后一个周期中曾经活跃的细胞将留在屏幕上。因此,假设您在整个屏幕上绘制活动单元格或空白单元格

方法2的成本:

(v) x (redraw cell + update cell on screen) + (n x n) x (RectVisible call)
其中v=已更改的单元格数量,请参见下面的警告注释

因此,如果:

(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
换句话说,更改的单元格数必须大于1减去可见成本/重画成本的比率。现在,与在屏幕上绘制位图相比,RectVisible通常速度相当快,尤其是当您的单元格分辨率较高时。因此,Rectvisible/redraw通常是一个非常小的数字,使得方法1比方法2更快,仅当超过99%的电路板同时发生变化时,这是不可能的

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

警告-您的代码无法正常工作
但是,方法2中的代码不起作用。你必须记住,在最后一个周期中,你必须清除那些曾经有一个活细胞的死细胞。换句话说,你使改变的细胞无效,而不仅仅是那些活着的细胞。在多个循环中处于活动状态的单元不需要失效。因此,MyInvalidate方法中的逻辑存在缺陷。

比什么更快?200个对象并没有那么多…那么每20毫秒使整个屏幕失效比什么要慢呢?200个对象并不是那么多……每20毫秒使整个屏幕失效是相当慢的