在KeyDown事件后删除延迟?

时间:2015-04-29 11:31:16

标签: c# events keypress keydown

当我在游戏中按住一个键移动我的播放器时:

public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Up)
    {
        Player.MoveUp();
    }
}

当我按下向下箭头时,玩家立即移动一步,然后暂停一小段时间,然后再开始平稳移动。这是为什么?我该如何预防?

3 个答案:

答案 0 :(得分:3)

遗憾的是,the proposed duplicate中的答案不正确。它不会忽略重复的KeyDown事件,因此将逐渐增加每个键案例处理方向上的“delta”值。它也不会立即响应按键(即直到第一个计时器滴答时才会发生动作)。

Holding Arrow Keys Down For Character Movement C# .Net ISSUES的答案解释了如何忽略后续的KeyDown事件,但没有解释你的角色将如何移动。

换句话说,我找不到实际正确回答您问题的重复问题。所以......

您想要做的基本技巧是:

  1. 不要移动实际的键输入。相反,生成自己的移动对象的时序逻辑。
  2. 不使用KeyDown事件来实际移动对象,而是使用它来设置移动方向,然后由时序逻辑处理。
  3. 有多种方法可以实现这一目标。一个版本看起来像这样:

    private bool _moveUp;
    private bool _moveDown;
    private bool _moveLeft;
    private bool _moveRight;
    
    // You can add the Timer in the Winforms Designer instead if you like;
    // The Interval property can be configured there at the same time, along
    // with the Tick event handler, simplifying the non-Designer code here.
    private System.Windows.Forms.Timer _movementTimer = new Timer { Interval = 100 };
    
    public MainForm()
    {
        InitializeComponent();
    
        _movementTimer.Tick += movementTimer_Tick;
    }
    
    private void movementTimer_Tick(object sender, EventArgs e)
    {
        _DoMovement();
    }
    
    private void _DoMovement()
    {
        if (_moveLeft) Player.MoveLeft();
        if (_moveRight) Player.MoveRight();
        if (_moveUp) Player.MoveUp();
        if (_moveDown) Player.MoveDown();
    }
    
    // You could of course override the OnKeyDown() method instead,
    // assuming the handler is in the Form subclass generating the
    // the event.
    public void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.IsRepeat)
        {
            // Ignore key repeats...let the timer handle that
            return;
        }
    
        switch (e.KeyCode)
        {
        case Keys.Up:
            _moveUp = true;
            break;
        case Keys.Down:
            _moveDown = true;
            break;
        case Keys.Left:
            _moveLeft = true;
            break;
        case Keys.Right:
            _moveRight = true;
            break;
        }
    
        _DoMovement();
        _movementTimer.Start();
    }
    
    public void MainForm_KeyUp(object sender, KeyEventArgs e)
    {
        switch (e.KeyCode)
        {
        case Keys.Up:
            _moveUp = false;
            break;
        case Keys.Down:
            _moveDown = false;
            break;
        case Keys.Left:
            _moveLeft = false;
            break;
        case Keys.Right:
            _moveRight = false;
            break;
        }
    
        if (!(_moveUp || _moveDown || _moveLeft || _moveRight))
        {
            _movementTimer.Stop();
        }
    }
    

    请注意,.NET中的计时器对象的分辨率有限。我在上面显示了100毫秒(每秒10次)的间隔(与其他问题的答案相同),这与您可靠获得的更新频率相同。即使这样,计时器的Tick事件也可能(并且可能不会)在正好 100 ms间隔时引发。来回会有一些变化。但它足够接近基本游戏。

    如果你需要更高的精度,你必须在某处实现自己的状态轮询和动画循环。那是另一个球。 :)

答案 1 :(得分:0)

一种主观的优雅方法:

public partial class Form1 : Form
{
    private static Timer timer;
    private static bool[] keys_down;
    private static Keys[] key_props;

    private void Form1_Load(object sender, EventArgs e)
    {
        keys_down = new bool[4];
        key_props = new []{Keys.A, Keys.D, Keys.W, Keys.S};
        timer = new Timer();
        timer.Interval = 15; // Roughly 67 FPS
        timer.Tick += tick;
        timer.Start();
        KeyDown += key_down_event;
        KeyUp += key_up_event;
        ... // More things to do when the form loads.
    }

    private void tick(Object source, EventArgs e)
    {
        ... // Do this every timing interval.
        byte n = 0;
        foreach (var v in keys_down)
        {
            if (n == 3 && v)
                ... // If the "s" key is being held down, no key delay issues. :)
            n++;
        }
        ...
    }

    private void key_down_event(object sender, KeyEventArgs e)
    {
        byte n = 0;
        foreach (var v in keys_down)
        {
            if (e.KeyCode == key_props[n])
                keys_down[n] = true;
            n++;
        }
    }

    private void key_up_event(object sender, KeyEventArgs e)
    {
        byte n = 0;
        foreach (var v in keys_down)
        {
            if (e.KeyCode == key_props[n])
                keys_down[n] = false;
            n++;
        }
    }

    public Form1()
    {
        InitializeComponent();
    }
}

答案 2 :(得分:0)

我一直在寻找解决方案来创建Flappy Bird的小本。也许我的决定会帮助某人。 我使用了在计时器中添加变量的球员位置来模拟重力。当我按下W键时,我使用布尔和简单的反向重力关闭了对命令的进一步接收

private void time_Tick(object sender, EventArgs e)
    {
        if (bird.Location.Y >= 540)
        {
            bird.Location = new Point(bird.Location.X, 540);
        }
        else 
        {
            bird.Location = new Point(bird.Location.X, bird.Location.Y+grav);
        } 
    }

private void Form1_KeyPress(object sender, KeyEventArgs e)
    {
        if (e.KeyData == Keys.W && press == false)
        {
            press = true;
            grav = -10;
        }
        return;
    }

    private void Form1_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.KeyData == Keys.W && press == true)
        {
            press = false;
            grav = 5;
        }
        return;
    }