2017-05-05 59 views
0

我寫了一個普通的調度程序定時器方法來創建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

回答

0

建立定時器不是很準確。使用這段代碼片段。我很多次使用過這個計時器。這個計時器是非常準確的。榮譽登錄到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://stackoverflow.com/questions/9228313/most-accurate-timer-in-net/31612770#31612770) – PolkaHard