【问题标题】:Removing the delay after KeyDown event?消除 KeyDown 事件后的延迟?
【发布时间】:2015-07-08 15:53:57
【问题描述】:

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

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

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

【问题讨论】:

标签: c# events keypress keydown


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    一种主观上优雅的方法:

    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();
        }
    }
    

    【讨论】:

      【解决方案3】:

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

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

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

      你要做的基本技巧是:

      1. 不要继续实际的键输入。相反,请生成您自己的时间逻辑来移动对象。
      2. 不要使用KeyDown 事件来实际移动对象,而是使用它来设置移动方向,然后由您的计时逻辑处理。

      有多种方法可以做到这一点。一个版本如下所示:

      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 毫秒的间隔触发。来回会有一些变化。但它对于基本游戏来说已经足够接近了。

      如果您需要比这更高的精度,则必须在某处实现自己的状态轮询和动画循环。那是一个完全不同的蜡球。 :)

      【讨论】:

      • 我没有“IsRepeat”的属性,所以我不得不单独做一个函数来判断我是否已经在移动了。
      猜你喜欢
      • 2012-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-27
      • 1970-01-01
      相关资源
      最近更新 更多