C# 如何在WPF中准确地使用调度定时器来控制帧速率?

C# 如何在WPF中准确地使用调度定时器来控制帧速率?,c#,wpf,timer,C#,Wpf,Timer,我在尝试使用System.Windows.Threading.Dispatchermer实例控制WPF中的帧速率时遇到了一个问题 为了尝试Dispatchermer的有效性,我创建了一个简单的WPF演示,其中只有一个窗口,其中有一个文本框和一个按钮。单击该按钮时,Dispatcher实例将根据文本框中的双倍数字开始滴答声,因为间隔和秒表同时启动,每个Dispatcher滴答声中的计数器变量将增加1。当StopWatch.elaspedmissions>1000(超过1秒)时,计时器停止滴答作响,

我在尝试使用System.Windows.Threading.Dispatchermer实例控制WPF中的帧速率时遇到了一个问题

为了尝试Dispatchermer的有效性,我创建了一个简单的WPF演示,其中只有一个窗口,其中有一个文本框和一个按钮。单击该按钮时,Dispatcher实例将根据文本框中的双倍数字开始滴答声,因为间隔和秒表同时启动,每个Dispatcher滴答声中的计数器变量将增加1。当StopWatch.elaspedmissions>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新手,无法上传图片):

由于WPF(和大多数其他渲染引擎)渲染每一帧所需的时间并不相等,而且由于不同的计算可能会进一步延迟帧之间的间隔,因此使用
CompositionTarget是正确的定时更新您的游戏在定期,严格控制的时间间隔

在优秀的《游戏编程模式》(在线免费提供)一书中,您可以找到非常有用的

在其完整形式中,您可以将其放入
CompositionTarget.Rendering
事件(伪代码)的处理程序中:

双电流=getCurrentTime(); 双运行=当前-上一个; 先前=当前; 滞后时间+=经过的时间; processInput(); 而(滞后>=MS\u/次更新) { 更新(); 滞后-=每次更新的毫秒数; } render();
在C#中,您可以通过调用
DateTime.Now
或使用
Stopwatch.appeased
来实现
getCurrentTime()

例如,调度程序无法控制WPF应用程序的帧速率。From:计时器不保证在时间间隔发生时准确执行,但保证在时间间隔发生之前不执行。请解释你到底想达到什么目的。谢谢你的评论,克莱门斯。我想要实现的是在WPF画布动画中使用Dispatchermer来控制帧速率。我感到奇怪的是,实际帧速率是20左右,而不是30,但它应该是30。我理解它不能保证准确执行。但实际帧速率似乎仍然与它应该是什么有关。我想知道如果计时器不是控制画布动画的好方法,那么更好的方法是什么?提前感谢。您可能想看一看每帧动画:绕过MSDN文章中的动画和计时系统部分。如果不可靠地再现问题,就不可能确定在您的情况下什么是最好的。但评论家克莱门斯提供了有用的建议;20 fps大约是任何基于计时器/线程的调度所能达到的速度,因为线程调度的粒度为50ms。为什么需要特定的帧速率?为什么不设置一个WPF动画,让框架尽可能地展示动画呢?@PeterDuniho感谢您的评论。实际上,我写的是一个小型游戏,比如俄罗斯方块,使用WPF画布。我试图找到一种方法来获得稳定的帧速率,比如30fps。我认为克莱门斯关于每帧间隔渲染的评论对我的要求很有帮助。但我还有一个问题:您对20fps的解释是,线程调度发生在50ms的粒度上,这意味着它的频率不能超过20fps吗?我试过上面粘贴的演示,我发现如果输入3,我可以得到更大的值。我仍然想知道原因。 double current = getCurrentTime(); double elapsed = current - previous; previous = current; lag += elapsed; processInput(); while (lag >= MS_PER_UPDATE) { update(); lag -= MS_PER_UPDATE; } render();