3

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

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

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

4

2 回答 2

3

这是一个简单的示例,说明您可以使用 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" : "-";
        }
    }
}
于 2013-05-25T01:36:19.567 回答
0

您可能想要调查较低级别并使用 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 线程上设置它。

于 2013-05-25T01:53:22.993 回答