如何准确地使用调度程序计时器来控制WPF中的帧速率?

当我尝试使用System.Windows.Threading.DispatcherTimer实例控制WPF中的帧速率时遇到问题。

为了尝试DispatcherTimer的有效性,我创建了一个简单的WPF Demo,其中包含一个带有文本框和按钮的窗口。 单击该按钮时,DispatcherTimer实例开始根据文本框中的双精度值作为间隔并且StopWatch同时启动,计数器变量在每个DispatcherTimer刻度上增加1。 当StopWatch.ElaspedMilliSeconds> 1000(超过1秒)时,计时器停止计时并且秒表也会重置,弹出一个消息框以显示计数器的值。

因此,如果输入33.3(1000/30),计数器值应该在30左右。 但结果是20左右。我请求帮助是否有人可以帮助检查下面的源代码中的问题。 提前致谢。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace TimerDemo { public partial class MainWindow : Window { private System.Windows.Threading.DispatcherTimer _timer = new System.Windows.Threading.DispatcherTimer(); public MainWindow() { InitializeComponent(); double ticks = 0L; System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); int frameCount = 0; //There is a textbox "txtTicks" which accepts a millisecond value //And a button "btn", by clicking the button the dispatchertimer & //stopwatcher are started. _timer.Tick += (sender, e) => { frameCount++; System.Diagnostics.Debug.WriteLine(watch.ElapsedMilliseconds); if (watch.ElapsedMilliseconds > 1000) { _timer.Stop(); watch.Reset(); MessageBox.Show(string.Format("Already 1 second! FrameCount: {0}", frameCount)); frameCount = 0; } }; this.btn.Click += (sender, e) => { double.TryParse(this.txtTicks.Text, out ticks); if (ticks != 0.0) { _timer.Interval = TimeSpan.FromMilliseconds(ticks); } _timer.Start(); watch.Start(); }; } } } 

运行结果如下(Stackoverflow的新手,尚无法上传图片):

http://sofzh.miximages.com/c%23/Bkp1uam.png

由于WPF(以及大多数其他渲染引擎)不会花费相等的时间来渲染每一帧,并且由于不同的计算可以进一步延迟帧之间的间隔,因此您正好使用CompositionTarget.Rendering来“计时”渲染间隔,但是您仍然可以使用“挂钟”计时器以定期,严格控制的间隔更新您的游戏。

在优秀的书“游戏编程模式”(免费在线提供)中,您可以找到非常有用的游戏循环模式 。

在完整forms中,这是您将置于CompositionTarget.Rendering事件的处理程序中(在伪代码中):

   double current = getCurrentTime();
   double elapsed = current  -  previous;
   previous = current;
  滞后+ =已过去;

   processInput();

   while(lag> = MS_PER_UPDATE)
   {
    更新();
     lag  -  = MS_PER_UPDATE;
   }

  渲染();

在C#中,您可以通过调用DateTime.Now或使用Stopwatch.Elapsed来实现getCurrentTime()