经过大量前辈的实践证明,C#中Timer们的精度都无法达到60fps的需求。这一点可以做个实验,用2个Timer,一个1s,另一个0.01667s,然后后面timer的tick时给count+1,前面的每秒统计一下后面有几个+1,就会发现可能每秒只有大概30次左右。
关于比较好的计时系统google上面也有,就是利用这个性能计数器,计量的经度就能到很高了。
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("kernel32")]
private static extern bool QueryPerformanceFrequency (ref long PerformanceFrequency);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("kernel32")]
private static extern bool QueryPerformanceCounter (ref long PerformanceCounter);
public class PreciseTimer {
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("kernel32")]
private static extern bool QueryPerformanceFrequency (ref long PerformanceFrequency);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("kernel32")]
private static extern bool QueryPerformanceCounter (ref long PerformanceCounter);
long _tickPerSecond = 0;
long _previousElapsedTime = 0;
public PreciseTimer () {
QueryPerformanceFrequency(ref _tickPerSecond);
GetElapsedTime();
}
public double GetElapsedTime () {
long Time = 0;
QueryPerformanceCounter(ref Time);
double ElapsedTime = (double)(Time - _previousElapsedTime) / (double)_tickPerSecond;
_previousElapsedTime = Time;
return ElapsedTime;
}
}
这样就可以得到一个相对准确的秒表( ╯□╰ )了。
[StructLayout(LayoutKind.Sequential)]
public struct Message {
public IntPtr hWnd;
public Int32 Msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point P;
}
并且我们需要C里面的PeekMessage函数。
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool PeekMessage (
out Message Msg,
IntPtr hWnd,
uint messageFilterMin,
uint messageFilterMax,
uint flags);
然后利用这些东西,我们就拼凑出了一个类,起名叫做TimeController。
PreciseTimer _preciseTimer = new PreciseTimer();
public delegate void LoopCallBack (double ElapsedTime);
LoopCallBack _loopCallBack;
public TimeController (LoopCallBack CallBack) {
_loopCallBack = CallBack;
Application.Idle += new EventHandler(OnApplicationEnterIdle);
}
private void OnApplicationEnterIdle (object sender, EventArgs e) {
while (IsAppStillIdle())
_loopCallBack(_preciseTimer.GetElapsedTime());
}
private bool IsAppStillIdle () {
Message Msg;
return !PeekMessage(out Msg, IntPtr.Zero, 0, 0, 0);
}
然后我们只需要在Form1.cs里面添加一个Gameloop函数
void GameLoop (double ElapsedTime) {
}
最后定义出来timeController。
TimeController timeController;
并且将Gameloop放进去
timeController = new TimeController(GameLoop);
就完成了。
这些东西在网上比较难找,所以特意记下来。