VC绘图/游戏简易教程--11:鼠标控制及高级按键控制
教程总目录:https://codebus.cn/bestans/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
学习本节课前,需要熟练掌握第 7 节。详见:https://codebus.cn/bestans/concise-lesson-7
==本节课开始==
首先,获取鼠标消息:
ExMessage m;
m = getmessage();
ExMessage 是 EasyX 定义的一个表示消息的结构体类型,以上代码表示用该类型声明了一个变量 m,然后通过 getmessage 函数获取消息,并返回给变量 m。根据 m 的内容,进一步分析获取到的是什么消息。鼠标消息可以通过以下成员获取鼠标消息中的信息:
USHORT message; // 当前消息
bool ctrl; // Ctrl 键是否按下
bool shift; // Shift 键是否按下
bool lbutton; // 鼠标左键是否按下
bool mbutton; // 鼠标中键是否按下
bool rbutton; // 鼠标右键是否按下
int x; // 当前鼠标 x 坐标
int y; // 当前鼠标 y 坐标
int wheel; // 鼠标滚轮滚动值
其中,“当前消息”可以是以下值:
- WM_MOUSEMOVE 鼠标移动消息
- WM_MOUSEWHEEL 鼠标滚轮拨动消息
- WM_LBUTTONDOWN 左键按下消息
- WM_LBUTTONUP 左键弹起消息
- WM_LBUTTONDBLCLK 左键双击消息
- WM_MBUTTONDOWN 中键按下消息
- WM_MBUTTONUP 中键弹起消息
- WM_MBUTTONDBLCLK 中键双击消息
- WM_RBUTTONDOWN 右键按下消息
- WM_RBUTTONUP 右键弹起消息
- WM_RBUTTONDBLCLK 右键双击消息
例如,判断获取的消息是否是鼠标左键按下,可以用:
if (m.message == WM_LBUTTONDOWN)
...
下面举一个综合的例子(我偷点懒,直接粘贴的绘图库帮助里面的鼠标范例),该程序会用红色的点标出鼠标移动的轨迹,按左键画一个小方块,按Ctrl+左键画一个大方块,按右键退出:
// 程序名称:鼠标操作演示
// 编译环境:Visual C++ 6.0~2019,EasyX_20190730
//
#include <graphics.h>
int main()
{
// 初始化图形窗口
initgraph(640, 480);
ExMessage m; // 定义消息变量
while(true)
{
// 获取一条鼠标或按键消息
m = getmessage(EM_MOUSE | EM_KEY);
switch(m.message)
{
case WM_MOUSEMOVE:
// 鼠标移动的时候画红色的小点
putpixel(m.x, m.y, RED);
break;
case WM_LBUTTONDOWN:
// 如果点左键的同时按下了 Ctrl 键
if (m.ctrl)
// 画一个大方块
rectangle(m.x - 10, m.y - 10, m.x + 10, m.y + 10);
else
// 画一个小方块
rectangle(m.x - 5, m.y - 5, m.x + 5, m.y + 5);
break;
case WM_KEYDOWN:
if (m.vkcode == VK_ESCAPE)
return 0; // 按 ESC 键退出程序
}
}
// 关闭图形窗口
closegraph();
return 0;
}
一些同学可能注意到了,以上例子中,消息还可以是 WM_KEYDOWN,这就是按键处理。
getmessage 获取到的消息包括四类:鼠标消息、按键消息、字符消息、窗口消息,每类消息都有不同的成员信息。getmessage 默认将会收到四类消息,或者,可以通过参数筛选出需要获取的消息。例如以上代码,getmessage(EM_MOUSE | EM_KEY) 表示获取鼠标消息和按键消息,另外还可以通过 EM_CHAR 获取字符消息,通过 EM_WINDOW 获取窗口消息。
当 message 值为 WM_KEYDOWN 或 WM_KEYUP 时,表示收到了按键消息,成员信息如下:
BYTE vkcode; // 按键的虚拟键码
BYTE scancode; // 按键的扫描码(依赖于 OEM)
bool extended :1; // 按键是否是扩展键
bool prevdown :1; // 按键的前一个状态是否按下
这样,就可以精确地处理当前的按键消息。
当 message 值为 WM_CHAR 时,表示收到了字符消息,成员信息如下:
TCHAR ch; 收到的字符
窗口消息类似,具体可以参考:https://docs.easyx.cn/exmessage
用 getmessage 获取消息是“阻塞”的,在获取到消息之前,程序不会往下执行。peekmessage 获取消息是非阻塞的,不管是否存在消息,peekmessage 都会立刻返回。以下是 peekmessage 的一个例子:
// 程序名称:鼠标跟随效果
// 编译环境:Visual C++ 6.0~2019,EasyX_20190730
//
#include <graphics.h>
int main()
{
// 初始化图形窗口
initgraph(640, 480);
int x[10] = {0}, y[10] = {0};
ExMessage m; // 定义消息变量
while(true)
{
// 获取鼠标消息
// 注意,这里用了 while 而不是 if,因为可能鼠标消息的产生速率会
// 超过 Sleep(20),如果用 if,会造成鼠标消息堆积产生延时效果。
while (peekmessage(&m, EM_MOUSE))
{
if(m.message == WM_MOUSEMOVE)
{
putpixel(m.x, m.y, BLUE);
}
}
// 计算跟随点的坐标(同时擦掉最末尾一个点)
setlinecolor(BLACK);
circle(x[9], y[9], 5);
for(int i = 9; i > 0; i--)
{
x[i] = x[i-1];
y[i] = y[i-1];
}
// 计算头结点坐标,向着鼠标当前位置移动
x[0] += (m.x - x[0]) / 4;
y[0] += (m.y - y[0]) / 4;
setlinecolor(RED);
circle(x[0], y[0], 5);
Sleep(20);
}
// 关闭图形窗口
closegraph();
return 0;
}
[本节作业]
画一个填充的三角形,要用鼠标点选三角形的三个顶点。提示:可以用 fillpolygon 函数画多边形。
写一个“格子涂色”的游戏,要求:屏幕上有16x8的格子,屏幕底部有类似画笔中的选色区(随便放上一些常用的颜色),鼠标点击选择区的颜色后,就作为当前颜色,然后再点屏幕上的格子,就可以用刚才的颜色填涂相应格子。
#include <graphics.h>
#include <conio.h>
int main()
{
// 初始化图形窗口
initgraph(640, 480);
int i=10, b=150;
setlinestyle(PS_SOLID, 10);
setlinecolor(WHITE);
setfillcolor(LIGHTRED);
fillrectangle(10,10,149,149);
setfillcolor(LIGHTGREEN);
fillrectangle(10+i+b, 10, 149 + i + b, 149);
setfillcolor(LIGHTBLUE);
fillrectangle(10 + (i + b)*2, 10, 149+(i + b) * 2, 149);
setfillcolor(YELLOW);
fillrectangle(10 + (i + b) * 3, 10, 149+(i + b) * 3, 149);
setlinecolor(WHITE);
setlinestyle(PS_SOLID, 3);
for (int m = 0; m < 641; m += 40) {
line(m, 159, m, 479);
}
for (int n = 159; n < 480; n+=40) {
line(0, n, 639, n);
}
ExMessage m; // 定义鼠标消息
setfillcolor(BLACK);
while (true)
{
// 获取一条鼠标消息
m = getmessage();
int tempx,tempy;
if (m.message == WM_LBUTTONDOWN)
{
if (m.y < 158)
{
for (int i = 160; i <= 640; i += 160)
{
if (m.x < i)
{
tempx = i;
break;
}
}
switch (tempx / 160)
{
case 1:
setfillcolor(LIGHTRED);
break;
case 2:
setfillcolor(LIGHTGREEN);
break;
case 3:
setfillcolor(LIGHTBLUE);
break;
case 4:
setfillcolor(YELLOW);
break;
}
}
else
{
for (int i = 0; i <= 640; i += 40)
{
if (m.x < i)
{
tempx = i;
break;
}
}
for (int i = 160; i <= 480; i += 40)
{
if (m.y < i) {
tempy = i;
break;
}
}
fillrectangle(tempx-40, tempy-40, tempx, tempy);
}
}
else if(m.message == WM_RBUTTONUP)// 按鼠标右键退出程序
return 0;
}
// 关闭图形窗口
_getch();
closegraph();
return 0;
}
并参考 EasyX 文档 https://docs.easyx.cn/zh-cn/exmessage
#include <graphics.h>
#include <conio.h>
int main()
{
// 初始化图形窗口
initgraph(640, 480);
ExMessage m; // 定义鼠标消息
while (true)
{
// 获取一条鼠标消息
m = getmessage();
switch (m.message)
{
case WM_MOUSEMOVE: // 鼠标移动的时候画红色的小点
putpixel(m.x, m.y, RED);
break;
case WM_LBUTTONDOWN: // 如果点左键的同时按下了 Ctrl 键
if (m.ctrl) // 画一个大方块
rectangle(m.x - 10, m.y - 10, m.x + 10, m.y + 10);
else // 画一个小方块
rectangle(m.x - 5, m.y - 5, m.x + 5, m.y + 5);
break;
case WM_RBUTTONUP:
return 0; // 按鼠标右键退出程序
}
}
// 关闭图形窗口
closegraph();
return 0;
}
如果要用老版本函数,解决办法:https://codebus.cn/yangw/c4996
并参考 EasyX 文档 https://docs.easyx.cn/zh-cn/exmessage
#include <graphics.h>
#include <conio.h>
int main()
{
// 初始化图形窗口
initgraph(640, 480);
ExMessage m; // 定义鼠标消息
while (true)
{
// 获取一条鼠标消息
m = getmessage();
switch (m.message)
{
case WM_MOUSEMOVE: // 鼠标移动的时候画红色的小点
putpixel(m.x, m.y, RED);
break;
case WM_LBUTTONDOWN: // 如果点左键的同时按下了 Ctrl 键
if (m.ctrl) // 画一个大方块
rectangle(m.x - 10, m.y - 10, m.x + 10, m.y + 10);
else // 画一个小方块
rectangle(m.x - 5, m.y - 5, m.x + 5, m.y + 5);
break;
case WM_RBUTTONUP:
return 0; // 按鼠标右键退出程序
}
}
// 关闭图形窗口
closegraph();
return 0;
}
// https://easyx.cn
//
#include <graphics.h>
int main()
{
// 初始化图形窗口
initgraph(640, 480);
ExMessage m; // 定义消息变量
while(true)
{
// 获取一条鼠标或按键消息
m = getmessage(EM_MOUSE | EM_KEY);
switch(m.message)
{
case WM_MOUSEMOVE:
// 鼠标移动的时候画红色的小点
putpixel(m.x, m.y, RED);
break;
case WM_LBUTTONDOWN:
// 如果点左键的同时按下了 Ctrl 键
if (m.ctrl)
// 画一个大方块
rectangle(m.x - 10, m.y - 10, m.x + 10, m.y + 10);
else
// 画一个小方块
rectangle(m.x - 5, m.y - 5, m.x + 5, m.y + 5);
break;
case WM_KEYDOWN:
if (m.vkcode == VK_ESCAPE)
return 0; // 按 ESC 键退出程序
}
}
// 关闭图形窗口
closegraph();
return 0;
}