在C#WinForms程序中闪烁屏幕清除
本文关键字:屏幕 清除 闪烁 C#WinForms 程序 | 更新日期: 2023-09-27 18:26:43
好吧,所以我对这个主题做了一些研究,我找到的大多数解决方案都声称可以解决这个问题,但我发现它们并不完全正确。我正处于实现一个简单的小粒子引擎的早期阶段,没什么疯狂的,我只是出于无聊才这么做。我以前没有用WinForms做过这样的事情,我当然用过C/C++,但这对我来说是一件新鲜事。以下是我用来将粒子绘制到屏幕上的代码,粒子的锅炉板代码并不相关,因为它工作得很好,我更好奇我的实际游戏循环。
以下是更新和重绘的主要代码
public MainWindow()
{
this.DoubleBuffered = true;
InitializeComponent();
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle(object sender, EventArgs e)
{
Graphics g = CreateGraphics();
while (IsApplicationIdle())
{
UpdateParticles();
RenderParticles(g);
g.Dispose();
}
}
//Variables for drawing the particle
Pen pen = new Pen(Color.Black, 5);
Brush brush = new SolidBrush(Color.Blue);
public bool emmiter = false;
private void EmitterBtn_Click(object sender, EventArgs e)
{
//Determine which emitter to use
if (emmiter == true)
{
//Creates a new particle
Particle particle = new Particle(EmitterOne.Left, EmitterOne.Top, .5f, .5f, 20, 20);
emmiter = false;
}
else if(emmiter == false)
{
Particle particle = new Particle(EmitterTwo.Left, EmitterTwo.Top, -.5f, .5f, 20, 20);
emmiter = true;
}
}
public void RenderParticles(Graphics renderer)
{
Invalidate();
Thread.Sleep(0);
//Iterate though the static list of particles
for (int i = 0; i < Particle.activeParticles.Count; i++)
{
//Draw Particles
renderer.DrawRectangle(pen, Particle.activeParticles[i].x,
Particle.activeParticles[i].y,
Particle.activeParticles[i].w,
Particle.activeParticles[i].h);
}
}
public void UpdateParticles()
{
for (int i = 0; i < Particle.activeParticles.Count; i++)
{
//Move particles
Particle.activeParticles[i].MoveParticle();
}
}
我遇到的问题是,每当屏幕被清除和更新时,它都会出现可怕的闪烁,不仅如此,有时我发射粒子时也不会。
这种形式基本上只是使用标签作为屏幕上的不可见位置,来说明在哪里渲染每个粒子。
无论如何,我以前见过这个主题,但没有任何东西能解决任何问题,当前的实现是最不闪烁/滞后的,但并不能解决问题。
感谢您的帮助!
EDIT*我意识到我从未在每个循环中取消分配图形对象,所以我这样做了,每当我单击发射器按钮时都不会有更多延迟,但闪烁仍然存在,我相应地更新了代码。
要去除可见的绘画工件需要双重缓冲。换言之,将场景渲染到后台缓冲区中,当后台缓冲区准备就绪时,可以在一步中快速闪电传输到屏幕表面。这是Winforms中的一个内置功能,只需在表单构造函数中将DoubleBuffered属性设置为true即可。必须使用"绘制"事件才能利用这一点。覆盖OnPaint()并调用RenderParticles(e.Graphics).
你需要注意时间,现在你的UI线程正在燃烧100%的核心,动画速度完全取决于粒子的数量和机器的速度。将Timer从工具箱中放到表单上,而不是Application.Iidle。在Tick事件处理程序中,调用UpdateParticles()和this。Invalidate()可使"绘制"事件再次启动。计时器的"间隔"属性值至关重要,您可以通过选择15或31毫秒(64或32 FPS)来获得最可复制的更新速率。
您并不总是能获得所需的FPS速率,如果机器繁忙或速度太慢,或者UI线程上的其他代码需要运行,计时器只会延迟或跳过Tick事件。为了确保这不会影响动画,必须测量实际经过的时间,而不是按固定的量移动粒子。Environment.TickCount、DateTime.UtcNow或Stopwatch都是测量真实运行时间的合适方法。