如何通过准确使用调度程序计时器来控制 WPF 中的帧速率?
How to control frame rate in WPF by using dispatcher timer accurately?
我在尝试使用 System.Windows.Threading.DispatcherTimer 实例控制 WPF 中的帧速率时遇到问题。
为了尝试 DispatcherTimer 的有效性,我创建了一个简单的 WPF 演示,其中包含一个 window,其中有一个文本框和一个按钮。单击按钮时,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();
};
}
}
}
运行结果如下(Whosebug菜鸟,还不能上传图片):
由于 WPF(和大多数其他渲染引擎)不会花费相同的时间来渲染每一帧,并且由于不同的计算会进一步延迟帧之间的间隔,因此使用 CompositionTarget.Rendering
是正确的 "clock" 渲染间隔,但您仍然可以使用 "wall clock" 计时器以定期、严格控制的间隔 更新 您的游戏。
在优秀的书籍 "Game Programming Patterns"(可在线免费获取)中,您可以找到非常有用的 Game Loop Pattern。
在其完整形式中,这是您要放入 CompositionTarget.Rendering
事件处理程序中的内容(伪代码):
double current = getCurrentTime();
double elapsed = current - previous;
previous = current;
lag += elapsed;
processInput();
while (lag >= MS_PER_UPDATE)
{
update();
lag -= MS_PER_UPDATE;
}
render();
例如,在 C# 中,您可以通过调用 DateTime.Now
或使用 Stopwatch.Elapsed
来实现 getCurrentTime()
。
我在尝试使用 System.Windows.Threading.DispatcherTimer 实例控制 WPF 中的帧速率时遇到问题。
为了尝试 DispatcherTimer 的有效性,我创建了一个简单的 WPF 演示,其中包含一个 window,其中有一个文本框和一个按钮。单击按钮时,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();
};
}
}
}
运行结果如下(Whosebug菜鸟,还不能上传图片):
由于 WPF(和大多数其他渲染引擎)不会花费相同的时间来渲染每一帧,并且由于不同的计算会进一步延迟帧之间的间隔,因此使用 CompositionTarget.Rendering
是正确的 "clock" 渲染间隔,但您仍然可以使用 "wall clock" 计时器以定期、严格控制的间隔 更新 您的游戏。
在优秀的书籍 "Game Programming Patterns"(可在线免费获取)中,您可以找到非常有用的 Game Loop Pattern。
在其完整形式中,这是您要放入 CompositionTarget.Rendering
事件处理程序中的内容(伪代码):
double current = getCurrentTime(); double elapsed = current - previous; previous = current; lag += elapsed; processInput(); while (lag >= MS_PER_UPDATE) { update(); lag -= MS_PER_UPDATE; } render();
例如,在 C# 中,您可以通过调用 DateTime.Now
或使用 Stopwatch.Elapsed
来实现 getCurrentTime()
。