You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 13:35:23