程序清单3-15 在屏幕上绘制弹跳颜色块
private void Method1_SimpleDraw_Paint(object sender, PaintEventArgs e)
{
// Draw our box at its current location
using (Brush b = new SolidBrush(Color.Blue))
{
gfx.FillRectangle(b, xpos, ypos, boxSize, boxSize);
}
}
这段代码完成了基础工作。但是,动画的闪烁情况很严重。您可以运行配套下载代码中的3_4_RenderingMethods示例项目,然后选择使用SimpleDraw呈现模式就能看到这种情况。虽然颜色块可以按照我们期望的方式移动,但移动时有比较严重的闪烁。这种类型的图形显示显然不能用于任何游戏。
注意:
由于不同的仿真器缓冲区对屏幕的更新方式不同,所以在有一些模拟器上运行该示例时闪烁可能不是特别明显。要看该动画的真实情况,要在实际的手机上运行程序。
如何才能避免发生这样的闪烁呢?我们看看在生成图形时实际发生了什么情况。
您会注意到Paint事件中并没有实际对窗体进行清除,但我们看到颜色块在移动时身后并没有产生轨迹。这是因为每次绘图时GDI会自动清除屏幕,然后才会绘制新的颜色块。由于所有这些操作都直接发生窗体上,而窗体正是我们所能看到的,窗体上的清除操作与绘图操作交互出现:在当清除操作已经完成而颜色块尚未绘制完成的间隙,窗体是空白的。
为了避免这种冲突,我们改变一下图形的绘制方式。不直接在屏幕上进行绘制,而是创建一个屏幕外缓冲区,并且在其中进行所有的绘图操作。在绘图的同时,屏幕上先前显示的东西都还保留在原位。
当所有的绘图工作完成后,用一个单独的操作将完整的缓冲区中的图像复制到屏幕上。这样,每个生成的画面可以直接切换到下一个画面,而看不到任何分步完成的图形操作。这个技术被称为双缓冲,因为使用了两个图形缓冲区:可见部分的缓冲区(显示在屏幕上)以及非可见部分的后台缓冲区(在该缓冲区中生成图形)。
在使用双缓冲进行绘图时还有一个不同的地方,由于我们是自己创建的后台缓冲区,因此可以对它进行完全的控制。与直接在窗体中进行绘制有所不同,GDI不会干扰缓冲区中已存在的内容,这意味着在上一个画面中所绘制的东西在下一次绘制时仍然存在。这非常有用,因为任何实际上没有发生移动的图形都可以保留在原地而不需要对它进行重绘。在下一章中我们会根据这种方法对游戏进行有效地优化。
此外还需要在绘制颜色块之前将缓冲区清空。否则在运行该动画时,颜色块的后面会存在拖尾,程序清单3-16演示了如何对后台缓冲区进行初始化,如何在后台缓冲区中绘图以及将它复制到窗体上使图形可见。