如何在Rider控制台的C#扫雷程序中获取鼠标输入
在C#控制台程序中实现鼠标点击交互(扫雷场景)
核心逻辑
控制台默认不支持鼠标事件监听,需要通过P/Invoke调用Windows API来实现,核心是获取控制台输入事件、解析鼠标动作并转换为棋盘单元格坐标。
步骤1:引入必要的Windows API声明
需要用到几个关键系统函数和结构体,用于获取控制台句柄、修改输入模式、读取鼠标事件:
using System; using System.Runtime.InteropServices; // 控制台坐标结构体 [StructLayout(LayoutKind.Sequential)] private struct COORD { public short X; public short Y; } // 输入记录结构体,用于存储控制台输入事件 [StructLayout(LayoutKind.Explicit)] private struct INPUT_RECORD { [FieldOffset(0)] public ushort EventType; [FieldOffset(4)] public MOUSE_EVENT_RECORD MouseEvent; } // 鼠标事件记录结构体 [StructLayout(LayoutKind.Sequential)] private struct MOUSE_EVENT_RECORD { public COORD MousePosition; public uint ButtonState; public uint ControlKeyState; public uint EventFlags; } // 常量定义 private const uint MOUSE_EVENT = 0x0002; private const uint LEFT_BUTTON_PRESSED = 0x0001; private const uint RIGHT_BUTTON_PRESSED = 0x0002; private const uint ENABLE_MOUSE_INPUT = 0x0010; private const uint ENABLE_EXTENDED_FLAGS = 0x0080; private const uint STD_INPUT_HANDLE = unchecked((uint)-10); // Windows API函数导入 [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetStdHandle(uint nStdHandle); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool ReadConsoleInput(IntPtr hConsoleInput, ref INPUT_RECORD lpBuffer, uint nLength, out uint lpNumberOfEventsRead);
步骤2:实现鼠标事件监听与坐标转换
完整示例代码
class Minesweeper { // (此处粘贴上述API声明代码) static void Main(string[] args) { var consoleInputHandle = GetStdHandle(STD_INPUT_HANDLE); // 保存原始控制台模式,退出时恢复 GetConsoleMode(consoleInputHandle, out uint originalMode); // 启用鼠标输入支持 SetConsoleMode(consoleInputHandle, originalMode | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS); // 自定义棋盘参数(根据你的扫雷程序调整) int boardStartX = 2; // 棋盘在控制台的起始X坐标(字符数) int boardStartY = 2; // 棋盘在控制台的起始Y坐标(字符数) int cellWidth = 3; // 每个单元格占的字符宽度 int cellHeight = 1; // 每个单元格占的字符高度 int boardCols = 10; // 棋盘列数 int boardRows = 10; // 棋盘行数 Console.WriteLine("点击棋盘单元格操作,按ESC退出"); while (true) { INPUT_RECORD inputRecord = new INPUT_RECORD(); uint eventsRead; ReadConsoleInput(consoleInputHandle, ref inputRecord, 1, out eventsRead); // 仅处理鼠标事件 if (inputRecord.EventType != MOUSE_EVENT) continue; var mouseEvent = inputRecord.MouseEvent; // 获取鼠标在控制台的字符坐标(相对于窗口左上角) int mouseX = mouseEvent.MousePosition.X; int mouseY = mouseEvent.MousePosition.Y; // 判断点击是否在棋盘区域内 bool isInBoard = mouseX >= boardStartX && mouseX < boardStartX + boardCols * cellWidth && mouseY >= boardStartY && mouseY < boardStartY + boardRows * cellHeight; if (!isInBoard) continue; // 转换为棋盘单元格坐标(从0开始计数) int cellCol = (mouseX - boardStartX) / cellWidth; int cellRow = (mouseY - boardStartY) / cellHeight; // 区分左右键操作 if ((mouseEvent.ButtonState & LEFT_BUTTON_PRESSED) != 0) { Console.WriteLine($"左键翻开:行{cellRow},列{cellCol}"); // 此处调用你的扫雷翻开逻辑 } else if ((mouseEvent.ButtonState & RIGHT_BUTTON_PRESSED) != 0) { Console.WriteLine($"右键标记地雷:行{cellRow},列{cellCol}"); // 此处调用你的扫雷标记逻辑 } // 检测ESC键退出程序 if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape) break; } // 恢复控制台原始模式 SetConsoleMode(consoleInputHandle, originalMode); } }
关键细节说明
- 坐标转换逻辑:控制台返回的鼠标坐标是字符坐标(不是像素),需要根据你绘制棋盘时的单元格尺寸(每个单元格占多少字符宽高)计算对应单元格索引。比如示例中每个单元格占3个字符宽度,就用鼠标X减去棋盘起始X后除以3得到列数。
- 左右键判断:通过
ButtonState的位掩码判断,LEFT_BUTTON_PRESSED对应左键按下,RIGHT_BUTTON_PRESSED对应右键按下。 - 控制台模式恢复:程序退出前必须恢复原始控制台模式,避免影响后续控制台的正常输入。
- 跨平台限制:该方案仅适用于Windows系统,因为依赖Windows API;如果需要跨平台,建议改用WinForms/WPF等GUI框架实现扫雷。
内容的提问来源于stack exchange,提问作者Mathis
相关产品推荐
相关产品推荐

