我正在制作一个简单的 WinForm 赛车游戏。我有两个对象 - 汽车,当按下键时它们在表单上移动(Form1KeyDown_Event)。
唯一的问题是,当一个玩家按下一个键时,另一个玩家不能按下他的键(什么都没有发生)。但是当第一个玩家松开钥匙时,第二个玩家可以按下他的一个键并正常控制他的车。
如何同时收听两个播放器键?我应该使用线程并让每辆车都有自己的线程吗?
我正在制作一个简单的 WinForm 赛车游戏。我有两个对象 - 汽车,当按下键时它们在表单上移动(Form1KeyDown_Event)。
唯一的问题是,当一个玩家按下一个键时,另一个玩家不能按下他的键(什么都没有发生)。但是当第一个玩家松开钥匙时,第二个玩家可以按下他的一个键并正常控制他的车。
如何同时收听两个播放器键?我应该使用线程并让每辆车都有自己的线程吗?
这是一个简单的示例,说明您可以使用 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/Invoking 到本机方法,但非常简单。你想要的钩子是 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 线程上设置它。