Консольное приложение Mouse-Click X Y Обнаружение / сравнение координат - PullRequest
10 голосов
/ 22 декабря 2009

У меня есть игра, над которой я работаю в консольном приложении C #, просто на практике, прежде чем переходить к лучшим методам. В отличие от использования чего-либо, такого как приложение Windows Forms, в которое встроены функции кнопок, я стараюсь захватить положение курсора (что я знаю, как это сделать) и сравнить его с количеством областей внутри консольного приложения, как определено возможно, в пикселях, но я также не знаю, есть ли какая-то встроенная единица пространства, кроме пикселей (этот последний бит - часть, которую я не могу изобразить).

P.S. Я знаю, что это в общих чертах, без предоставленного кода, но я не чувствую, что это необходимо, поскольку все, о чем я прошу, это краткое объяснение того, как получить координаты XY внутри консольного приложения и вставить их в переменные типа int .

Большое спасибо заранее! : D

Ответы [ 6 ]

10 голосов
/ 30 апреля 2015

После долгого поиска я наконец нашел этот пример . Загрузите пример программы на странице. Это дает вам, помимо прочего, расположение мыши в окне консоли (на основе символов).

РЕДАКТИРОВАТЬ: Это мой 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;
6 голосов
/ 17 июня 2010

Кроме того, консоль предназначена не только для обработки текста. Вы можете написать довольно приличные оконные менеджеры для этого. Вы можете сделать что-нибудь с этим. Это просто сложнее.

Хотя медленнее. Я реализовал виртуальную машину в C #, используя консоль для пользовательского интерфейса. Он не печатает строки текста одну за другой; он [интерфейс] действует скорее как GUI.

Если вы хотите ввести мышь с консоли, попробуйте этот хук: http://blogs.msdn.com/b/toub/archive/2006/05/03/589468.aspx?PageIndex=2#comments

5 голосов
/ 17 июня 2010

Когда вы пишете игру без использования событий ... все, что вы действительно делаете, это реализует события самостоятельно. Это выгодно, потому что вы можете сделать его намного более эффективным, чем с помощью встроенных событий вашего языка. Игры, написанные таким образом, менее подвержены ошибкам, если вы знаете, что делаете.

Например, когда я пытался научить моего брата, как пишутся игры, я написал для него простую игру со змеями. У меня была основная петля в потоке, переместите змею и нарисуйте ее в новой позиции в цикле. Я хотел бы иметь поток, работающий в то же время, что постоянно проверяет 4 вещи:

  1. Если змея врезалась в себя (игра окончена); если игра окончена, остановите основной поток, который обновляет основное положение змеи, выведите игру на экран, дождитесь ввода с клавиатуры и перезапустите игру.

  2. Если бы змея съела яблоко; увеличьте переменную счетчика, которая сообщает, сколько яблок было съедено, и выведите это новое значение на экран, перезаписав то, что было ранее.

  3. Если змея съела количество яблок, кратных 10 (змея растет на 1 клетку, вычтите из переменной ожидания, которая говорит, сколько времени должно пройти между каждым движением змеи)

  4. Если нажата клавиша со стрелкой. Если влево, установите перемещение в 0, если вправо установите в 1, если вниз установите в 2, если в верх установите в 3. Указатель, в котором он хранится, является указателем на массив из 4 делегатов, которые заставляют змею двигаться. в правильном направлении.

Основной цикл, который обновляет положение змеи, сообщает нити, проверяющей эти 4 вещи, что делает змея. То, как я это делаю, заключается в том, что у меня есть каждая ячейка на экране, которую голова змеи перемещает, чтобы ссылаться на двумерный массив делегатов. Об этом массиве делегатов:

Игра написана в режиме консоли и использует цвета консоли. Консоль установлена ​​на 80x50 символов. Делегат выглядит следующим образом: "делегат void ptr ()"; затем я создаю массив с: "ptr [,] pos = new 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», этой кнопке был бы назначен делегат; этот делегат затем выполняется с тем, на что он указывает.

Трудно объяснить, но имеет ли это для вас смысл?

3 голосов
/ 22 декабря 2009

Что сказал @Frank Krueger. Вы действительно хотите это сделать? Windows Forms разработан, чтобы сделать это намного проще.

Если вы это сделаете, вам нужно будет использовать PInvoke в низкоуровневом Windows API. Попробуйте this в качестве отправной точки, но имейте в виду, что это значительно сложнее, чем было бы в приложении Windows Forms.

0 голосов
/ 16 апреля 2017
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;
    }

}
}
0 голосов
/ 16 апреля 2017

После долгих исследований я нашел решение.

С помощью класса Button и графического интерфейса, который я создал ниже, можно создать кнопку, щелкнув ее или мышью (она не работает идеально). И вам нужно импортировать System.Windows.Forms и System.Drawing.

...