【问题标题】:Console App Mouse-Click X Y Coordinate Detection/Comparison控制台应用程序鼠标单击 X Y 坐标检测/比较
【发布时间】:2010-12-29 00:07:41
【问题描述】:

我有一个正在 C# 控制台应用程序中开发的游戏,纯粹是作为练习,然后再使用更好的方法。与使用诸如内置按钮功能的 Windows 窗体应用程序之类的东西相反,我正在努力抓住光标位置(我知道该怎么做)并将其与控制台应用程序中定义的多个区域进行比较也许是像素位置,但我也不知道除了像素之外是否还有某种内置的空间单位(最后一点是我无法计算的部分)。

附:我知道这是笼统的说法,没有提供任何代码,但我认为不需要它,因为我所要求的只是简要说明如何在控制台应用程序中获取 XY 坐标,并将它们粘贴到 int 变量中.

提前非常感谢! :D

【问题讨论】:

  • 控制台应用程序通常不与鼠标交互,也不太关心像素坐标系。虽然我确信您将获得一堆描述如何捕获鼠标之类的答案,但我认为您最好退后一步,重新考虑您在做什么。
  • 非常感谢您的评论,但我正在尽可能多地学习 C# 控制台应用程序。大约 3 个月后,我会感到满意,然后继续学习表格,最终学习新的语言。我已经阅读了您博客的一部分,并感谢您对首先学习哪种语言的看法。让一种语言做一些在某些情况下通常不会做的事情只是挑战的一部分。
  • 这不能通过挂钩鼠标点击来完成吗?也许this

标签: c# console-application coordinates mouseevent


【解决方案1】:

找了很久终于找到this example。下载页面上的example program。除其他外,它还为您提供控制台窗口中的鼠标位置(基于字符)。

编辑:这是我的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);

    }
}


为了使其正常工作,您可能需要先执行此代码:

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);

有了这个文件头:

using System;
using static ConsoleLib.NativeMethods;

【讨论】:

  • 这是一个了不起的发现!帮了我很多忙,谢谢!
【解决方案2】:

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

不过,它比较慢。 我使用控制台作为用户界面在 C# 中实现了一个虚拟机。它不会一个接一个地打印文本行;它[界面] 的行为更像是一个 GUI。

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

【讨论】:

    【解决方案3】:

    当您编写游戏而不使用事件时……您实际上是在自己实现事件。这是有利的,因为您可以使它比使用您的语言的内置事件更有效。如果您知道自己在做什么,那么以这种方式编写的游戏就不容易出错。

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

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

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

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

      李>
    4. 如果已按下箭头键。如果向左,则设置移动到 0,如果向右设置移动到 1,如果向下设置移动到 2,如果向上设置移动到 3。存储 this 的 int 是指向 4 个委托的数组的指针,这些委托使蛇移动方向正确。

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

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

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

    另一个: 当在屏幕上绘制一个苹果时,它被绘制的代理位置(随机)被设置为指向“void increment_apple()”,这会增加苹果计数器,从视图中删除当前苹果,并绘制一个新苹果在屏幕上,将旧的苹果位置设置为指向什么都不做的“void nop()”,并将新的苹果位置设置为指向“void increment_apple()”。

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

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

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

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

    【讨论】:

      【解决方案4】:

      @Frank Krueger 说了什么。你真的想这样做吗? Windows 窗体旨在让这更多变得更容易。

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

      【讨论】:

      • 哇,太复杂了。我的印象是游戏是作为控制台应用程序编写的,或者类似的东西,而不是 Windows 窗体等,因为它们是基于事件的。没有更简单的方法吗?
      • 我们确实警告过您!控制台应用程序实际上仅用于处理文本或文件的实用程序。在 PC 上,图形密集型游戏通常使用 DirectX、WPF 或 Windows 窗体——Windows 窗体可能是最容易上手的。如您所见,Windows 窗体等 UI 框架都是关于事件以及如何处理它们的。
      • 那为什么我总是听说游戏不是基于事件的,而是基于循环的?
      • 链接好像坏了。
      【解决方案5】:

      我知道这是不久前的事了,但在控制台中获取鼠标位置并不是特别难。我将向您展示我使用的代码,以防其他人想要答案:

      public static class Input
      {
          [DllImport("user32.dll")]
          static extern bool GetCursorPos(out POINT point);
      
          struct POINT
          {
              public int x;
              public int y;
          }
          public static POINT GetMousePosition()
          {
              POINT pos;
              GetCursorPos(out pos);
              return pos;
          }
      }
      

      享受吧!

      编辑: 我只是想出了一个简单的方法来获得鼠标点击(这也适用于键盘按钮,只需参考https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes?redirectedfrom=MSDN 了解您的需要

      [DllImport("user32.dll")]
      public static extern bool GetAsyncKeyState(int button);
      public static bool IsMouseButtonPressed(MouseButton button)
      {
          return GetAsyncKeyState((int)button);
      }
      public enum MouseButton
      {
          LeftMouseButton = 0x01,
          RightMouseButton = 0x02,
          MiddleMouseButton = 0x04,
      }
      

      【讨论】:

      • 为什么不用GetAsyncKeyState((int)button) 而不是GetAsyncKeyState((int)button) != false
      • 这是一个很好的问题,我想我在想只要我按下一个键,但我删除了它。谢谢!
      【解决方案6】:

      经过大量研究,我找到了解决方案。

      使用 Button 类和我在下面创建的 GUI,可以制作一个按钮,然后用鼠标或鼠标单击它(它不能完美地工作)。 而且你需要导入System.Windows.FormsSystem.Drawing

      【讨论】:

        【解决方案7】:
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks;
        using Traingames.NetElements;
        //using System.Windows.Forms;
        using System.Drawing;
        
        namespace ConsoleTools.NET
        {
            class Program
            {
                static ConsoleFramework c = new ConsoleFramework();
                static public Point MousePos;
                static Button One = new Button();
                static Pixel Mouse = new Pixel();
        
                static void Main(string[] args)
                {
                    Console.ForegroundColor = ConsoleColor.White;
                    // t.Draw(10, 40, ConsoleColor.Gray);
                    One.Set(0, 10, "░░1░░", ConsoleColor.Gray);
        
                    GUI.Add(One);
                    GUI.CalculateOnStart();
                    for (;;)
                    {
                        MousePos = new Point(System.Windows.Forms.Control.MousePosition.X / (Console.LargestWindowWidth / 24), System.Windows.Forms.Control.MousePosition.Y / (Console.LargestWindowHeight / 7));
                        if (One.Pressed(MousePos))
                        {
                            Console.Write("1");
                        }
                        //   Console.Clear();
                    }
                }
            }
            }
        
            namespace Traingames.NetElements
            {
                public class ConsoleFramework
                {
                public char[] chars = { '█', '▓', '▒', '░' };
        
                Point MousePos()
                {
                    return new Point((System.Windows.Forms.Control.MousePosition.X / (Console.LargestWindowWidth / 24)) - 100, System.Windows.Forms.Control.MousePosition.Y / (Console.LargestWindowHeight / 7));
                }
        
                public void SetPixel(int x, int Y, ConsoleColor color)
                {
                    int y = (int)Math.Floor(Y / 1.5f);
        
                    for (int i = 0; i < y; i++)
                    {
                        Console.WriteLine("");
                    }
        
                    for (int i = 0; i < x - 1; i++)
                    {
                        Console.Write(" ");
                    }
                    Console.BackgroundColor = color;
                    Console.Write(" ");
                    Console.BackgroundColor = ConsoleColor.Black;
                }
            }
        
            public class Pixel : GUI
            {
                public void Set(int X, int Y, string text)
                {
                    ConsoleColor backColor = ConsoleColor.Black;
                    BackColor = backColor;
                    int yyyyyy = (int)Math.Floor(Y / 1.5f);
                    Text = text;
                    y = Y;
                    x = X;
                }
            }
        
            public class GUI
            {
                public int x, y;
                public static GUI[,] GraphicalUserInterfaces = new GUI[1000, 1000];
                public ConsoleColor BackColor;
                public string Text;
        
                public void Draw()
                {
                    int X = x;
                    int Y = y;
                    ConsoleColor backColor = BackColor;
                    string text = Text;
        
        
                    for (int i = 0; i < y; i++)
                    {
                        Console.WriteLine("");
                    }
        
                    for (int i = 0; i < x - 1; i++)
                    {
                        Console.Write(" ");
                    }
                    Console.BackgroundColor = BackColor;
                    Console.Write("[" + text + "]");
                    Console.BackgroundColor = ConsoleColor.Black;
                    Point M = ConsoleTools.NET.Program.MousePos;
        
                    // return M.X >= xx && M.X <= (xx + Text.Length + 1) && M.Y >= yy && M.Y <= yy + 2 && Control.MouseButtons == MouseButtons.Left;
                }
                static GUI Last;
                public static void Add(GUI gui)
                {
                    GraphicalUserInterfaces[gui.x, gui.y] = gui;
                }
        
                public static void CalculateOnStart()
                {
                    for (int x = 0; x < 1000; x++)
                    {
                        for (int y = 0; y < 1000; y++)
                        {
                            if (GraphicalUserInterfaces[x, y] != null)
                            {
        
                                if (Last != null && y < Last.y)
                                {
                                    GraphicalUserInterfaces[x, y].x = Last.x - GraphicalUserInterfaces[x, y].x;
                                    GraphicalUserInterfaces[x, y].y = Last.y - GraphicalUserInterfaces[x, y].y;
                                }
                                GraphicalUserInterfaces[x, y].Draw();
                                GraphicalUserInterfaces[x, y].x = x;
                                GraphicalUserInterfaces[x, y].y = y;
                                Last = GraphicalUserInterfaces[x, y];
                            }
        
                        }
                    }
                }
        
            }
        
            public class Button : GUI
            {
        
                public bool Over(Point M)
                {
                    int yy = ((y * 2) - y / 3) + 2;
        
                    int xx = (x / (Console.LargestWindowWidth / 24)) + Text.Length;
        
                    if (M.X >= xx && M.X <= (xx + Text.Length + 1) && M.Y >= yy && M.Y <= yy + 2)
                        Console.BackgroundColor = ConsoleColor.DarkBlue;
        
                    return M.X >= xx && M.X <= (xx + Text.Length + 1) && M.Y >= yy && M.Y <= yy + 2;
                }
        
                public bool Pressed(Point M)
                {
                    int yy = ((y * 2) - y / 3) + 1;
        
                    int xx = (x / (Console.LargestWindowWidth / 24));
        
                    return M.X >= xx && M.X <= (xx + Text.Length * 1.5f) && M.Y >= yy && M.Y <= yy + 2 && System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left;
                }
        
                public void CalculateClick(Point M)
                {
                    if (Pressed(M))
                    {
                        Console.Clear();
                        Draw();
                    }
                }
        
                public void Set(int X, int Y, string text, ConsoleColor backColor)
                {
                    BackColor = backColor;
                    int yyyyyy = (int)Math.Floor(Y / 1.5f);
                    Text = text;
                    y = Y;
                    x = X;
        
                    int xx = (x / (Console.LargestWindowWidth / 24)) + Text.Length;
                }
            }
        }
        
        

        【讨论】:

        • 无解释的代码块难以理解,供以后参考;尝试添加简短的描述性文字
        猜你喜欢
        • 1970-01-01
        • 2014-03-20
        • 1970-01-01
        • 2012-05-31
        • 2011-01-10
        • 2014-12-02
        • 1970-01-01
        • 2016-11-30
        • 2019-01-15
        相关资源
        最近更新 更多