控制台应用程序鼠标单击XY坐标检测/比较

Blo*_*ust 11 c# console-application mouseevent coordinates

我有一个游戏,我正在C#控制台应用程序中工作,纯粹作为练习,然后继续更好的方法.与使用内置按钮功能的Windows窗体应用程序相比,我正在努力抓住光标位置(我知道该怎么做)并将其与控制台应用程序内部的一些区域进行比较,如下所示:也许是像素位置,但我也不知道是否有某种内置单位的空间而不是像素(这最后一位是我无法想象的部分).

PS我知道这是一般性的,没有代码已经提供,但我不觉得它是需要的,因为我要求的是如何在控制台应用程序内抓取XY坐标的简要说明,并将它们粘贴在int中变量.

提前谢谢了!:d

SWd*_*WdV 11

搜索了很久之后我终于找到了这个例子.下载页面上的示例程序.除了其他功能外,它还为您提供了控制台窗口中的鼠标位置(基于字符).

编辑:这是我的ConsoleListener班级(我班级的一部分NativeMethods).
您可以在MouseEvent(调用Start()方法之后)附加处理程序.

using System;
using System.Runtime.InteropServices;
using System.Threading;
using static ConsoleLib.NativeMethods;

namespace ConsoleLib
{
    public static class ConsoleListener
    {
        public static event ConsoleMouseEvent MouseEvent;

        public static event ConsoleKeyEvent KeyEvent;

        public static event ConsoleWindowBufferSizeEvent WindowBufferSizeEvent;

        private static bool Run = false;


        public static void Start()
        {
            if (!Run)
            {
                Run = true;
                IntPtr handleIn = GetStdHandle(STD_INPUT_HANDLE);
                new Thread(() =>
                {
                    while (true)
                    {
                        uint numRead = 0;
                        INPUT_RECORD[] record = new INPUT_RECORD[1];
                        record[0] = new INPUT_RECORD();
                        ReadConsoleInput(handleIn, record, 1, ref numRead);
                        if (Run)
                            switch (record[0].EventType)
                            {
                                case INPUT_RECORD.MOUSE_EVENT:
                                    MouseEvent?.Invoke(record[0].MouseEvent);
                                    break;
                                case INPUT_RECORD.KEY_EVENT:
                                    KeyEvent?.Invoke(record[0].KeyEvent);
                                    break;
                                case INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT:
                                    WindowBufferSizeEvent?.Invoke(record[0].WindowBufferSizeEvent);
                                    break;
                            }
                        else
                        {
                            uint numWritten = 0;
                            WriteConsoleInput(handleIn, record, 1, ref numWritten);
                            return;
                        }
                    }
                }).Start();
            }
        }

        public static void Stop() => Run = false;


        public delegate void ConsoleMouseEvent(MOUSE_EVENT_RECORD r);

        public delegate void ConsoleKeyEvent(KEY_EVENT_RECORD r);

        public delegate void ConsoleWindowBufferSizeEvent(WINDOW_BUFFER_SIZE_RECORD r);

    }


    public static class NativeMethods
    {
        public struct COORD
        {
            public short X;
            public short Y;

            public COORD(short x, short y)
            {
                X = x;
                Y = y;
            }
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct INPUT_RECORD
        {
            public const ushort KEY_EVENT = 0x0001,
                MOUSE_EVENT = 0x0002,
                WINDOW_BUFFER_SIZE_EVENT = 0x0004; //more

            [FieldOffset(0)]
            public ushort EventType;
            [FieldOffset(4)]
            public KEY_EVENT_RECORD KeyEvent;
            [FieldOffset(4)]
            public MOUSE_EVENT_RECORD MouseEvent;
            [FieldOffset(4)]
            public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
            /*
            and:
             MENU_EVENT_RECORD MenuEvent;
             FOCUS_EVENT_RECORD FocusEvent;
             */
        }

        public struct MOUSE_EVENT_RECORD
        {
            public COORD dwMousePosition;

            public const uint FROM_LEFT_1ST_BUTTON_PRESSED = 0x0001,
                FROM_LEFT_2ND_BUTTON_PRESSED = 0x0004,
                FROM_LEFT_3RD_BUTTON_PRESSED = 0x0008,
                FROM_LEFT_4TH_BUTTON_PRESSED = 0x0010,
                RIGHTMOST_BUTTON_PRESSED = 0x0002;
            public uint dwButtonState;

            public const int CAPSLOCK_ON = 0x0080,
                ENHANCED_KEY = 0x0100,
                LEFT_ALT_PRESSED = 0x0002,
                LEFT_CTRL_PRESSED = 0x0008,
                NUMLOCK_ON = 0x0020,
                RIGHT_ALT_PRESSED = 0x0001,
                RIGHT_CTRL_PRESSED = 0x0004,
                SCROLLLOCK_ON = 0x0040,
                SHIFT_PRESSED = 0x0010;
            public uint dwControlKeyState;

            public const int DOUBLE_CLICK = 0x0002,
                MOUSE_HWHEELED = 0x0008,
                MOUSE_MOVED = 0x0001,
                MOUSE_WHEELED = 0x0004;
            public uint dwEventFlags;
        }

        [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
        public struct KEY_EVENT_RECORD
        {
            [FieldOffset(0)]
            public bool bKeyDown;
            [FieldOffset(4)]
            public ushort wRepeatCount;
            [FieldOffset(6)]
            public ushort wVirtualKeyCode;
            [FieldOffset(8)]
            public ushort wVirtualScanCode;
            [FieldOffset(10)]
            public char UnicodeChar;
            [FieldOffset(10)]
            public byte AsciiChar;

            public const int CAPSLOCK_ON = 0x0080,
                ENHANCED_KEY = 0x0100,
                LEFT_ALT_PRESSED = 0x0002,
                LEFT_CTRL_PRESSED = 0x0008,
                NUMLOCK_ON = 0x0020,
                RIGHT_ALT_PRESSED = 0x0001,
                RIGHT_CTRL_PRESSED = 0x0004,
                SCROLLLOCK_ON = 0x0040,
                SHIFT_PRESSED = 0x0010;
            [FieldOffset(12)]
            public uint dwControlKeyState;
        }

        public struct WINDOW_BUFFER_SIZE_RECORD
        {
            public COORD dwSize;
        }

        public const uint STD_INPUT_HANDLE = unchecked((uint)-10),
            STD_OUTPUT_HANDLE = unchecked((uint)-11),
            STD_ERROR_HANDLE = unchecked((uint)-12);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetStdHandle(uint nStdHandle);


        public const uint ENABLE_MOUSE_INPUT = 0x0010,
            ENABLE_QUICK_EDIT_MODE = 0x0040,
            ENABLE_EXTENDED_FLAGS = 0x0080,
            ENABLE_ECHO_INPUT = 0x0004,
            ENABLE_WINDOW_INPUT = 0x0008; //more

        [DllImportAttribute("kernel32.dll")]
        public static extern bool GetConsoleMode(IntPtr hConsoleInput, ref uint lpMode);

        [DllImportAttribute("kernel32.dll")]
        public static extern bool SetConsoleMode(IntPtr hConsoleInput, uint dwMode);


        [DllImportAttribute("kernel32.dll", CharSet = CharSet.Unicode)]
        public static extern bool ReadConsoleInput(IntPtr hConsoleInput, [Out] INPUT_RECORD[] lpBuffer, uint nLength, ref uint lpNumberOfEventsRead);

        [DllImportAttribute("kernel32.dll", CharSet = CharSet.Unicode)]
        public static extern bool WriteConsoleInput(IntPtr hConsoleInput, INPUT_RECORD[] lpBuffer, uint nLength, ref uint lpNumberOfEventsWritten);

    }
}
Run Code Online (Sandbox Code Playgroud)


为了使其正常工作,您可能希望首先执行此代码:

IntPtr inHandle = GetStdHandle(STD_INPUT_HANDLE);
uint mode = 0;
GetConsoleMode(inHandle, ref mode);
mode &= ~ENABLE_QUICK_EDIT_MODE; //disable
mode |= ENABLE_WINDOW_INPUT; //enable (if you want)
mode |= ENABLE_MOUSE_INPUT; //enable
SetConsoleMode(inHandle, mode);
Run Code Online (Sandbox Code Playgroud)

使用此文件头:

using System;
using static ConsoleLib.NativeMethods;
Run Code Online (Sandbox Code Playgroud)

  • 这是一个惊人的发现!帮了我很多,谢谢! (2认同)

小智 7

此外,控制台不仅适用于文本处理.你可以为它编写相当不错的窗口管理器.你可以用它做任何事情.这更难了.

但速度较慢.我使用用户界面的控制台在C#中实现了一个虚拟机.它不会一个接一个地打印文本行; 它[界面]的行为就像一个GUI.

如果你想在控制台上输入鼠标,试试这个钩子:http: //blogs.msdn.com/b/toub/archive/2006/05/03/589468.aspx?PageIndex=2#comments


小智 5

当您在不使用事件的情况下编写游戏时,您实际上就是在自己实现事件。这是有优势的,因为与使用语言的内置事件相比,您可以使其效率更高。如果您知道自己在做什么,那么用这种方式编写的游戏就不太容易出错。

例如,当我试图教我的兄弟如何编写游戏时,我为他编写了一个简单的蛇游戏。我将主循环置于线程中,移动蛇并在一个循环中将其绘制到新位置。我将同时运行一个线程,该线程连续检查4件事情:

  1. 如果蛇撞向自己(游戏结束);如果游戏结束,则暂停更新蛇的主要位置的主线程,将游戏打印到屏幕上,等待键输入,然后重新启动游戏。

  2. 如果蛇吃了一个苹果;增加表示已经吃了多少苹果的计数器变量,然后在屏幕上打印此新值,覆盖以前的值。

  3. 如果蛇吃了许多被10整除的苹果(蛇长出1个单元格,请从等待变量中减去,该变量表示蛇在每次移动之间应经过多少时间)

  4. 如果已按下箭头键。如果向左移动,则将移动设置为0,如果向右移动,则设置为1;如果向下移动,则设置为2,如果向上移动为3。存储在其中的int是指向使蛇移动的4个委托的数组的指针在正确的方向。

更新蛇的位置的主循环将告诉检查蛇这四件事的线程。我这样做的方法是让屏幕上蛇头移动的每个单元格都引用二维代表数组。关于这个代表数组:

游戏以控制台模式编写,并使用控制台颜色。控制台设置为80x50个字符。委托如下:“ delegate void ptr()”; 然后我用以下数组创建数组:“ ptr [,] pos = new ptr [80,50]”。假设蛇的头部位于屏幕上的位置(4,5),移动到那里后,主循环将执行“ pos [4,5] .Invoke();”。

其中之一:当蛇移动到新位置时,主循环线程将获取蛇在屏幕上覆盖的每个单元格,并将该委托设置在该位置以指向名为“ void gameover()”的函数,该函数将将gameover_变量设置为true。因此,当检查游戏状态的循环线程检查游戏结束时,它将冻结游戏并在屏幕上打印游戏。

另一方法:当在屏幕上绘制一个苹果时,其绘制位置(是随机的)的代表位置设置为指向“ void增量_apple()”,该值将递增苹果计数器,从视图中删除当前苹果,并绘制一个屏幕上的新苹果,将旧苹果位置设置为不执行任何操作的“ void nop()”,将新苹果位置设置为指向“ void增量_apple()”。

基本上这就是游戏的运作方式。如您所见,蛇会移动到屏幕上的这些位置,并且无需执行任何明确的检查(例如“ if(snake_position == some_position)”),游戏就会自动为游戏中发生的所有事情执行预期的操作,就像您单击表单上的按钮时一样,分配给该事件的动作将自动执行,而无需您自己检查该事件。

如此看来,我本可以使用C#提供的表单和默认事件,但没有使用。我使用了控制台界面,并实现了自己的事件系统。

这就是它在后台的工作方式:表单应用程序的主循环将在线程中运行,该线程检查屏幕上所有按钮等的输入。这些项目中的每一项都会将它们使用的布尔变量设置为true。当您单击该按钮时,另一个运行循环的线程将检查您所按的内容,并说您按下了一个名为“ button1”的按钮,该按钮将被分配一个委托。然后,该委托将随其指向的对象一起执行。

有点难以解释,但这对您有意义吗?


Jer*_*Gee 1

@Frank Krueger 说的话。你真的想这样做吗?Windows Forms 的设计就是为了实现这一点容易。

如果这样做,您将需要在低级 Windows API 中使用 PInvoke。尝试将此作为起点 - 但请注意,这比 Windows 窗体应用程序要复杂得多。