按住键时计时器的延迟

时间:2019-02-28 11:47:32

标签: c# winforms timer

我在程序中使用计时器时遇到问题。按住控制Sprite的键时会发生问题,因为这样做会使计时器暂停,直到用户释放该键为止。

一个对我有用的解决方案是调用“ Application.DoEvents();”。在执行keydown方法之后,该程序会利用100%的CPU使用率,导致在按下某个键时游戏会滞后。

还有另一种方法可以防止用户按住键时计时器暂停吗?任何帮助将不胜感激。谢谢。

private void timer1_Tick(object sender, EventArgs e)
{
    TimeElasped += 0.1F;
    TimeLabel.Text = String.Format("{0:0.0} Secs" , TimeElasped);
}

private void PlayMenu_KeyDown(object sender, KeyEventArgs e)
{
    //Application.DoEvents();
    if (e.KeyCode == Keys.Left)
    {
        Position.X -= 10;      
    }

    if (e.KeyCode == Keys.Right)
    {
        Position.X += 10;
    }

    if (e.KeyCode == Keys.Up)
    {
        Position.Y -= 10;
    }

    if (e.KeyCode == Keys.Down)
    {
        Position.Y += 10;
    }
}

1 个答案:

答案 0 :(得分:1)

您不需要计时器即可完成您要尝试的操作。您可以尝试在另一个线程上执行此操作。

在下面的代码中,我创建了一个新线程,在该线程上运行一个无限循环(不会占用CPU,只会占用我们创建的线程)。我将新线程休眠了0.1秒,生成了新的标签文本。

除了UI线程(之前您正在做所有工作的地方)之外,您无法更新任何UI元素。因此,我们必须使用BeginInvoke,它可以在UI线程准备就绪时更新标签。

如果您想了解更多信息,请参见以下有关为什么我使用BeginInvoke的更多信息: https://harriyott.com/2006/05/using-begininvoke-to-update-gui-safely

我更新了String.Format以使用较新的$字符串插值。它更具可读性。无论如何,编译器都会在后台用String.Format替换它,但是使用起来会容易一些。

有关更多信息,请参见: https://weblog.west-wind.com/posts/2016/Dec/27/Back-to-Basics-String-Interpolation-in-C

最后,我们需要调用您的新线程,因此我需要在表单加载时执行。

private void PlayMenu_Load(object sender, EventArgs e)
{
    // call our new method that starts a new thread when the form loads.
    StartANewThread();
}

void StartANewThread()
{
    var task = Task.Run(() =>
    {
        /* if you're not used to threading an infinite while loop would look bad but
           it's actually okay because you're going to pause it for 100 milliseconds and 
           it's not going to hold up the rest of the CPU processes.
         */

        while (true)
        {
            // sleep this thread for 0.1 seconds. 
            Thread.Sleep(TimeSpan.FromMilliseconds(100));

            TimeElasped += 0.1F;

            // I would usually use begin invoke to switch back to the UI Thread and update the label
            // as you can't update UI Elements from a Non-UI thread.
            label1.BeginInvoke(new MethodInvoker(UpdateLabelOnUiThread));
        }
    });
}

private void UpdateLabelOnUiThread()
{
    // We're back on the UI thread here because of the BeginInvoke. 
    // We can now update the label.
    label1.Text = $"{TimeElasped:0.0} Secs";
}

一些奖励:

您在KeyDown处理程序中的if语句有点混乱,因此我将它们替换为switch语句。我不确定您是否知道这一点,但是使用起来更具可读性和易用性:

private void PlayMenu_KeyDown(object sender, KeyEventArgs e)
{
    // I also refactored your if statements into a switch. It's a lot cleaner.
    switch (e.KeyCode)
    {
        case Keys.Left:
            Position.X -= 10;
            break;
        case Keys.Right:
            Position.X += 10;
            break;
        case Keys.Up:
            Position.Y -= 10;
            break;
        case Keys.Down:
            Position.Y += 10;
            break;
    }

}

有关switch语句的更多信息,请参见此处: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/switch

如果您需要更多信息,请在下面的评论部分中与我联系,希望对您有所帮助。否则,请不要忘记将其标记为您接受的答案。