我目前在GDI和计时器方面遇到了一个非常奇怪的问题。
首先是守则:
class Graph : UserControl {
private System.Threading.Timer timer;
private int refreshRate = 25; //Hz (redrawings per second)
private float offsetX = 0; //X offset for moving graph
public Graph() {
timer = new System.Threading.Timer(timerTick);
}
private void timerTick(object data) {
offsetX -= 1 / refreshRate;
this.Invalidate();
}
public void timerStart() {
timer.Change(0, 1000 / refreshRate);
}
private void onPaint(object sender, PaintEventArgs e) {
//350 lines of code for drawing the graph
//Here the offsetX will be used to move the graph
}
}
我正在尝试将特定时间内的绘制图形移动到左侧的1“图形单位”。所以我使用一个计时器,它会以很小的步长改变偏移,所以它将是一个平滑的移动(这就是refreshRate)。
在第一个视图中,此代码有效,但后来我发现了以下问题: 如果我使用的刷新率为1(1Hz),则可以将我的图形在1步(图形单位)中向左移动。如果我增加refreshRate,我的动作会慢下来。在20 FPS时,它的速度非常慢,速度为200 FPS,速度非常慢......
所以这就是我的尝试:
我使用了Refresh或Update而不是Invalidate
我使用普通线程(带睡眠)而不是计时器
两个代码更改都没有改变结果..
除了使用计时器的移动之外,我还可以使用鼠标移动图形,如果计时器正在运行,我仍然可以使用鼠标平滑地移动图形。所以它不是一个性能问题..
我想到了绘画队列中的一个问题,因为我比绘画更快地刷新了吗? (但为什么我可以用鼠标顺利移动图形?!)
所以我需要一点帮助。 感谢
答案 0 :(得分:1)
速度为20 FPS时速度很慢,速度为200 FPS,速度非常慢......
这里有一个根本问题;要获得200fps的刷新率,您需要每5ms重绘一次。这绝不会发生。无论您将计时器的间隔设置为何,其分辨率都限制在10-15ms左右。所以你最好的帧速率大约是66-100fps,这假设你的绘图代码没有时间,当然它没有。
最重要的是,你正在使用System.Threading.Timer
在UI线程上执行它的回调,所以从那里调用Invalidate()可能甚至都不安全。您通常不会将System.Threading.Timer
用于UI代码。
您可能想尝试使用所谓的Multi-Media Timer shown here,它使用CPU的高性能计时器,并提供大约1毫秒的分辨率。
答案 1 :(得分:0)
您可以尝试稍微更改问题。
既然你不知道你的画会花多长时间,你就不能对此作出假设。
你知道的是你希望过渡发生的时间,比方说30秒,就像这样:
private MAX_TRANSITION_TIME = 30; //seconds
此时,您还可以跟踪自操作开始以来经过的时间,记录您的操作何时开始,比如说
private DateTime _startMoment;
现在您可以做的是确保您拥有正确的例行程序,并根据startMoment和现在
之间的差异来计算您的位置var elapsedTime = DateTime.Now.Subtract(_startMoment).Milliseconds;
var elapsedPercent = elapsedTime / MAX_TRANSITION_TIME * 1000.0 ;
从现在开始,您可以做的是根据您经过的时间百分比进行绘画。
因此,在完成OnPaint后,您应该可以刷新它。如果您使用一个UI计时器,则可以执行以下操作:
private void onPaint(object sender, PaintEventArgs e) {
Timer1.Enabled = false;
//350 lines of (adjusted)code go here
If (ElapsedPercent<1)
{
Timer1.Enabled=True;
}
else
{
// you may need to perform the last draw setting the ElapsedPercent
// to 1. This is because it is very unlikely that your
// last call will happen at the very last millisecond
}
}
其中Timer1必须是Timer控件。
在Interval事件中,您只需编写
_startMoment = DateTime.Now();
this.Invalidate();
希望这有帮助