简体   繁体   English

控制台检查是否有按键,例如在单机游戏中

[英]Console check for pressed keys like in monogame

In my console application, I would like to be able to detect for key presses somewhat like in mono, where you can get a list of the currently pressed keys and check if a key is on the list, or detect if a key is being pressed. 在我的控制台应用程序中,我希望能够像在mono中那样检测按键的按下情况,在这里您可以获得当前按下的按键的列表,并检查按键是否在列表中,或者检测是否按下了按键。 。 My problem with using 我的使用问题

if( Console.KeyAvailable ) k = Console.ReadKey( true ).Key;

is the fact that there will be a delay after I press the first key. 我按下第一个键后会有延迟的事实。 You can see an example of this if you open notepad and hold down "A". 如果打开记事本并按住“ A”,您可以看到一个示例。 A will be pressed, then a delay, and then A will be spammed. A将被按下,然后被延迟,然后A将被发送垃圾邮件。

How can I get keyboard input without the delay in between presses? 如何获得键盘输入而没有按键之间的延迟? I'm not afraid to use low level functions such as hooking into kernel32.dll 我不怕使用低级功能,例如挂接到kernel32.dll

So I wrote some code, based off what I've read so far. 所以我根据到目前为止所读的内容编写了一些代码。

Step 1: Copy the following code into your console application. 步骤1:将以下代码复制到控制台应用程序中。 It must be STAThread or it will throw an error. 它必须是STAThread,否则将引发错误。 Put in the commands you want to use in the switch statement. 在switch语句中输入要使用的命令。 All other keys will be blocked by ReadKey(true). 所有其他键将被ReadKey(true)阻止。

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using System;
using System.Windows.Forms;
using System.Windows.Input;

namespace ConsoleApplication10
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            KeyListener.RegisterHotKey(Keys.A);
            KeyListener.HotKeyPressed += new EventHandler<HotKeyEventArgs>(KeyListener_HotKeyPressed);
            while (true)
            {
                Console.ReadKey(true);
            }
        }

        static void KeyListener_HotKeyPressed(object sender, HotKeyEventArgs e)
        {
            switch (e.Key)
            {
                case Keys.A:
                {
                    Console.WriteLine("Do stuff");
                    return;
                }
                default:
                    return;
            }
        }
    }
}

Step 2: Add a reference to System.Windows.Forms. 步骤2:添加对System.Windows.Forms的引用。 You need this to have a hidden Form that is necessary for the message loop for the keyboard hook. 您需要具有隐藏的Form,这对于键盘挂钩的消息循环是必需的。

Step 3: Add the following static class. 步骤3:添加以下静态类。 It does all the heavy lifting of the keyboard hooks for you, so you don't have to do it. 它为您完成了所有繁重的键盘挂钩,因此您不必这样做。

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;

namespace ConsoleApplication10
{
    public static class KeyListener
    {
        public static event EventHandler<HotKeyEventArgs> HotKeyPressed;

        public static int RegisterHotKey(Keys key, KeyModifiers modifiers)
        {
            _windowReadyEvent.WaitOne();
            int id = System.Threading.Interlocked.Increment(ref _id);
            _wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, (uint)modifiers, (uint)key);
            return id;
        }

        public static int RegisterHotKey(Keys key)
        {
            _windowReadyEvent.WaitOne();
            int id = System.Threading.Interlocked.Increment(ref _id);
            _wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, (uint)KeyModifiers.None, (uint)key);
            return id;
        }

        public static void UnregisterHotKey(int id)
        {
            _wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
        }

        delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
        delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);

        private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
        {
            RegisterHotKey(hwnd, id, modifiers, key);
        }

        private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
        {
            UnregisterHotKey(_hwnd, id);
        }

        private static void OnHotKeyPressed(HotKeyEventArgs e)
        {
            if (KeyListener.HotKeyPressed != null)
            {
                KeyListener.HotKeyPressed(null, e);
            }
        }

        private static volatile MessageWindow _wnd;
        private static volatile IntPtr _hwnd;
        private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
        static KeyListener()
        {
            Thread messageLoop = new Thread(delegate ()
            {
                Application.Run(new MessageWindow());
            });
            messageLoop.Name = "MessageLoopThread";
            messageLoop.IsBackground = true;
            messageLoop.Start();
        }

        private class MessageWindow : Form
        {
            public MessageWindow()
            {
                _wnd = this;
                _hwnd = this.Handle;
                _windowReadyEvent.Set();
            }

            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_HOTKEY)
                {
                    HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
                    KeyListener.OnHotKeyPressed(e);
                }

                base.WndProc(ref m);
            }

            protected override void SetVisibleCore(bool value)
            {
                // Ensure the window never becomes visible
                base.SetVisibleCore(false);
            }

            private const int WM_HOTKEY = 0x312;
        }

        [DllImport("user32", SetLastError = true)]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

        [DllImport("user32", SetLastError = true)]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        private static int _id = 0;
    }


    public class HotKeyEventArgs : EventArgs
    {
        public readonly Keys Key;
        public readonly KeyModifiers Modifiers;

        public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
        {
            this.Key = key;
            this.Modifiers = modifiers;
        }

        public HotKeyEventArgs(IntPtr hotKeyParam)
        {
            uint param = (uint)hotKeyParam.ToInt64();
            Key = (Keys)((param & 0xffff0000) >> 16);
            Modifiers = (KeyModifiers)(param & 0x0000ffff);
        }
    }

    [Flags]
    public enum KeyModifiers
    {
        None = 0,
        Alt = 1,
        Control = 2,
        Shift = 4,
        Windows = 8,
        NoRepeat = 0x4000
    }
}

Step 4: 第四步:

Now, there's still a delay. 现在,仍然有延迟。 It's way more elegant, but you still have the operating system fighting you. 它的方式更优雅,但是您仍然需要操作系统来对抗您。 So what to do? 那么该怎么办?

You have two options. 您有两个选择。

a) You implement a timing option and you simply repeat the action for as long as a key is down on the timer tick event. a)您实现了一个计时选项,并且只要在计时器滴答事件中按下了一个键,您就只需重复该操作即可。 You can either copy the code or merge it with the hotkey approach that I've given you. 您可以复制代码,也可以将其与我提供的热键方法合并。

See here for details: Removing the delay after KeyDown event? 有关详细信息,请参见此处: 消除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();
    }
}

b) In your Main method, you get the delay setting, set the delay programmatically to the lowest setting, and on Application Exit, you set it back to its original setting. b)在您的Main方法中,获得延迟设置,以编程方式将延迟设置为最低设置,然后在Application Exit上将其重新设置为原始设置。

See here for where to find it in the registry: https://superuser.com/questions/388160/keyboard-repeat-rate-repeat-delay-values-in-win7 在此处找到在注册表中的位置: https : //superuser.com/questions/388160/keyboard-repeat-rate-repeat-delay-values-in-win7

And how to read/write to the registry: https://msdn.microsoft.com/en-us/library/microsoft.win32.registry_methods(v=vs.110).aspx 以及如何读取/写入注册表: https : //msdn.microsoft.com/zh-cn/library/microsoft.win32.registry_methods(v=vs.110).aspx

Note: With this approach, there's still a small delay. 注意:使用这种方法,仍然会有一些延迟。 It's small, but it's there. 它很小,但是在那里。 Good luck. 祝好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM