IE盒子

搜索
查看: 78|回复: 0

用C/C++编写小游戏:12.5 鼠标仿真点击按钮

[复制链接]

5

主题

10

帖子

23

积分

新手上路

Rank: 1

积分
23
发表于 2023-1-12 06:52:19 | 显示全部楼层 |阅读模式
12.5  鼠标仿真点击按钮

上一节介绍了在程序中使用 MessageBox 函数弹出消息框,改进了连连看游戏程序的消息提示界面。下面讨论如何进一步完善游戏功能。
现有的连连看游戏程序还缺少以下实用功能:(1)设置游戏难度为初级、中等和困难三个级别;(2)在游戏中途允许用户退出当前这一局并新建一局;(3)在游戏中途允许用户对现有牌面进行混洗。
按照本书前面一些章节中的做法,可以接受用户的键盘输入,当用户按键为预先设置的字母时就执行相应的功能。这是一个合理的做法。不过,本章想要介绍一种编程技巧,用于在界面上制作一些仿真按钮,并接受用户的鼠标点击。之所以说是“仿真按钮”,是因为这种方法并不是 Windows 系统中使用 Visual C++ 或其它语言编写程序制作出的标准按钮(那需要读者学习使用那些编程语言),而是使用读者在《高级语言程序设计》课程和本书前文所述的游戏编程方法,制作出接受鼠标点击按钮的界面效果。
按照以下步骤设计制作仿真按钮。
1、首先,需要设计好需要显示在程序运行界面上并接受鼠标点击的按钮。
对于现有的连连看游戏,需要允许用户点击写有以下文字的几个仿真按钮:
[新建]  [初级]  [中等]  [困难]  [提示]  [混洗]  [退出]
与功能相对应的每个词都置于一对方括号内,显得象是一个个按钮。上面的每个仿真按钮(两个汉字加上方括号)的长度为 6,相互之间间隔 2 个空格。
2、然后考虑把这些按钮放置在游戏运行界面上的位置。仿照一般软件在窗口上方显示菜单的做法,可以把这些按钮放置在控制台窗口的顶行(行号为 0)。
但是,原先是把控制台窗口的顶行的行首写有游戏名称“连连看”,又怎么处理呢?其实,把游戏名称写在控制台窗口顶行是一个简便的做法,而标准的 Windows 软件通常是把软件名称写在窗口的标题栏。有一个名为“SetConsoleTitle”的 Windows API 函数可以设置控制台窗口的标题栏。可以删除原有的 drawBoard 函数中在首行输出游戏名称“连连看”的语句,然后在主函数中开始部分写这一条语句:
SetConsoleTitle("连连看"); //设置控制台窗口标题栏
3、设计这些按钮的显示方式。对于游戏难度,当前总是应该加亮显示其中某一个,而且当鼠标置于某个按钮上方时,也需要把该按钮加亮显示。因此可以编写如下函数用于显示这行文字:
void drawBtns(int level, int cur) { //在界面顶行绘制仿真按钮
    static HANDLE hdout = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出的句柄
    char btns[7][7] = {"[容易]", "[中等]", "[困难]", "[新建]", "[提示]", "[混洗]", "[退出]"};
    for (int k = 0; k < 7; k++) {
        SetConsoleTextAttribute(hdout, BLACK * 16 + WHITE); //黑底白字
        if (level +1 == k) //游戏难度level 值为 0, 1, 2,对应 k 值为 1, 2, 3
            SetConsoleTextAttribute(hdout, GREEN * 16 + WHITE); //绿底白字
        if (cur == k) //鼠标当前悬浮所在的按钮
            SetConsoleTextAttribute(hdout, WHITE * 16 + BLUE); //白底蓝字
        gotoxy(8 * k, 0);
        cout << btns[k];
    }
    SetConsoleTextAttribute(hdout, BLACK * 16 + WHITE); //默认黑底白字
}
4、在主函数中定义与各个按钮相对应的枚举常量。
enum {NONE, NEW, L1, L2, L3, PROMPT, SHUFFLE, ESC, CLICK}; //表示动作类型的枚举常量
请注意,上面的定义中,在 NONE 之后的各个枚举常量与顶部各个按钮依次相对应:NEW 对应于"[新建]",L1、L2 和 L3 分别对应于"[容易]"、"[中等]"和"[困难]",PROMPT、SHUFFLE 和 ESC 对应于"[提示]"、"[混洗]"和"[退出]"。最末尾的 CLICK 是表示鼠标在图板中的点击事件。
5、在主函数中每一局开始之前,需要绘制顶行所有(第2个参数为-1)仿真按钮:
drawBtns(level, -1); //绘制顶行所有仿真按钮
6、在主函数中处理鼠标移动时绘制顶行仿真按钮和处理鼠标点击事件。
action = NONE;  //赋值为无效动作
FlushConsoleInputBuffer(hdin); //清空刷新控制台输入缓冲区
ReadConsoleInput(hdin, &rcd, 1, &rcdnum); //从控制台输入缓冲区中读取数据
if (rcd.EventType == MOUSE_EVENT) {  //如果当前为鼠标事件
   pos = rcd.Event.MouseEvent.dwMousePosition;    //获得当前鼠标位置
   if (pos.Y == 0 && pos.X % 8 < 6) { //顶行仿真按钮
     showcursor(false);
     drawBtns(level, pos.X / 8); //加亮绘制顶行当前按钮
     drawn = true;  //顶行按钮已加亮绘制
   } else if (drawn) { //鼠标从顶行移走时,重绘一次顶行所有按钮
     drawBtns(level, -1);
     drawn = false; //顶行按钮未加亮绘制
     showcursor(true);
   }
   x = (pos.X - X0) / 2; //转换为图板中的相对坐标
   y = (pos.Y - Y0);
   if (x >= 1 && x <= col && y >= 1 && y <= row) //坐标在图板中则移动光标
   gotoxy(pos.X, pos.Y);

   if (rcd.Event.MouseEvent.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED) {
     if (pos.Y == 0 && pos.X % 8 < 6 ) //点击顶部按钮
       action = pos.X / 8 + 1; //按钮序号(与枚举常量相对应)
     if (x >= 1 && x <= col && y >= 1 && y <= row) //点击图板中的字符
       action = CLICK;
   }
}
在上面的代码片段中需要注意以下几点:
(1)为了防止控制台中的一个输入事件被重复读取并重复处理,在使用 ReadConsoleInput 函数从控制台输入缓冲区中读取数据之前,使用了 FlushConsoleInputBuffer 函数清空刷新控制台输入缓冲区。
(2)用表达式“(pos.Y == 0 && pos.X % 8 < 6)”判断得知鼠标悬浮于顶行某个按钮上方时,就需要加亮绘制鼠标当前悬浮所在的按钮(序号为“pos.X / 8”)。然后把标志变量 drawn 置为 true。在此之后第一次检测到鼠标离开了按钮时,就需要把所有按钮重绘一次(以消除上一次的加亮绘制效果)。
(3)对于鼠标左键单击事件,如果检测到是单击界面顶行的按钮,则用语句“action = pos.X / 8 + 1;”计算出按钮序号,并赋给变量 action。由于按钮序号与前面定义的相应的枚举常量 NEW、L1、L2、L3、PROMPT、SHUFFLE 和 ESC 是依次对应的,所以这样就实现了按钮与相应功能的对应。
(4)当检测到鼠标左键单击了图板中的字符时,给变量 action 赋值为枚举常量 CLICK。
(5)由于已经用鼠标左键单击顶行按钮实现了多个功能,所以就不再处理鼠标右键单击和鼠标双击事件了。
7、在主程序中编写代码依次处理各个动作。这是比较容易的,具体如下所示:
if (action == NEW) {  //新建游戏
   strcpy(msg, "是否放弃当前这一局,并重新开始新一局游戏?");
   if (MessageBox(NULL, msg, "连连看", MB_ICONQUESTION | MB_YESNO) == IDYES)
     match = -1; //表示开始新的一局
}

if ((action==L1 || action==L2 || action==L3) && action - L1 != level){ //难度
   char s1[3][5] =  {"容易", "中等", "困难"};
   sprintf(msg, "是否把游戏难度设为 %s ,并重新开始新一局游戏?", s1[action-L1]);
   if (MessageBox(NULL, msg, "连连看", MB_ICONQUESTION | MB_YESNO)== IDYES) {
     level = action - L1;
     match = -1; //表示开始新的一局
   }
}

if (action == SHUFFLE) {  //混洗
   int ix, iy, jx, jy, tmp;
   for (iy = 1; iy <= row; iy++)
     for (ix = 1; ix <= col; ix++) {
       if (brd[iy][ix] == 0)
         continue;
       do {
         jx = rand() % col + 1;
         jy = rand() % row + 1;
       } while (brd[jy][jx] == 0);
       tmp = brd[iy][ix];
       brd[iy][ix] = brd[jy][jx];
       brd[jy][jx] = tmp;
     }
   drawBoard();
}
以上介绍了在连连看游戏中使用 Windows 消息框和鼠标仿真按钮,这是一个完全支持鼠标操作的在控制台窗口运行的字符界面程序。完整的源程序文件名为 cgameC(links)v2.cpp。读者可以从本文作者的 Gitee 开源程序库中(https://gitee.com/devcpp/cgames)下载它。
该程序运行界面如下图所示:



图12-2  支持鼠标操作的控制台字符型连连看游戏

继续阅读:12.6 小结与展望
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表