我在OpenGL上下文窗口中运行了一些动画,所以我需要不断重绘它。所以我想出了以下代码:
void InitializeRedrawTimer()
{
var timer = new Timer();
timer.Interval = 1000 / 60;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
glWin.Draw();
}
但这只能给我40 FPS。如果我将间隔设置为1 ms,我可以达到60.那么其他20 ms的位置是什么?那只是由于计时器的准确性差或者是什么?如果我想让我的程序尽可能快地运行,有没有办法连续调用绘制函数?
答案 0 :(得分:7)
您可以尝试实施游戏循环。
http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx
基本循环(稍微修改了原始版本和新SDK中的版本以便于阅读):
public void MainLoop()
{
// Hook the application’s idle event
System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);
System.Windows.Forms.Application.Run(myForm);
}
private void OnApplicationIdle(object sender, EventArgs e)
{
while (AppStillIdle)
{
// Render a frame during idle time (no messages are waiting)
UpdateEnvironment();
Render3DEnvironment();
}
}
private bool AppStillIdle
{
get
{
NativeMethods.Message msg;
return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
}
//And the declarations for those two native methods members:
[StructLayout(LayoutKind.Sequential)]
public struct Message
{
public IntPtr hWnd;
public WindowMessage msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
[System.Security.SuppressUnmanagedCodeSecurity] // We won’t use this maliciously
[DllImport(“User32.dll”, CharSet=CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
简单,优雅,有效。没有额外的分配,没有额外的集合,它只是工作..当队列中没有消息时,Idle事件触发,然后处理程序继续循环,直到出现消息,在这种情况下它停止..一旦所有消息都是处理完毕后,再次触发idle事件,并重新开始该过程。
此链接描述了使用应用程序的空闲事件的链接。它可能是有用的。您可以简单地执行一点时间测试或睡眠以将其减慢到所需的fps。尝试使用System.Diagnostics.StopWatch类来获得最准确的计时器。
我希望这会有所帮助。
答案 1 :(得分:3)
根据您正在做的事情,看看WPF及其动画支持,在WinForms中编写自己的动画可能是更好的解决方案。
另外考虑使用DirectX,因为它现在有一个.NET包装器并且非常快,但是使用WPF或WinForms更难。
连续调用绘图功能:
但是,您的用户可能不喜欢您杀死他们的计算机!
Application.Idle也是另一个不错的选择,但我喜欢这样的事实,Windows会降低它在需要时发送WmPaint消息的速度。
答案 2 :(得分:2)
Windows窗体计时器最终的分辨率受限于计算机的标准系统时钟分辨率。这在计算机之间有所不同,但通常在1毫秒到20毫秒之间。
在WinForms中,每当您在代码中请求计时器间隔时,Windows将仅保证您的计时器将被调用不再经常,然后指定的毫秒数。它不能保证您的Timer将在您指定的毫秒时间内被调用。
这是因为Windows是一个分时操作系统,而不是实时操作系统。其他进程和线程将被安排为并发运行,因此您的线程将被强制等待处理器的使用。因此,您不应编写依赖于精确间隔或毫秒延迟的WinForms计时器代码。
在您的情况下,将间隔设置为1毫秒实际上只是告诉Windows尽可能频繁地触发此计时器。正如您所看到的,这绝对不是1毫秒(60fps =约17毫秒)。
如果您需要更准确,分辨率更高的计时器,Windows API会提供高分辨率/高性能计时器,如果您的硬件支持,请参阅How to use the high resolution timer。这样做的一个缺点是,您的应用程序CPU使用率将会增加,以支持更高的性能。
总的来说,我认为你最好实现一个独立于硬件/系统的游戏循环。这样,无论实际的帧速率如何,场景的感知动画看起来都是恒定的。有关详细信息,请these articles提供良好的背景信息。
答案 3 :(得分:-1)
我在行中的给定示例代码中注意到了:
timer.Interval = 1000 / 60;
它使用整数除法运算,因此结果将四舍五入为整数,不会精确为16.6666ms,而是17ms。因此,计时器比屏幕刷新更大。