在我的游戏中使用什么代替Application.DoEvents?

问题描述 投票:4回答:3

我正在用C#WinForms创建一个Space Invaders游戏,当编码玩家大炮的移动时,我创建了这个事件处理程序:

private void Game_Screen_KeyDown(object sender, KeyEventArgs e)
{
    for (int i = 0; i < 500; i++)
    {

        if (e.KeyCode == Keys.Left)
        {
            cannonBox.Location = new Point(cannonBox.Left - 2, cannonBox.Top); //Changes location of cannonBox to a new location to the left
            Application.DoEvents();
            System.Threading.Thread.Sleep(10); //Delays the movement by couple milliseconds to stop instant movement
        }

        if (e.KeyCode == Keys.Right)
        {
            cannonBox.Location = new Point(cannonBox.Left + 2, cannonBox.Top); //Changes location of cannonBox to a new location to the right
            Application.DoEvents();
            System.Threading.Thread.Sleep(10); //Delays the movement by couple milliseconds to stop instant movement
        }

        if (e.KeyCode == Keys.Up)
        {
            createLaser(); //Calls the method whenever Up arrow key is pressed
        }
    }
}

但是关于C#中不可靠的不同网站,我将确保不要在其上使用它。还有什么其他替代方法可以代替此实例中的Application.DoEvents使用?

c# winforms
3个回答
5
投票

我建议使用事件处理程序async并使用await Task.Delay()而不是Thread.Sleep()

private async void Game_Screen_KeyDown(object sender, KeyEventArgs e)
{
    for (int i = 0; i < 500; i++)
    {
        if (e.KeyCode == Keys.Left)
        {
            cannonBox.Location = new Point(cannonBox.Left - 2, cannonBox.Top); //Changes location of cannonBox to a new location to the left
            await Task.Delay(10); //Delays the movement by couple milliseconds to stop instant movement
        }

        if (e.KeyCode == Keys.Right)
        {
            cannonBox.Location = new Point(cannonBox.Left + 2, cannonBox.Top); //Changes location of cannonBox to a new location to the right
            await Task.Delay(10); //Delays the movement by couple milliseconds to stop instant movement
        }

        if (e.KeyCode == Keys.Up)
        {
            createLaser(); //Calls the method whenever Up arrow key is pressed
        }
    }
}

这样,控制流将返回给调用者,您的UI线程有时间处理其他事件(因此不需要Application.DoEvents())。然后在(大约)10ms之后,返回控件并恢复该处理程序的执行。

可能需要进行更多微调,因为当然,当方法尚未完成时,您可以设法击中更多键。如何处理这取决于周围环境。您可以声明一个标志,指示当前执行并拒绝进一步的方法条目(此处不需要线程安全,因为它在UI线程上按顺序发生)。 或者不是拒绝重新进入队列,而是在另一个事件中按键并处理它们,例如“闲置”事件(如评论中提出的Lasse)。


请注意,事件处理程序是少数情况下使用async而不返回Task的情况之一。


2
投票

使用一个定时器,每20毫秒调用游戏处理。

KeyDown / KeyUp事件中,只需更改游戏处理使用的当前状态。

示例代码:

[Flags]
public enum ActionState
{
    MoveLeft,
    MeveRight,
    FireLaser,
}

// stores the current state
private ActionState _actionState;

// set action state
private void Game_Screen_KeyDown(object sender, KeyEventArgs e)
{
    switch ( e.KeyCode )
    {
        case Keys.Left:
            _actionState |= ActionState.MoveLeft;
            break;
        case Keys.Right:
            _actionState |= ActionState.MoveRight;
            break;
        case Keys.Up:
            _actionState |= ActionState.FireLaser;
            break;
        default:
            break;
    }
}

// remove action state
private void Game_Screen_KeyUp(object sender, KeyEventArgs e)
{
    switch ( e.KeyCode )
    {
        case Keys.Left:
            _actionState &= ~ActionState.MoveLeft;
            break;
        case Keys.Right:
            _actionState &= ~ActionState.MoveRight;
            break;
        case Keys.Up:
            _actionState &= ~ActionState.FireLaser;
            break;
        default:
            break;
    }
}

// called from a timer every 20 milliseconds
private void Game_Screen_LoopTimer_Tick(object sender, EventArgs e)
{
    if ( _actionState.HasFlag( ActionState.MoveLeft ) && !_actionState.HasFlag( ActionState.MoveRight ) )
    {
        cannonBox.Location = new Point(cannonBox.Left - 2, cannonBox.Top); //Changes location of cannonBox to a new location to the left
    }
    if ( _actionState.HasFlag( ActionState.MoveRight ) && !_actionState.HasFlag( ActionState.MoveLeft ) )
    {
        cannonBox.Location = new Point(cannonBox.Left + 2, cannonBox.Top); //Changes location of cannonBox to a new location to the right
    }
    if ( _actionState.HasFlag( ActionState.FireLaser ) )
    {
        createLaser(); //Calls the method whenever Up arrow key is pressed
    }
}

0
投票

Application.DoEvents()中断您的方法的执行,UI线程将处理其事件(包括重绘UI)。根据我的经验,在正确的地方使用它并没有错...

使用“异步”模式(如RenéVogt建议的那样)是制作响应式UI的最佳实践。

然而。你必须问自己,如果关键是向左,向右还是向上,你需要一个检查500次的循环。特别是因为看起来这个循环是由一个按键事件引发的......

如果你在main中创建一个'while(true)'循环并从那里调用Application.DoEvents可能会更容易。

或者您对key_down事件做出反应并一次执行操作。 =>按左 - >向左移动 - >再按左 - >向左移动一个......依此类推。

https://msdn.microsoft.com/en-us//library/system.windows.forms.application.doevents(v=vs.110).aspx

© www.soinside.com 2019 - 2024. All rights reserved.