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

C# 在我的游戏中使用什么来代替Application.DoEvents?,c#,winforms,C#,Winforms,我正在用C#WinForms创建一个太空入侵者游戏,在对玩家大炮的移动进行编码时,我创建了以下事件处理程序: private void Game_Screen_KeyDown(object sender, KeyEventArgs e) { for (int i = 0; i < 500; i++) { if (e.KeyCode == Keys.Left) { cannonBox.Location = new Po

我正在用C#WinForms创建一个太空入侵者游戏,在对玩家大炮的移动进行编码时,我创建了以下事件处理程序:

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
        }
    }
}
private void Game\u Screen\u KeyDown(对象发送者,KeyEventArgs e)
{
对于(int i=0;i<500;i++)
{
if(e.KeyCode==Keys.Left)
{
cannonBox.Location=新点(cannonBox.Left-2,cannonBox.Top);//将cannonBox的位置更改为左侧的新位置
Application.DoEvents();
System.Threading.Thread.Sleep(10);//将移动延迟几毫秒以停止即时移动
}
if(e.KeyCode==Keys.Right)
{
cannonBox.Location=新点(cannonBox.Left+2,cannonBox.Top);//将cannonBox的位置更改为右侧的新位置
Application.DoEvents();
System.Threading.Thread.Sleep(10);//将移动延迟几毫秒以停止即时移动
}
if(e.KeyCode==Keys.Up)
{
createLaser();//每当按下向上箭头键时调用该方法
}
}
}

但是在不同的网站上,关于这在C#中是如何不可靠的,我将确保不会从上面使用它。在这个实例中,还有哪些替代方法可以代替Application.DoEvents使用?

我建议将事件处理程序
设置为异步
,并使用
wait Task.Delay()
而不是
Thread.Sleep()

private async void Game\u Screen\u KeyDown(对象发送方,KeyEventArgs e)
{
对于(int i=0;i<500;i++)
{
if(e.KeyCode==Keys.Left)
{
cannonBox.Location=新点(cannonBox.Left-2,cannonBox.Top);//将cannonBox的位置更改为左侧的新位置
wait Task.Delay(10);//将移动延迟几毫秒以停止即时移动
}
if(e.KeyCode==Keys.Right)
{
cannonBox.Location=新点(cannonBox.Left+2,cannonBox.Top);//将cannonBox的位置更改为右侧的新位置
wait Task.Delay(10);//将移动延迟几毫秒以停止即时移动
}
if(e.KeyCode==Keys.Up)
{
createLaser();//每当按下向上箭头键时调用该方法
}
}
}
这样,控制流将返回给调用者,您的UI线程有时间处理其他事件(因此不需要
Application.DoEvents()
)。然后在(大约)10毫秒之后,返回控件并继续执行该处理程序

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


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

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

使用“异步”模式(如RenéVogt所建议的)是制作Responsive UI的最佳实践

不过。你必须问问自己,你是否需要一个检查500次的循环,如果向下键是左键、右键还是向上键。特别是当这个循环似乎是由一个键关闭事件触发的

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

或者,您对按键按下事件做出反应,并一次执行操作。=>按左->向左移动一个->再次按左->再向左移动一个。。。等等


使用一个计时器,该计时器每20毫秒调用一次游戏处理

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

示例代码:

[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
    }
}

谢谢你的改进!它的运行比以前平稳得多,没有应用程序时也一样。DoEvents:)请注意,如果在循环完成之前按了左键(或者它被注册为按下,autorepeat?),则实际上会同时运行两个这样的循环。500乘以10毫秒等于5秒,这已经足够让这样的事情发生了。你可以简单地记录下玩家想要向左移动,并将实际移动代码移动到其他事件,比如“空闲”之类的事件,然后记录玩家不再想在向上键时向左移动。游戏循环是少数几个使用Application.DoEvents()从根本上说没有错误的例子之一。但它属于循环,而不属于事件处理程序。谷歌“windows窗体游戏循环”查找示例代码..“主循环”-主循环是什么?你是说?或多或少是的。主要方法是C#应用程序的入口点。
[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
    }
}