我有一个我在C#控制台应用程序中工作的游戏,纯粹作为练习,然后继续使用更好的方法。与使用内置按钮功能的Windows窗体应用程序相比,我正在努力抓住光标位置(我知道该怎么做)并将其与控制台应用程序内部的一些区域进行比较,如下所示:也许像素的位置,但我也不知道是否有某种内置的空间单位而不是像素(这最后一位是我无法想象的部分)。
P.S。我知道这是一般性的,没有代码已经提供,但我不觉得它是需要的,因为我要求的是如何在控制台应用程序中获取XY坐标的简要说明,并将它们粘贴在int变量中
非常感谢提前! :d
答案 0 :(得分:10)
经过很长一段时间的搜索,我终于找到了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;
答案 1 :(得分:6)
此外,控制台不仅适用于文本处理。你可以为它编写相当不错的窗口管理器。你可以用它做任何事情。这更难。
但是,它的速度较慢。 我使用用户界面的控制台在C#中实现了一个虚拟机。它不会一个接一个地打印文本行;它[界面]的行为就像一个GUI。如果您想在控制台上输入鼠标,请尝试此钩子: http://blogs.msdn.com/b/toub/archive/2006/05/03/589468.aspx?PageIndex=2#comments
答案 2 :(得分:5)
当你在不使用事件的情况下编写游戏时...你真正在做的就是自己实现事件。这是有利的,因为您可以比使用语言的内置事件更有效。如果您知道自己在做什么,以这种方式编写的游戏不易出错。
例如,当我试图教我的兄弟如何写游戏时,我为他写了一个简单的蛇游戏。我在一个线程中有一个主循环,移动蛇并在一个循环中将它绘制在新的位置。我会在同一时间运行一个线程,不断检查4件事:
如果蛇撞到了自己(比赛结束);如果游戏结束,停止更新蛇主位置的主线程,将游戏打印到屏幕上,等待键输入,然后重新开始游戏。
如果蛇吃了一个苹果;增加计数器变量,说明已经吃了多少苹果,然后在屏幕上打印这个新值,覆盖以前的那些。
如果蛇吞下了一些被10整除的苹果(蛇长了1个细胞,减去一个等待变量,说明蛇在每次运动之间应该经过多长时间)
如果按下了箭头键。如果向左,则将move设置为0,如果右边设置移动到1,如果向下设置移动到2,如果向上设置移动到3.存储的int是指向4个委托的数组的指针,使蛇移动朝着正确的方向前进。
更新蛇位置的主循环会告诉线程检查这4条蛇正在做什么。我这样做的方法是让屏幕上的每个单元格都会让蛇的头移动来指代一个二维的代表阵列。关于这一组代表:
游戏以控制台模式编写,并使用控制台颜色。控制台设置为80x50个字符。代表如下:“delegate 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 :(得分:3)
@Frank Krueger说道。你真的想这么做吗? Windows窗体旨在使更多更容易。
如果这样做,则需要将PInvoke用于低级Windows API。尝试使用this作为起点 - 但请注意,这比Windows窗体应用程序要复杂得多。
答案 4 :(得分:0)
经过多次研究,我找到了解决方案。
使用我在下面创建的Button类和GUI,可以创建一个按钮,然后单击或鼠标(它不能完美地工作)。
您需要导入System.Windows.Forms
和System.Drawing
。
答案 5 :(得分:0)
我知道这是很久以前的事了,但是在控制台中获取鼠标位置并不是特别困难。我只会向您展示我使用的代码,以防其他人想要答案:
except:
raise Exception(str(e) + ' not found') from None
享受吧!
答案 6 :(得分:-1)
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;
}
}
}