Thread.Sleep(1);对于Silverlight动画来说太慢了,有没有替代方案?

3
我有一个算法在Canvas上进行动画绘制,但我无法控制动画的速度,它绘制得非常快。因此,我添加了Thread.Sleep(1),但当绘制数千个像素时,这就太慢了。我尝试了一个Storyboard方法,但那也很慢。那么,有没有替代Thread.sleep来减缓循环的速度的方法呢?
void DrawGasket(object sender, DoWorkEventArgs e)
    {
        Random rnd = new Random();
        Color color = Colors.White;
        while (Counter <= noOfPx)
        {
            switch (rnd.Next(3))
            {
                case 0:
                    m_lastPoint.X = (m_top.X + m_lastPoint.X)/2;
                    m_lastPoint.Y = (m_top.Y + m_lastPoint.Y)/2;
                    color = Colors.White;
                    break;
                case 1:
                    m_lastPoint.X = (m_left.X + m_lastPoint.X)/2;
                    m_lastPoint.Y = (m_left.Y + m_lastPoint.Y)/2;
                    color = Colors.Orange;
                    break;
                case 2:
                    m_lastPoint.X = (m_right.X + m_lastPoint.X)/2;
                    m_lastPoint.Y = (m_right.Y + m_lastPoint.Y)/2;
                    color = Colors.Purple;
                    break;
            }
            Dispatcher.BeginInvoke(() =>
            {
                var px = new Rectangle { 
                                       Height = m_pxSize, 
                                       Width = m_pxSize,
                                       Fill = new SolidColorBrush(color) 
                                       };
                Canvas.SetTop(px, m_lastPoint.Y);
                Canvas.SetLeft(px, m_lastPoint.X);
                can.Children.Add(px);
                lblCounter.Text = Counter.ToString();
            });
            Counter++;
            Thread.Sleep(1);
        }
    }
5个回答

13

你能不能通过每N次循环休眠一次,而不是每次都要休眠呢?


4
这是一个简单的方法:你希望它在特定时间绘制...在每个循环期间,它会查看现在的时间。如果是“时间”(无论什么),则以你确定的速率进行绘制/更新。否则,只需循环即可。(例如,如果您想要每秒钟更新10次,并且您刚刚更新了,请将当前时间存储在变量中...然后与该变量进行比较。在过去的1/10秒内,不要重新绘制,只需循环即可。)
只要您可以在此循环内完成其他任何需要做的事情,这并不糟糕。但是,如果必须在发生任何其他事件之前完成此循环,那么您将浪费很多CPU周期。
如果您想变得高级,并且做得正确,您可以让它查看经过了多少时间,然后按正确的数量更新视觉效果。因此,例如,如果您希望您的对象每秒移动30像素,您可以精确地更新其位置(例如,如果经过了0.0047秒,则应移动0.141像素)。您存储该值。当然,在视觉上,在另一轮循环之前,它不会移动,但是它移动的时间和距离将由时间而不是计算机速度确定。这样,您将获得每秒移动30像素的效果,无论机器速度如何...在慢速机器上它将只是更跳跃。

我有点喜欢时间的想法,尽管它可能有点复杂,但它确实允许循环在不同的计算机上以大约相同的速度运行;在快速计算机上,它会更频繁地休眠而不是在慢速计算机上。 - Fredrik Mörk
+1 表示建议采用基本的计时循环。还有其他选项(例如在内存中绘制并仅在需要显示新图像时翻转图像),但这直接回答了问题。 - Greg D
我会尝试稍后实现这个,因为这是一种更好的动画实现方式。但现在我会采用mattnewport的解决方案。 - Qwark

4

CompositionTarget.Rendering事件在Silverlight绘制每一帧时触发,这是编写任何自定义绘图代码的好地方。您可以使用其他机制(例如计时器)来跟踪您的“游戏时间”,并在与绘图代码不同的线程(或至少不同的执行路径)上适当地调整模型。这样,当绘制UI时,就没有需要执行的计算,只需绘制模型的当前状态即可。基本上,只需将Dispatcher.BeginInvoke中的代码移动到CompositionTarget.Rendering的事件处理程序中,我相信您会做得很好。


3

一种选择是不要每次都休眠:

if(Counter % 2 == 0) Thread.Sleep(1);


(注:该代码片段在IT技术中常用于控制线程执行速度)

3

使用或不使用Thread.Sleep(),渲染速度取决于系统的速度或进程获取的时间。-> 不好!

您应该采用现代视频游戏编写的方法。跟踪当前时间,并且每秒只调用x次BeginInvoke()(其中x是每秒帧数)。

在BeginInvoked函数中检查绘制了多少和还需要绘制多少。尤其不要为每个矩形都调用BeginInvoke()。即使没有Thread.Sleep(),这也总是一种上下文切换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接