响应多个KeyDown事件

本文关键字:事件 KeyDown 响应 | 更新日期: 2023-09-27 18:15:31

我正在制作一个简单的WinForm赛车游戏。我有两个对象-汽车,当键被按下时,它们在表单上移动(Form1KeyDown_Event)。

唯一的问题是,当一个玩家按下一个键时,另一个玩家不能按他的键(什么也没有发生)。但是当第一个玩家释放键时,第二个玩家可以按下他的一个键并正常控制他的车。

我如何同时收听两个播放器键?我应该使用线程,让每辆车都有自己的线程吗?

响应多个KeyDown事件

这里有一个简单的示例,说明您可以使用keyup和keydown事件来同时监听多个键。

using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace WinFormTest {
    public partial class Form1 : Form {
        private readonly IDictionary<Keys, bool> downState;
        public Form1() {
            InitializeComponent();
            downState = new Dictionary<Keys, bool>();
            downState.Add(Keys.W, false);
            downState.Add(Keys.D, false);
            KeyDown += remember;
            KeyUp += forget;
        }
        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);
            Timer timer = new Timer() { Interval = 100 };
            timer.Tick += updateGUI;
            timer.Start();
        }
        private void remember(object sender, KeyEventArgs e) {
            downState[e.KeyCode] = true;
        }
        private void forget(object sender, KeyEventArgs e) {
            downState[e.KeyCode] = false;
        }
        private void updateGUI(object sender, EventArgs e) {
            label1.Text = downState[Keys.W] ? "Forward" : "-";
            label2.Text = downState[Keys.D] ? "Right" : "-";
        }
    }
}

您可能想要研究更低级的方法,并使用windows钩子来检测键盘事件。这需要在本机方法中调用P/invoke,但是非常直接。您想要的钩子是WH_LL_KEYBOARD。详情可在pinvoke.net找到。

您需要一些样板文件,但它是您可以合理期望得到的最接近键盘事件的:

[StructLayout(LayoutKind.Sequential)]
public struct KBDLLHOOKSTRUCT
{
    public uint vkCode;
    public uint scanCode;
    public uint flags;
    public uint time;
    public IntPtr dwExtraInfo;
}
public delegate IntPtr LowLevelKeyboardProc(int, IntPtr, KBDLLHOOKSTRUCT);
[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idhook, LowLevelKeyboardProc proc, IntPtr hMod, uint threadId);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
public static IntPtr SetHook(LowLevelKeyboardProc proc)
{
    using (var curProc = Process.GetCurrentProcess())
    using (var curMod = curProc.MainModule)
    {
        return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curMod.ModuleName), 0u);
    }
}
public IntPtr MyKeyboardHook(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT keyboardInfo)
{
    if (code < 0)
    {
        return CallNextHookEx(IntPtr.Zero, wParam, ref keyboardInfo);
    }
    // Do your thing with the keyboard info.
    return CallNextHookEx(IntPtr.Zero, code, wParam, ref keyboardInfo);
}

确保当你的应用不再需要你的处理程序时,把它解挂钩。KBDLLHOOKSTRUCT封装了Windows将给你的关于键盘事件的所有信息;其成员的详细信息可在MSDN找到。

这种钩子的一个细节是它会在注册它的线程上执行,所以请确保你注意到这一点,如果它要做任何长时间运行的事情,不要将它设置在UI线程上。