Direct2D随笔2——在C#的WinForm环境下创建Gameloop的Timer精度无法达到60fps

http://blog.csdn.net/hitachi_ht/article/details/38150955


经过大量前辈的实践证明,C#中Timer们的精度都无法达到60fps的需求。这一点可以做个实验,用2个Timer,一个1s,另一个0.01667s,然后后面timer的tick时给count+1,前面的每秒统计一下后面有几个+1,就会发现可能每秒只有大概30次左右。

关于比较好的计时系统google上面也有,就是利用这个性能计数器,计量的经度就能到很高了。

[csharp]  view plain  copy
  1. [System.Security.SuppressUnmanagedCodeSecurity]  
  2. [DllImport("kernel32")]  
  3. private static extern bool QueryPerformanceFrequency (ref long PerformanceFrequency);  
  4. [System.Security.SuppressUnmanagedCodeSecurity]  
  5. [DllImport("kernel32")]  
  6. private static extern bool QueryPerformanceCounter (ref long PerformanceCounter);  

为了把这个融入到Gameloop里面,我写了个类用来计时

[csharp]  view plain  copy
  1. public class PreciseTimer {  
  2.       [System.Security.SuppressUnmanagedCodeSecurity]  
  3.       [DllImport("kernel32")]  
  4.       private static extern bool QueryPerformanceFrequency (ref long PerformanceFrequency);  
  5.       [System.Security.SuppressUnmanagedCodeSecurity]  
  6.       [DllImport("kernel32")]  
  7.       private static extern bool QueryPerformanceCounter (ref long PerformanceCounter);  
  8.       long _tickPerSecond = 0;  
  9.       long _previousElapsedTime = 0;  
  10.       public PreciseTimer () {  
  11.             QueryPerformanceFrequency(ref _tickPerSecond);  
  12.             GetElapsedTime();  
  13.       }  
  14.       public double GetElapsedTime () {  
  15.             long Time = 0;  
  16.             QueryPerformanceCounter(ref Time);  
  17.             double ElapsedTime = (double)(Time - _previousElapsedTime) / (double)_tickPerSecond;  
  18.             _previousElapsedTime = Time;  
  19.             return ElapsedTime;  
  20.       }  
  21. }  
这样就可以得到一个相对准确的秒表( ╯□╰ )了。
然后,我们需要windows的消息处理机制,这里我们使用了C里面的Message。

[csharp]  view plain  copy
  1. [StructLayout(LayoutKind.Sequential)]  
  2. public struct Message {  
  3.       public IntPtr hWnd;  
  4.       public Int32 Msg;  
  5.       public IntPtr wParam;  
  6.       public IntPtr lParam;  
  7.       public uint time;  
  8.       public System.Drawing.Point P;  
  9. }  
并且我们需要C里面的PeekMessage函数。
[csharp]  view plain  copy
  1. [System.Security.SuppressUnmanagedCodeSecurity]  
  2. [DllImport("User32.dll", CharSet = CharSet.Auto)]  
  3. public static extern bool PeekMessage (  
  4.       out Message Msg,  
  5.       IntPtr hWnd,  
  6.       uint messageFilterMin,  
  7.       uint messageFilterMax,  
  8.       uint flags);  
然后利用这些东西,我们就拼凑出了一个类,起名叫做TimeController。

[csharp]  view plain  copy
  1. PreciseTimer _preciseTimer = new PreciseTimer();  
  2. public delegate void LoopCallBack (double ElapsedTime);  
  3. LoopCallBack _loopCallBack;  
  4. public TimeController (LoopCallBack CallBack) {  
  5.             _loopCallBack = CallBack;  
  6.             Application.Idle += new EventHandler(OnApplicationEnterIdle);  
  7. }  
  8. private void OnApplicationEnterIdle (object sender, EventArgs e) {  
  9.             while (IsAppStillIdle())  
  10.                   _loopCallBack(_preciseTimer.GetElapsedTime());  
  11. }  
  12. private bool IsAppStillIdle () {  
  13.             Message Msg;  
  14.             return !PeekMessage(out Msg, IntPtr.Zero, 0, 0, 0);  
  15. }  
然后我们只需要在Form1.cs里面添加一个Gameloop函数

[csharp]  view plain  copy
  1. void GameLoop (double ElapsedTime) {  
  2. }  
最后定义出来timeController。
[csharp]  view plain  copy
  1. TimeController timeController;  
并且将Gameloop放进去

[csharp]  view plain  copy
  1. timeController = new TimeController(GameLoop);  

就完成了。

这些东西在网上比较难找,所以特意记下来。

你可能感兴趣的:(C#winform)