簡體   English   中英

控制台檢查是否有按鍵,例如在單機游戲中

[英]Console check for pressed keys like in monogame

在我的控制台應用程序中,我希望能夠像在mono中那樣檢測按鍵的按下情況,在這里您可以獲得當前按下的按鍵的列表,並檢查按鍵是否在列表中,或者檢測是否按下了按鍵。 。 我的使用問題

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

我按下第一個鍵后會有延遲的事實。 如果打開記事本並按住“ A”,您可以看到一個示例。 A將被按下,然后被延遲,然后A將被發送垃圾郵件。

如何獲得鍵盤輸入而沒有按鍵之間的延遲? 我不怕使用低級功能,例如掛接到kernel32.dll

所以我根據到目前為止所讀的內容編寫了一些代碼。

步驟1:將以下代碼復制到控制台應用程序中。 它必須是STAThread,否則將引發錯誤。 在switch語句中輸入要使用的命令。 所有其他鍵將被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;
            }
        }
    }
}

步驟2:添加對System.Windows.Forms的引用。 您需要具有隱藏的Form,這對於鍵盤掛鈎的消息循環是必需的。

步驟3:添加以下靜態類。 它為您完成了所有繁重的鍵盤掛鈎,因此您不必這樣做。

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
    }
}

第四步:

現在,仍然有延遲。 它的方式更優雅,但是您仍然需要操作系統來對抗您。 那么該怎么辦?

您有兩個選擇。

a)您實現了一個計時選項,並且只要在計時器滴答事件中按下了一個鍵,您就只需重復該操作即可。 您可以復制代碼,也可以將其與我提供的熱鍵方法合並。

有關詳細信息,請參見此處: 消除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)在您的Main方法中,獲得延遲設置,以編程方式將延遲設置為最低設置,然后在Application Exit上將其重新設置為原始設置。

在此處找到在注冊表中的位置: https : //superuser.com/questions/388160/keyboard-repeat-rate-repeat-delay-values-in-win7

以及如何讀取/寫入注冊表: https : //msdn.microsoft.com/zh-cn/library/microsoft.win32.registry_methods(v=vs.110).aspx

注意:使用這種方法,仍然會有一些延遲。 它很小,但是在那里。 祝好運。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM