|
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 对应于&#34;[新建]&#34;,L1、L2 和 L3 分别对应于&#34;[容易]&#34;、&#34;[中等]&#34;和&#34;[困难]&#34;,PROMPT、SHUFFLE 和 ESC 对应于&#34;[提示]&#34;、&#34;[混洗]&#34;和&#34;[退出]&#34;。最末尾的 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, &#34;是否放弃当前这一局,并重新开始新一局游戏?&#34;);
if (MessageBox(NULL, msg, &#34;连连看&#34;, MB_ICONQUESTION | MB_YESNO) == IDYES)
match = -1; //表示开始新的一局
}
if ((action==L1 || action==L2 || action==L3) && action - L1 != level){ //难度
char s1[3][5] = {&#34;容易&#34;, &#34;中等&#34;, &#34;困难&#34;};
sprintf(msg, &#34;是否把游戏难度设为 %s ,并重新开始新一局游戏?&#34;, s1[action-L1]);
if (MessageBox(NULL, msg, &#34;连连看&#34;, 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 小结与展望 |
|