好吧,所以我已经对这个主题做了一些研究,我发现的大多数解决方案都声称可以解决这个问题,但我发现它们的工作效果并不是很好。我正处于实现一个简单的小粒子引擎的早期阶段,没有什么可疯狂的我只是出于无聊而做到这一点。我以前没有用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();
}
}
我遇到的问题是,无论何时屏幕被清除和更新,它都会出现这种可怕的闪烁,不仅如此,但每当我发射粒子时它有时都不会。
表单基本上只是使用标签作为屏幕上的不可见位置来说明每个粒子的渲染位置。
无论如何,我之前已经看过这个话题,但没有任何东西可以解决任何问题,目前的实现是最少的flickery / laggy但是没有解决问题。
感谢任何帮助,谢谢!
编辑*我意识到我从来没有在每个循环中释放图形对象所以我这样做了,每当我点击发射器按钮时没有更多延迟,但闪烁仍然存在,我相应地更新了代码。
答案 0 :(得分:5)
摆脱可见的油漆工件需要双缓冲。换句话说,将场景渲染成后缓冲区,当准备就绪时,可以在一个步骤中快速进入屏幕表面。这是Winforms中的内置功能,只需在表单构造函数中将DoubleBuffered属性设置为 true 即可。您必须使用Paint事件来利用它。覆盖OnPaint()并调用RenderParticles(e.Graphics)。
你需要注意计时,现在你的UI线程正在燃烧100%核心,动画速度完全取决于粒子的数量和机器的速度。而不是Application.Idle,将Timer从工具箱中拖放到表单上。在Tick事件处理程序中,调用UpdateParticles()和this.Invalidate()以再次触发Paint事件。计时器的Interval属性值至关重要,您可以通过选择15或31毫秒(64或32 FPS)获得最可重现的更新速率。
您并不总是能够获得所需的FPS速率,如果计算机繁忙或速度太慢或者UI线程上的其他代码需要运行,计时器将简单地延迟或跳过Tick事件。要确保不影响动画,您必须测量实际经过的时间,而不是将粒子移动固定的量。 Environment.TickCount,DateTime.UtcNow或Stopwatch是衡量真实经过时间的合适方法。