14

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

PS我知道这是笼统的,还没有提供任何代码,但我觉得没有必要,因为我所要求的只是简要说明如何在控制台应用程序中获取 XY 坐标,并将它们粘贴在 int 中变量。

提前谢谢了!:D

4

7 回答 7

15

找了半天终于找到了这个例子。下载页面上的示例程序。它为您提供控制台窗口中的鼠标位置(基于字符)。

编辑:这是我的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;
于 2015-04-30T15:03:52.187 回答
7

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

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

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

于 2010-06-17T13:43:12.787 回答
4

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

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

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

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

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

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

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

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

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

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

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

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

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

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

于 2010-06-17T13:38:41.363 回答
2

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

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

于 2009-12-22T06:52:10.327 回答
1

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

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-代码?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,
}
于 2021-08-03T03:50:43.597 回答
0

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

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

于 2017-04-15T22:24:18.367 回答
-2
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;
        }
    }
}

于 2017-04-15T22:32:24.580 回答