如何摆脱C#中的字符重复延迟?

时间:2009-07-23 15:32:02

标签: c# keypress

http://www.microsoft.com/windowsxp/using/accessibility/characterrepeatrate.mspx - Windows中有一个用于设置重复延迟的选项。这意味着如果持续按下键,第一次击键和其他击键之间的延迟。我正在创造一种游戏,我需要摆脱这种“功能”。

到目前为止,我已经设法找到了这个方法:



[DllImport("user32.dll")]
static extern ushort GetAsyncKeyState(int vKey);

public static bool IsKeyPushedDown(Keys keyData)
{
   return 0 != (GetAsyncKeyState((int)keyData) & 0x8000);
}

但是方法IsKeyPushedDown发现在调用函数的时刻是否按下了键 - 所以我需要一个循环来测试密钥是否关闭。问题是它仍然没有捕获所有的击键 - 我猜的循环太慢了。

第二个选择是覆盖ProcessCmdKey:


protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
  // processing keys
            if (keyData == Keys.Left || keyData == Keys.Right || keyData == Keys.Up || keyData == Keys.Down)
            {
                return true;
            }
            else
            {
                return base.ProcessCmdKey(ref msg, keyData);
            }

}

这个效果非常好,但它会受到重复延迟的影响,因此我的怪物在游戏中的移动就像:

我的问题有解决方案吗? 感谢

编辑:我通过组合这两个程序解决了这个问题。但这是非常难看的解决方案。我仍然希望有更好的解决方案。

2 个答案:

答案 0 :(得分:4)

如果您想要更好地控制按下和释放按键之间发生的事情,您应该处理KeyDownKeyUp而不是ProcessCmdKey

在你的场景中最简单的事情就是在你的表单上有一个移动怪物的计时器。在KeyDown被触发时启用计时器(并设置某种变量或指示如何移动怪物的deltaX和deltaY),然后在KeyUp被触发时停止计时器。

修改

作为一个例子(非常准确)

public class MyForm : Form
{
    private int deltaX;
    private int deltaY;

    private const int movementAmount = 10;

    private Timer movementTimer = new Timer();

    public MyForm()
    {
        movementTimer.Interval = 100; // make this whatever interval you'd like there to be in between movements

        movementTimer.Tick += new EventHandler(movementTimer_Tick);
    }

    void movementTimer_Tick(object sender, EventArgs e)
    {
        myMonster.Location = new Point(myMonster.X + deltaX, myMonster.Y + deltaY);
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);

        switch (e.KeyCode)
        {
            case Keys.Left:
                {
                    deltaX -=movementAmount;
                } break;
            case Keys.Right:
                {
                    deltaX += movementAmount;
                } break;
            case Keys.Up:
                {
                    deltaY -= movementAmount;
                } break;
            case Keys.Down:
                {
                    deltaY += movementAmount;
                } break;
        }

        UpdateTimer();
    }

    protected override void OnKeyUp(KeyEventArgs e)
    {
        base.OnKeyUp(e);

        switch (e.KeyCode)
        {
            case Keys.Left:
                {
                    deltaX += movementAmount;
                } break;
            case Keys.Right:
                {
                    deltaX -= movementAmount;
                } break;
            case Keys.Up:
                {
                    deltaY += movementAmount;
                } break;
            case Keys.Down:
                {
                    deltaY -= movementAmount;
                } break;
        }

        UpdateTimer();
    }

    private void UpdateTimer()
    {
        movementTimer.Enabled = deltaX != 0 || deltaY != 0;
    }
}

答案 1 :(得分:0)

我不喜欢计时器和pInvoke。

在WinForms程序中,我如下解决了不必要的键重复

    List<Keys> cKeys = new List<Keys>() { Keys.Q, Keys.W, Keys.E, Keys.R,
       Keys.T, Keys.Y, Keys.U, Keys.I, Keys.O, Keys.P, 
       Keys.OemOpenBrackets, Keys.OemCloseBrackets };  // define relevant keys
  
    Keys keepc = Keys.IMENonconvert; // some key which is not relevant

    private void Form2_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode!=keepc) // repeater detect
        {
            keepc = e.KeyCode;
            if (cKeys.IndexOf(e.KeyCode) >= 0) // if relevant key..
            {
                // do press key action
            }
        }         
    }

    private void Form2_KeyUp(object sender, KeyEventArgs e)
    { 
        keepc = Keys.IMENonconvert; // reset the repeater detect
        if (cKeys.IndexOf(e.KeyCode) >= 0) // if relevant key..
        {
            // do release key action
        }
    }