如何为我的游戏获得更好的计时事件?

问题描述:

我写了一个普通的调度程序定时器方法来创建WPF中的gameloop。我注意到,如果它设置为比200毫秒更短的时间间隔,它不会正确追上。如何为我的游戏获得更好的计时事件?

我在屏幕上打印了秒(见下面的代码),并将其与我的手表进行了比较。设置为500毫秒,没关系,但如果我将其设置得低得多,则会有很大差异。我尝试了10毫秒的设置,这意味着屏幕上的时间在(实际)时间内只传递了38秒! (请注意,我的所有游戏引擎代码在测试期间都被移除了,所以它只是被调用的计时器循环。此外,如果我从VS或Debug文件夹中的exe文件运行代码,则无关紧要。)

请注意,我创建的游戏运行流畅,只是我的(标准)Each_Tick方法不会在正确的时间被调用。这反过来意味着我的游戏在更快的计算机上运行得更快。

那么如何保持正确的时间跟踪并确保Each_Tick方法在同一时间独立于所使用的计算机(或手机)启动?也就是说,我宁愿对游戏对象的数量,碰撞检测精度等有限制,但要及时,而不是尽可能快。换句话说,如果我将计时器递增值设置为50ms(我认为这是合理的,因为那意味着每秒20次),我真的希望游戏每秒更新20次。

我真的不想进入线程等,如果有可能避免它,因为现在游戏自己运行良好。我只是希望他们以相同的速度播放。

我在How to control frame rate in WPF by using dispatcher timer accurately?上查找了一些很好的回复,但是这仍然不能回答我的问题:无论现代电脑/手机如何让我的游戏以相同的速度运行,以及它是WPF/UWP或者别的什么?

或者这是不可能以一种相当简单的方式来完成的,我应该接受那个游戏速度取决于所用的计算机?

谢谢!

代码:

public void StartTimer() 
    { 
     //This variable is used to get to the controls (labels etc) of the MainWindow (WPF) 
     MainWindow mainWin = System.Windows.Application.Current.Windows.Cast<System.Windows.Window>().FirstOrDefault(window => window is MainWindow) as MainWindow; 

     time = TimeSpan.FromSeconds(0); 

     //What the code below does is this: 
     //For each 10 ms, call the different methods. Then add 10 ms to the current time. 

     timer = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 10), DispatcherPriority.Normal, delegate 
     { 
      if (runGame == false) return; //only go on if not in pause mode 
      mainWin.txtInfo.Text = time.ToString("mm\\:ss");//Shows the timer in a textbox, only showing minutes and seconds. 


      //Collision code etc removed during the test 

      time = time.Add(TimeSpan.FromMilliseconds(10)); //adds a specified time to the current time 
     }, System.Windows.Application.Current.Dispatcher); 
    } 

注意,上面的代码被用于测试目的添加。原始代码(面临相同的问题)看起来像这样:

public void StartTimer() 
    { 
     //A note on the dispatcherTimer http://www.wpf-tutorial.com/misc/dispatchertimer/ 
     var timer = new DispatcherTimer(); 
     timer.Interval = new TimeSpan(0, 0, 0, 0, 10); // Each every n milliseconds (set low to avoid flicker) 
     timer.Tick += EachTick; 
     timer.Start(); 
    } 

    // A testing counter 
    int counter = 0; 

    // Raised every tick while the DispatcherTimer is active. 
    private void EachTick(object sender, object e) 
    { etc 
+0

您假定无限定时器精度,它不存在。除了添加TimeSpans并因此累积不准确之外,最好使用'DateTime.Now'来获取当前的确切时间。这样你可以使你的游戏独立于定时器的不准确性。 – Clemens

+0

谢谢Clemens,那正是我所希望的那种智慧。 :) – user2040543

+0

一个问题:DateTime.Now总是正确到毫秒?如果是这样的话,我可以尽可能快地运行游戏(比如每10分钟向Every_Tick发送一次all),然后将每个游戏对象的移动距离与自上次调用Each_Tick(或如此)以来所经过的时间相除。然后,一台快速电脑将获得更流畅的体验,但所有电脑都将获得相同的速度。 – user2040543

建立定时器不是很准确。使用这段代码片段。我很多次使用过这个计时器。这个计时器是非常准确的。荣誉登录到John

public class AccurateTimer 
    { 
     private delegate void TimerEventDel(int id, int msg, IntPtr user, int dw1, int dw2); 
     private const int TIME_PERIODIC = 1; 
     private const int EVENT_TYPE = TIME_PERIODIC; 
     [DllImport("winmm.dll")] 
     private static extern int timeBeginPeriod(int msec); 
     [DllImport("winmm.dll")] 
     private static extern int timeEndPeriod(int msec); 
     [DllImport("winmm.dll")] 
     private static extern int timeSetEvent(int delay, int resolution, TimerEventDel handler, IntPtr user, int eventType); 

     private readonly int _mTimerId; 

     public AccurateTimer(int delay) 
     { 
      timeBeginPeriod(1); 
      _mTimerId = timeSetEvent(delay, 0, TimerTick, IntPtr.Zero, EVENT_TYPE); 
     } 

     public void Stop() 
     { 
      timeEndPeriod(1); 
      System.Threading.Thread.Sleep(100);// Ensure callbacks are drained 
     } 

     private void TimerTick(int id, int msg, IntPtr user, int dw1, int dw2) 
     { 
      Console.WriteLine("Tick " + DateTime.Now.TimeOfDay.TotalMilliseconds); 
     } 
    } 
+0

非常感谢这段代码。我会尝试将它融入我的游戏中。我真的很感谢这个代码放在上下文中(如果你碰巧在游戏中使用它)。 – user2040543

+0

您可以使用Action委托检查John的解决方案。这个代表将在计时器滴答时触发。所以你可以把这个定时器包含到某个类中并链接委托。 [link to Johns post](http://*.com/questions/9228313/most-accurate-timer-in-net/31612770#31612770) – PolkaHard