//设立标志位
bool flag;
for (; is_run(); delay_fps(60)) {
//创建鼠标消息结构体变量,并初始化
mouse_msg msg = { 0 };
//标志位状态重置
flag = false;
while (mousemsg()) {
msg = getmouse();
//对消息进行检测,只进行简单操作
if (鼠标进行了操作)
flag = true;
}
//标志位判断并做处理
if (flag) {
//做绘图等操作
}
}
鼠标正常放着时,是不会产生消息的。能让鼠标产生消息的,有以下几种:
这几种鼠标消息,在EGE中并不区分开来发送,都是保存到一个鼠标消息结构体里,通过一些标志位来区分不同的消息,并且包含产生消息时鼠标的状态, 比如鼠标位置,鼠标左右键、中键有没有按下,滚轮是否滑动,向哪边滑动等。
鼠标消息结构体定义如下
typedef struct mouse_msg {
int x;
int y;
mouse_msg_e msg;
unsigned int flags;
int wheel;
//成员函数
bool is_left() { return (flags & mouse_flag_left) != 0; }
bool is_right() { return (flags & mouse_flag_right) != 0; }
bool is_mid() { return (flags & mouse_flag_mid) != 0; }
bool is_down() { return msg == mouse_msg_down; }
bool is_up() { return msg == mouse_msg_up; }
bool is_move() { return msg == mouse_msg_move; }
bool is_wheel() { return msg == mouse_msg_wheel; }
}mouse_msg;
参数说明:
mouse_msg_down, 鼠标按键按下消息
mouse_msg_up, 鼠标按键松开消息
mouse_msg_move,鼠标移动消息
mouse_msg_wheel, 鼠标滚轮消息
不建议直接访问,EGE已经有相应的函数来判断鼠标消息了
typedef enum mouse_flag_e {
mouse_flag_left = 1,
mouse_flag_right = 2,
mouse_flag_mid = 4,
mouse_flag_shift = 0x100,
mouse_flag_ctrl = 0x200,
}mouse_flag_e;
不建议直接访问,EGE已经有相应的函数来判断鼠标消息了
鼠标消息结构体中的一些成员函数(C++部分)
用来判断鼠标消息类型和标志位的
下面几个是判断是鼠标哪个按键产生的消息(通过标志位判断)。
bool is_left() { return (flags & mouse_flag_left) != 0; }
bool is_right() { return (flags & mouse_flag_right) != 0; }
bool is_mid() { return (flags & mouse_flag_mid) != 0; }
下面几个是用来判断鼠标的操作的,是按键按下,还是松开、移动、滚轮滚动
bool is_down() { return msg == mouse_msg_down; }
bool is_up() { return msg == mouse_msg_up; }
bool is_move() { return msg == mouse_msg_move; }
bool is_wheel() { return msg == mouse_msg_wheel; }
比如
是否是鼠标左键按下的消息
if (mouseMsg.is_left() && mouseMsg.is_down())
mouse_msg msg;
获取鼠标消息之前,一定要先对mouse_msg 初始化, 否则可能会出现判断错误
mouse_msg msg;
for ( ; is_run(); delay_fps(60)) {
//初始化
msg = mouse_msg();
//鼠标消息的处理
}
当然,将mouse_msg 结构体变量直接定义在帧循环中, 并给出初始化值,每次进入循环,mouse_msg 结构体变量都将会被初始化,这也是可以的
for ( ; is_run(); delay_fps(60)) {
//每次循环都重新创建并初始化
mouse_msg msg = {0};
//鼠标消息的处理
}
这里有两种处理方式,一种是 使用 if()
//如果有鼠标消息
if (mousemsg()) {
//读取鼠标消息
msg = getmouse();
}
另一种是使用 while(), 我们应该使用这种
//循环检测是否有鼠标消息
while (mousemsg()) {
//有则读取
msg = getmouse();
}
所以鼠标消息处理写起来就是
for ( ; is_run(); delay_fps(60)) {
//每次循环都重新创建并初始化
mouse_msg msg = {0};
//鼠标消息的处理
while (mousemsg()) {
//有则读取
msg = getmouse();
}
//使用鼠标消息
}
上面经过 msg = getmouse() ,已经获得了鼠标消息,下面可以使用这个结构体来获取信息。可以查看上面鼠标消息结构体的定义
是否是鼠标左键按下的消息
if (msg.is_left() && msg.is_down())
鼠标点击可以查看后面鼠标消息的判断
因为鼠标移动时,每一秒大约会产生 150条 左右的鼠标移动消息。
所以一般我们用 while() 的写法
下面来运行个示例,来看看两种写法的区别:
把下面一个处理鼠标消息的while , 改成 if ,看看鼠标消息处理滞后的情况
还有个 cleardevice() ,把这一行注释掉试试,看看清屏的作用
这是一个红色小圆跟随鼠标移动的示例,读取鼠标移动消息,获取鼠标所在位置,然后绘制。
#include
int main()
{
setinitmode(INIT_RENDERMANUAL, 100, 100); //设置为手动模式, 设置窗口位置
initgraph(640, 640); //初始化窗口
setcaption("EGE鼠标消息处理"); //设置窗口标题
setbkcolor(WHITE); //设置窗口背景为白色
setcolor(RED); //设置前景色为红色
setfillcolor(RED); //设置填充色为红色
for (; is_run(); delay_fps(60)) {
//定义个鼠标结构体变量, 并初始化
mouse_msg msg = { 0 };
//改下面这个 while, 改成 if 试试
//获取鼠标消息
while (mousemsg()) {
msg = getmouse();
}
//鼠标移动则绘画
if (msg.is_move()) {
//清屏, 可以注释掉试试
cleardevice();
//画红色小圆
fillellipse(msg.x, msg.y, 30, 30);
}
}
closegraph();
return 0;
}
鼠标移动时会产生大量的消息,所以在每一帧的鼠标消息处理循环中,如果鼠标正在移动,那么就会处理到多条鼠标消息,只保留最后一条。所以如果你的鼠标在点击时移动,点击的消息很有可能被移动的消息覆盖掉。
所以应该使用状态位,在while循环中,对每一条消息进行判断,设置标志位。处理完成后,再通过标志位来判断鼠标的事件。
后面会有消息判断的示例。
if (msg.is_left() && msg.is_down()) {
//鼠标左键处于按下状态
}
if (msg.is_left() && msg.is_up()) {
//鼠标左键处于按下状态
}
下面这个鼠标点击判断,对鼠标消息进行处理后,在外面进行判断。要知道,鼠标移动时会产生大量的消息,所以在每一帧的鼠标消息处理循环中,如果鼠标正在移动,那么就会处理到多条鼠标消息,只保留最后一条。所以你的鼠标在点击时稍微移动,点击的消息很有可能被移动的消息覆盖掉,就判断不出来。
点击时移动试试,看看能不能正确判断点击
#include
int main()
{
initgraph(640, 480, INIT_RENDERMANUAL); //初始化窗口
setcaption("EGE鼠标点击错误示例"); //设置窗口标题
setbkcolor(WHITE); //设置窗口背景为白色
setcolor(BLACK); //设置前景色为黑色
setfillcolor(EGERGB(0xff, 0x90, 0xff));
setfont(20, 0, "楷体");
setfontbkcolor(WHITE);
int clickCount = 0;
for (; is_run(); delay_fps(60)) {
//定义个鼠标结构体变量
mouse_msg msg = { 0 };
//获取鼠标消息
while (mousemsg()) {
msg = getmouse();
}
if (msg.is_left() && msg.is_down()) {
clickCount++;
fillellipse(msg.x, msg.y, 40, 40);
}
xyprintf(20, 20, "点击次数:%d", clickCount);
}
closegraph();
return 0;
}
应该设立一个点击标志位,在鼠标消息处理循环检测点击事件,有点击就把标志位设为true。然后再在外面通过标志位检测。但是每次鼠标消息循环之前,都要把标志位设置 false
#include
int main()
{
initgraph(640, 480, INIT_RENDERMANUAL); //初始化窗口
setcaption("EGE鼠标点击正确示例"); //设置窗口标题
setbkcolor(WHITE); //设置窗口背景为白色
setcolor(BLACK); //设置前景色为黑色
setfillcolor(EGERGB(0xff, 0x90, 0xff));
setfont(20, 0, "楷体");
setfontbkcolor(WHITE);
int clickCount = 0;
bool isClick;
int xClick, yClick;
for (; is_run(); delay_fps(60)) {
//标志位设为false, 这是必要的步骤
isClick = false;
//获取鼠标消息
while (mousemsg()) {
mouse_msg msg = getmouse();
//在消息处理循环中判断点击
if (msg.is_left() && msg.is_down()) {
isClick = true;
xClick = msg.x;
yClick = msg.y;
}
}
//在这里通过标志位来判断
if (isClick) {
clickCount++;
fillellipse(xClick, yClick, 40, 40);
}
xyprintf(20, 20, "点击次数:%d", clickCount);
}
closegraph();
return 0;
}
或者使用 mousepos() 获取鼠标实时位置
int x, y;
mousepos(&x, &y);
现在x, y就是鼠标所在的位置了
可以使用 keystate() 来获取
鼠标左中右三个键的键码如下:
key_mouse_l = 0x01, //左键
key_mouse_r = 0x02, //右键
key_mouse_m = 0x04, //中键
检测鼠标左键是否是按下状态
if (keystate(key_mouse_l)) {
}
if (msg.is_wheel())
//先判断是否是滚轮滚动的消息
if (msg.is_wheel()) {
if (msg.wheel > 0) {
//滚轮前滚
}
else {
//滚轮后滚
}
}
if (msg.is_move()) {
//鼠标产生了移动消息
//可以通过 msg.x, msg.y获取鼠标移动消息产生时的位置
}
shift键 和 ctrl键
这两个键盘上的辅助键的标志位 只在鼠标移动或者滚轮滚动有效
如果鼠标在移动或滚轮滚动的同时有按住这两个辅助键,标志位就有效。
if (msg.flags == mouse_flag_shift) {
//移动或滚轮滚动的同时按下了 shift 键
}
if (msg.flags == mouse_flag_ctrl) {
//移动或滚轮滚动的同时按下了 ctrl 键
}
下面做个鼠标消息判断的示例:
请注意这里的结构体变量没有进行初始化
#include
int main()
{
setinitmode(INIT_RENDERMANUAL, 100, 100); //设置为普通窗口, 设置窗口位置
initgraph(640, 480); //初始化窗口
setcaption("EGE鼠标消息获取"); //设置窗口标题
setbkcolor(WHITE);
setcolor(BLACK);
setfont(18, 0, "黑体");
mouse_msg msg = { 0 };
for (; is_run(); delay_fps(60))
{
//获取鼠标消息,这个函数会等待,等待到有消息为止
//类似地,有和kbhit功能相近的函数MouseHit,用于检测有没有鼠标消息
while (mousemsg())
{
msg = getmouse();
}
//格式化输出为字符串,用于后面输出
//msg和flag常数请参考文档或者mouse_msg_e, mouse_flag_e的声明
cleardevice();
xyprintf(0, 20, "鼠标消息位置: x = %4d y = %4d",
msg.x, msg.y);
xyprintf(0, 40, "消息类型:move = %d down = %d up = %d",
(int)msg.is_move(),
(int)msg.is_down(),
(int)msg.is_up());
xyprintf(0, 60, "按键: left = %d mid = %d right = %d",
(int)msg.is_left(),
(int)msg.is_mid(),
(int)msg.is_right());
xyprintf(0, 80, "滚轮: wheel = %d wheel rotate = %d",
(int)msg.is_wheel(),
msg.wheel);
xyprintf(0, 100, "辅助键: shift = %d ctrl = %d",
msg.flags == mouse_flag_shift,
msg.flags == mouse_flag_ctrl);
}
getch();
closegraph();
return 0;
}
获取鼠标的当前位置,可以用 mousepos(), 这个不依赖于鼠标消息,不产生鼠标消息也能获取鼠标当前位置。
int mousepos(int *x, int *y);
传入的是x, 和y的地址,调用后,会修改x和y,x和y就保存了鼠标的位置。
使用示例:
int x, y;
mousepos(&x, &y);
但是如果鼠标位置是用来作点击判断,最好还是用鼠标点击时的 msg.x, msg.y, 因为如果点击后快速移动鼠标,但帧率较小时,鼠标位置可能和点击位置相差极大。
请注意上面我们把结构体的变量定义在了哪个位置??
mouse_msg msg = { 0 };
for (; is_run(); delay_fps(60)){
while (mousemsg()) {
msg = getmouse();
}
}
这样我们每次循环,鼠标消息结构体还是保留上次的状态,所以如果没有鼠标消息,那么下一次检测的就会是之前的消息, 所以这样会有什么问题??
这涉及到鼠标消息初始化的问题, 下面我们做个区别的示例
#include
int main()
{
initgraph(300, 300);
setbkcolor(WHITE);
setcolor(0xFF0000);
int leftClickCount = 0;
mouse_msg msg = { 0 };
for (; is_run(); delay_fps(60)) {
//初始化
//msg = mouse_msg();
while (mousemsg()) {
msg = getmouse();
}
if (msg.is_left() && msg.is_down())
leftClickCount++;
xyprintf(20, 50, "鼠标左键点击次数:%3d", leftClickCount);
}
getch();
closegraph();
return 0;
}
这个小示例中,左键点击一次, 计数就会增很多,如果你按住不放,就会一直增.因为上面鼠标左键点击的次数是根据消息是否是左键而且是按下的消息来计算的。按住不放后,只产生一条按下的消息,每次循环,检测的msg保存的依然是之前的消息。所以就会一直计数。然后做松开或者移动等其它动作时,消息改变,就停止计数。
所以做状态检测时,需要保证每次循环开始,对要检测的状态进行初始化。
那我们可以放在while循环中,没有鼠标消息就不会执行检测。这样能保证即使我们没有给mouse_msg变量初始化,但我们检测的仍是最新的消息。
while (mousemsg()) {
msg = getmouse();
if (msg.is_left() && msg.is_down())
leftClickCount++;
}
再来运行一下试试
#include
int main()
{
initgraph(300, 300);
setbkcolor(WHITE);
setcolor(0xFF0000);
int leftClickCount = 0;
mouse_msg msg = { 0 };
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
msg = getmouse();
//鼠标左键点击计数
if (msg.is_left() && msg.is_down())
leftClickCount++;
}
xyprintf(20, 50, "鼠标左键点击次数:%3d", leftClickCount);
}
getch();
closegraph();
return 0;
}
所以我们 可以把mouse_msg 变量定义在帧绘制循环中 ,这样 每次进行鼠标消息处理之前都会被初始化 那么没有鼠标消息的时候,因为已经被初始化了,所以判断毫无问题。
#include
int main()
{
initgraph(300, 300);
setbkcolor(WHITE);
setcolor(0xFF0000);
int leftClickCount = 0;
for (; is_run(); delay_fps(60)) {
//放在了帧绘制循环中
mouse_msg msg = { 0 };
while (mousemsg()) {
msg = getmouse();
}
//鼠标左键点击计数
if (msg.is_left() && msg.is_down())
leftClickCount++;
xyprintf(20, 50, "鼠标左键点击次数:%3d", leftClickCount);
}
getch();
closegraph();
return 0;
}
可以看到,鼠标不动时点击是毫无问题的,如果鼠标边移动边点击,那么很大可能是不会计数的
为什么?因为while()循环只保留最后一次的消息,你把检测消息放在了while()循环外,而鼠标移动时是会发出大量的消息的,那么很有可能会忽略掉鼠标移动的消息
所以正确的应该是,在帧绘制循环的开始处放个点击的标志位,自动会进行一次初始化,你定义在帧循环之外也行,重要的是每次进入循环都要进行一次状态重置,.然后在while()循环中对每一条鼠标消息进行判断。如果是点击的鼠标消息,设置点击的标志有效,这样鼠标无论怎么移动,都是计数正确的
鼠标点击计数的正确示例如下:
int main()
{
initgraph(300, 300);
setbkcolor(WHITE);
setcolor(0xFF0000);
int leftClickCount = 0;
mouse_msg msg = { 0 };
for (; is_run(); delay_fps(60)) {
//对状态位进行初始化(变量定在循环体内,每次进入循环都会初始化)
bool isLeftClick = false;
while (mousemsg()) {
msg = getmouse();
//鼠标左键点击判断
if (msg.is_left() && msg.is_down()) {
//改变状态位
isLeftClick = true;
}
}
//对状态位进行检测
if (isLeftClick) {
//鼠标点击处理(绘图等等,这里只进行简单的计数)
leftClickCount++;
}
xyprintf(20, 50, "鼠标左键点击次数:%3d", leftClickCount);
}
getch();
closegraph();
return 0;
}
while() 循环获取鼠标消息,循环退出后,会只保留最后一条鼠标消息,其它鼠标消息丢失。如果你想要检测每一条鼠标消息,请放在while()中。
判断哪些状态位需要每次进入帧绘制循环都要进行初始化,哪些不用
检查鼠标是否点击,应该放在while()中检测。
mouse_msg msg = { 0 };
//设立标志位
bool flag;
for (; is_run(); delay_fps(60)) {
//标志位状态重置
flag = false;
while (mousemsg()) {
msg = getmouse();
//对消息进行检测,只进行简单操作
if (鼠标进行了操作)
flag = true;
}
//标志位判断并做处理
if (flag) {
//做绘图等操作
}
}
mouse_msg msg = { 0 };
bool leftDown = false;
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
msg = getmouse();
//放在while循环中
if (msg.is_left()) {
if (msg.is_down())
leftDown = true;
else if (msg.is_up())
leftDown = false;
}
}
if (leftDown) {
如果左键是按下状态
}
}
mouse_msg msg = { 0 };
//0, 1, 2分别表示鼠标左中右键
bool down[3] = { false };
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
msg = getmouse();
//按键状态监测
bool key[3] = { msg.is_left(), msg.is_mid(), msg.is_right() };
for (int i = 0; i < 3; i++) {
//判断来自哪个键
if (key[i]) {
//状态检测
if (msg.is_down())
down[i] = true;
else if (msg.is_up())
down[i] = false;
break;
}
}
}
if (down[0]) {
//如果左键是按下状态;
}
}
判断鼠标按键点击的代码
#include
int main()
{
initgraph(300, 300);
setbkcolor(WHITE);
setcolor(0xFF0000);
mouse_msg msg = { 0 };
const char* keyname[3] = { "左键", "中键","右键" };
//0, 1, 2分别表示鼠标左中右键
bool click[3] = { false };
int clickCount[3] = { 0 };
for (; is_run(); delay_fps(60)) {
for (int i = 0; i < 3; i++)
click[i] = false;
//鼠标消息检测
while (mousemsg()) {
msg = getmouse();
//按键状态监测
bool key[3] = { msg.is_left(), msg.is_mid(), msg.is_right() };
for (int i = 0; i < 3; i++) {
//判断来自哪个键
if (key[i]) {
//状态检测
if (msg.is_down())
click[i] = true;
//知道是来自一个键后,其它键就不用判断了
break;
}
}
}
for (int i = 0; i < 3; i++) {
if (click[i])
clickCount[i]++;
xyprintf(20, i * 30, "鼠标%s点击数:%3d", keyname[i], clickCount[i]);
}
}
getch();
closegraph();
return 0;
}
为了能更好地进行鼠标处理,我为此定义一个鼠标状态类 ,用以保存鼠标的按键信息,判断现在鼠标按键是按下还是松开状态。还可以检测鼠标单击,双击。因为鼠标的位置已经有 mousepos() 函数可以实时获取位置了,滚轮只在触发时有效,所以不用保存。
鼠标状态类可以直接用,保存在工程中,以后可以用来进行鼠标消息处理
需要进行一下两个步骤:
#pragma once
#ifndef MOUSE_STATE_H_
#define MOUSE_STATE_H_
//鼠标按键状态
enum mouse_key_state {
MOUSE_UP = 0, //按键松开
MOUSE_DOWN = 1, //按键按下
};
//鼠标状态类
//可以保存鼠标按键状态,获取单击,双击事件
//需要在每一帧处理鼠标消息事件之前调用 resetState(), 进行按键状态重置
//需要对每一个鼠标消息使用 handleMouseMsg(mousemsg) 进行处理
class MouseState
{
private:
//鼠标按键的状态(MOUSE_DOWN 或 MOUSE_UP)
mouse_key_state curMouseState[3];
int xClick[3], yClick[3];
bool clickEvent[3]; //鼠标点击事件
bool releaseEvent[3]; //鼠标释放事件
bool isPressMsg[3]; //是鼠标点击消息
bool isReleaseMsg[3]; //是鼠标释放消息
//鼠标双击(0, 1, 2分别是左中右)
bool doubleClickEvent[3]; //鼠标双击事件
double interval; //双击触发的两次点击最大时间间隔
double clickTimes[3]; //记录点击的时间,用于判断双击
public:
MouseState();
//设置双击触发的两次点击最大时间间隔(单位:秒)
void setIntervalTime(double time_seconds);
//获取双击时间间隔
double getINtervalTime() { return interval; }
//重置鼠标按键状态,必须在每一帧中,处理鼠标消息之前调用
void resetState();
//鼠标消息处理函数
void handleMouseMsg(mouse_msg msg);
//鼠标是否有单击事件产生
bool hasLeftClick() { return clickEvent[0]; }
bool hasMidClick() { return clickEvent[1]; }
bool hasRightClick() { return clickEvent[2]; }
//鼠标是否有按键松开事件产生
bool hasLeftRelease() { return releaseEvent[0]; }
bool hasMidRelease() { return releaseEvent[1]; }
bool hasRightRelease() { return releaseEvent[2]; }
//鼠标是否有双击事件产生
bool hasLeftDoubleClick() { return doubleClickEvent[0]; }
bool hasMidDoubleClick() { return doubleClickEvent[1]; }
bool hasRightDoubleClick() { return doubleClickEvent[2]; }
//是否是鼠标按键按下的消息,只针对一条鼠标消息
bool isLeftPressMsg() { return isPressMsg[0]; }
bool isMidPressMsg() { return isPressMsg[1]; }
bool isRightPressMsg() { return isPressMsg[2]; }
//是否是鼠标按键松开的消息,只针对一条鼠标消息
bool isLeftReleaseMsg() { return isReleaseMsg[0]; }
bool isMidtReleaseMsg() { return isReleaseMsg[1]; }
bool isRightReleaseMsg() { return isReleaseMsg[2]; }
//获取内部按键状态数组,方便循环检测
const bool* getKeysPress() { return isPressMsg; }
const bool* getKeysRelease() { return isReleaseMsg; }
const bool* getKeysClick() { return clickEvent; }
const bool* getKeysDoubleClick() { return doubleClickEvent; }
const mouse_key_state* getKeysState() { return curMouseState; }
//判断鼠标现在是否处于按下状态
bool isLeftDown() { return curMouseState[0] == MOUSE_DOWN; }
bool isMidDown() { return curMouseState[1] == MOUSE_DOWN; }
bool isRightDown() { return curMouseState[2] == MOUSE_DOWN; }
//获取鼠标单击点击位置,需要在鼠标单击事件发生后获取才有效
void getLeftClickPos(int* x, int* y);
void getMidClickPos(int* x, int* y);
void getRightClickPos(int* x, int* y);
private:
//检测鼠标双击
void checkDoubleClick(int keyType);
};
#endif //! MOUSE_STATE_H_
#define SHOW_CONSOLE
#include
#include "MouseState.h"
MouseState::MouseState()
{
interval = 0.4f;
for (int i = 0; i < 3; i++) {
curMouseState[i] = MOUSE_UP;
clickEvent[i] = false;
releaseEvent[i] = false;
doubleClickEvent[i] = false;
xClick[i] = yClick[i] = 0;
//初始赋一个较小的负值
clickTimes[i] = -1000;
isPressMsg[i] = isReleaseMsg[i] = false;
}
}
//设置双击触发的两次点击最大时间间隔(单位:秒)
void MouseState::setIntervalTime(double time_seconds)
{
interval = (time_seconds > 0) ? time_seconds : 0;
}
//重置鼠标按键状态,必须在每一帧中,处理鼠标消息之前调用
void MouseState::resetState()
{
for (int i = 0; i < 3; i++) {
clickEvent[i] = false;
releaseEvent[i] = false;
doubleClickEvent[i] = false;
isPressMsg[i] = isReleaseMsg[i] = false;
}
}
//鼠标消息处理函数
void MouseState::handleMouseMsg(mouse_msg msg)
{
bool isKey[3] = { msg.is_left(), msg.is_mid(), msg.is_right()};
for (int i = 0; i < 3; i++) {
isPressMsg[i] = false;
isReleaseMsg[i] = false;
}
//点击事件检查
for (int i = 0; i < 3; i++) {
if (isKey[i]) {
//鼠标点击
if (msg.is_down()) {
isPressMsg[i] = true;
isReleaseMsg[i] = false;
curMouseState[i] = MOUSE_DOWN;
clickEvent[i] = true;
xClick[i] = msg.x;
yClick[i] = msg.y;
}
//鼠标松开
else if (msg.is_up()) {
isPressMsg[i] = false;
isReleaseMsg[i] = true;
curMouseState[i] = MOUSE_UP;
}
checkDoubleClick(i);
//鼠标消息只能属于一个键,其它键不用再检测
break;
}
}
}
//检测鼠标双击事件
void MouseState::checkDoubleClick(int keyType) {
double curTime = fclock();
/*鼠标双击事件检查*/
if (clickEvent[keyType]) {
if (curTime - clickTimes[keyType] > interval) {
clickTimes[keyType] = curTime;
}
else {
doubleClickEvent[keyType] = true;
clickTimes[keyType] = 0;
}
}
}
//获取鼠标点击位置
void MouseState::getLeftClickPos(int* x, int* y)
{
*x = xClick[0];
*y = yClick[0];
}
void MouseState::getMidClickPos(int* x, int* y)
{
*x = xClick[1];
*y = yClick[1];
}
void MouseState::getRightClickPos(int* x, int* y)
{
*x = xClick[2];
*y = yClick[2];
}
使用流程:
MouseState mouseState;
mouse_msg mouseMsg;
for ( ; is_run(); delay_fps(60)) {
mouseState.resetState();
while (mousemsg()) {
mouseMsg = getmouse();
mouseState.handleMouseMsg(mouseMsg);
//这里用来处理对每一条鼠标消息判断的
//当前的鼠标消息是否是左键按下的消息
if (mouseState.isLeftPressMsg()) {
}
}
//用来处理鼠标点击事件,或者鼠标状态判断
//如果有鼠标左键点击
if (mouseState.hasLeftClick()) {
}
//如果左键是按下状态
if (mouseState.isLeftDown()) {
}
}
#include
#include "MouseState.h"
int main()
{
setinitmode(0, 100, 100); //设置为普通窗口, 设置窗口位置
setrendermode(RENDER_MANUAL); //设置为手动模式
initgraph(700, 300); //初始化窗口
setcaption("EGE鼠标消息获取"); //设置窗口标题
setbkcolor(WHITE);
setcolor(BLACK);
setfont(18, 0, "黑体");
MouseState mouseState;
mouse_msg msg = { 0 };
//设置双击需要两次点击时间间隔不超过多少秒才会触发
mouseState.setIntervalTime(0.4f);
//点击次数计数,0, 1, 2分别是左中右
int clickCount[3] = { 0 }, doubleClickCount[3] = { 0 };
//鼠标按下松开次数计数
int pressCount[3] = { 0 }, releaseCount[3] = { 0 };
//鼠标消息数计数
int msgCount = 0;
int xClickPos[3] = { 0 }, yClickPos[3] = { 0 };
for (; is_run(); delay_fps(60))
{
//这个是mouseState正确处理鼠标点击消息的必要调用,在每一帧未处理鼠标消息之前调用。
mouseState.resetState();
//检测并处理鼠标消息
while (mousemsg())
{
msg = getmouse();
msgCount++;
mouseState.handleMouseMsg(msg);
//鼠标按键松开按下消息是对某一条个鼠标消息来说的,所以放在鼠标消息处理的循环中
//获取状态数组,方便检测3个按键状态
const bool* isPress = mouseState.getKeysPress();
const bool* isRelease = mouseState.getKeysRelease();
for (int i = 0; i < 3; i++) {
if (isPress[i])
pressCount[i]++;
if (isRelease[i])
releaseCount[i]++;
}
}
//计算点击次数,点击事件 在 下次resetState()之前都有效,所以放在鼠标消息处理完成之后
//在下次resetState之前都可以查询是否有点击事件产生
//可以用 hasLeftClick(), hasMidClick(), hasRightClick() 来检测单击事件
//为了方便循环输出,这里直接获取状态数组
const bool* keysClick = mouseState.getKeysClick();
const bool* keysDoubleClick = mouseState.getKeysDoubleClick();
for (int i = 0; i < 3; i++) {
if (keysClick[i]) {
clickCount[i]++;
switch (i) {
case 0: mouseState.getLeftClickPos(&xClickPos[0], &yClickPos[0]); break;
case 1: mouseState.getMidClickPos(&xClickPos[1], &yClickPos[1]); break;
case 2: mouseState.getRightClickPos(&xClickPos[2], &yClickPos[2]); break;
}
}
if (keysDoubleClick[i])
doubleClickCount[i]++;
}
//格式化输出为字符串,用于后面输出
//msg和flag常数请参考文档或者mouse_msg_e, mouse_flag_e的声明
int xMouse, yMouse;
mousepos(&xMouse, &yMouse); //获取鼠标实时位置
cleardevice();
xyprintf(0, 20, "鼠标位置: x = %4d y = %4d",
xMouse, yMouse);
xyprintf(0, 40, "消息类型:move = %d down = %d up = %d",
(int)msg.is_move(),
(int)msg.is_down(),
(int)msg.is_up());
xyprintf(0, 60, "按键: left = %d mid = %d right = %d",
(int)msg.is_left(),
(int)msg.is_mid(),
(int)msg.is_right());
xyprintf(0, 80, "滚轮: wheel = %d wheel rotate = %d",
(int)msg.is_wheel(),
msg.wheel);
xyprintf(0, 100, "辅助键: shift = %d ctrl = %d",
msg.flags == mouse_flag_shift,
msg.flags == mouse_flag_ctrl);
const char* keyNames[3] = { "左", "中", "右" };
const mouse_key_state* keysState = mouseState.getKeysState();
for (int i = 0; i < 3; i++) {
xyprintf(0, 120 + i * 20, "鼠标%s键状态: %s, 按下次数:%3d, 松开次数:%3d, 单击次数:%3d, 双击次数:%3d",
keyNames[i],
(keysState[i] == MOUSE_DOWN) ? "按下" : "松开",
pressCount[i], releaseCount[i],
clickCount[i], doubleClickCount[i]);
}
xyprintf(0, 180, "鼠标消息数总计:%d", msgCount);
xyprintf(0, 220, "点击位置:左[%d, %d], 中[%d, %d], 右[%d, %d]",
xClickPos[0], yClickPos[0], xClickPos[1], yClickPos[1], xClickPos[2], yClickPos[2]);
}
getch();
closegraph();
return 0;
}
int showmouse(int bShow);
bShow为布尔类型,1或0, 1为显示, 0为不显示,可以用true 和 false代替
showmouse(false);
showmouse(true);
当你觉得缓存区中的鼠标消息已经无用时,可以把缓存区中的鼠标消息清空。
flushmouse();
假设有个宽高数为N * M 的格子方块,每个格子宽高分别为 GRID_WIDTH, GRID_HEIGHT。
如果方格区域左上角为 (BLOCK_LEFT, BLOCK_TOP),知道方格区域宽高后,可以直接根据鼠标位置算出鼠标点击的格子坐标,而不用一个一个格子地判断鼠标点击位置是否在范围内。
int xGrid = xClick / GRID_WIDTH;
int yGrid = yClick / GRID_HEIGHT;
这里(xGrid, yGrid)就是格子的坐标
左上角的格子是(0, 0), 右下角的格子是(N - 1, M - 1)。
int xGrid = xClick / (GRID_WIDTH + DIVIDE_WIDTH);
int yGird = yClick / (GRID_HEIGHT + DIVIDE_HEIGHT);
//计算点击坐标在对应大格子中的位置
int x = xClick - xGrid * (GRID_WIDTH + DIVIDE_WIDTH);
int y = yClick - yGrid * (GRID_HEIGHT + DIVIDE_HEIGHT);
然后根据x, y 来判断是否点击到实际格子
下面是示例程序:通过点击,使方格颜色翻转。
#include
//方块参数
const int N = 4, M = 8;
const int GRID_WIDTH = 150, GRID_HEIGHT = 75;
const int BLOCK_LEFT = 10, BLOCK_TOP = 60;
const int BLOCK_WIDTH = N * GRID_WIDTH, BLOCK_HEIGHT = M * GRID_HEIGHT;
//窗口大小
const int SCR_WIDTH = BLOCK_LEFT * 2 + BLOCK_WIDTH;
const int SCR_HEIGHT = BLOCK_TOP + BLOCK_HEIGHT + 10;
//颜色定义
const color_t DARK_GRAY = EGERGB(0x33, 0x33, 0x33);
const color_t LIGHT_GRAY = EGERGB(0xaa, 0xaa, 0xaa);
const color_t BG_COL = WHITE;
int grid[M][N] = { 0 };
void checkBlockClick(int x, int y, int* xGrid, int* yGrid); //方块点击检测
void drawAllGrid(); //绘制所有格子
void drawGrid(int xGrid, int yGrid); //绘制格子
void flipGrid(int xGrid, int yGrid); //翻转格子
int main()
{
initgraph(SCR_WIDTH, SCR_HEIGHT, INIT_RENDERMANUAL);
setbkcolor(BG_COL);
setcaption("点击方格翻转");
setfont(28, 0, "楷体");
setbkmode(TRANSPARENT);
drawAllGrid();
bool click_flag;
int xClick, yClick;
for (; is_run(); delay_fps(60)) {
//点击标志位清零
click_flag = false;
//处理鼠标消息
while (mousemsg()) {
mouse_msg msg = getmouse();
//左键按下
if (msg.is_left() && msg.is_down()) {
//标志位置位,记录点击位置
click_flag = true;
xClick = msg.x;
yClick = msg.y;
}
}
//检测到点击
if (click_flag) {
//信息输出区域清屏
setfillcolor(BG_COL);
bar(0, 0, SCR_WIDTH, BLOCK_TOP);
//输出鼠标点击坐标
setcolor(BLACK);
xyprintf(0, 0, "鼠标点击:[%d, %d]", xClick, yClick);
//计算鼠标点击格子坐标
int xGrid, yGrid;
checkBlockClick(xClick, yClick, &xGrid, &yGrid);
//点击在格子区域
if (xGrid != -1 || yGrid != -1) {
setcolor(BLACK);
xyprintf(0, 30, "翻转格子:(%d, %d)\n", xGrid, yGrid);
//翻转对应格子
flipGrid(xGrid, yGrid);
}
}
}
closegraph();
return 0;
}
//绘制格子
void drawGrid(int xGrid, int yGrid)
{
//计算格子所在区域
int left = BLOCK_LEFT + xGrid * GRID_WIDTH, top = BLOCK_TOP + yGrid * GRID_HEIGHT;
int right = left + GRID_WIDTH - 1, bottom = top + GRID_HEIGHT - 1;
setfillcolor((grid[yGrid][xGrid] == 0) ? DARK_GRAY : LIGHT_GRAY);
bar(left, top, right, bottom);
//绘制间隔线
setcolor(WHITE);
if (xGrid != N - 1)
line(right, top, right, bottom);
if (yGrid != M - 1)
line(left, bottom, right, bottom);
}
//翻转格子
void flipGrid(int xGrid, int yGrid)
{
if (0 <= xGrid && xGrid < N && 0 <= yGrid && yGrid < M) {
grid[yGrid][xGrid] = !grid[yGrid][xGrid];
drawGrid(xGrid, yGrid);
}
}
//绘制所有格子
void drawAllGrid()
{
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
drawGrid(j, i);
}
}
}
void checkBlockClick(int x, int y, int* xGrid, int* yGrid)
{
//判断点击位置是否在方块区域
if (BLOCK_LEFT <= x && x < BLOCK_LEFT + BLOCK_WIDTH
&& BLOCK_TOP <= y && y < BLOCK_TOP + BLOCK_HEIGHT) {
//计算相对于方块区域左上角的坐标,方便计算
x -= BLOCK_LEFT;
y -= BLOCK_TOP;
//计算出点击的格子
*xGrid = x / GRID_WIDTH;
*yGrid = y / GRID_HEIGHT;
}
else {
*xGrid = *yGrid = -1;
}
}
#include
#include
struct Circle
{
int x, y;
double radius;
int edge;
};
void draw(const Circle& c)
{
setfillcolor(EGEARGB(0xFF, 0xa0, 0x00, 0xa0));
ege_fillellipse(c.x - c.radius - c.edge, c.y - c.radius - c.edge, 2 * (c.radius + c.edge), 2 * (c.radius + c.edge));
setfillcolor(EGEARGB(0xFF, 0xFF, 0x33, 0xFF));
ege_fillellipse(c.x - c.radius, c.y - c.radius, c.radius * 2, c.radius * 2);
}
int main()
{
initgraph(640, 480, INIT_RENDERMANUAL);
setbkcolor(WHITE);
delay_ms(0);
setcolor(BLACK);
setfont(20, 0, "楷体");
setbkmode(TRANSPARENT);
//开启抗锯齿
ege_enable_aa(true);
const int N = 3;
Circle circles[N] = {
{100, 240, 80.0, 10 },
{300, 240, 80.0, 10},
{500, 240, 80.0, 10}
};
int xBase, yBase; //记录鼠标左键点击的位置
int checkid = -1;
bool move_flag = false, zoom_flag = false;
bool redraw = true;
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
mouse_msg msg = getmouse();
//鼠标左键点击位置记录
if (msg.is_move() ) {
if (checkid != -1) {
//移动
if (move_flag) {
//根据基点和当前位置移动物体
circles[checkid].x += msg.x - xBase;
circles[checkid].y += msg.y - yBase;
}
//缩放
else {
//根据基点和当前位置分别到圆心的距离,计算缩放的大小
int x1 = msg.x - circles[checkid].x, y1 = msg.y - circles[checkid].y;
int x2 = xBase - circles[checkid].x, y2 = yBase - circles[checkid].y;
double dr = sqrt(x1 * x1 + y1 * y1) - sqrt(x2 * x2 + y2 * y2);
circles[checkid].radius += dr;
//缩放限制
if (circles[checkid].radius < circles[checkid].edge)
circles[checkid].radius = circles[checkid].edge;
}
//操作后,改变基点
xBase = msg.x;
yBase = msg.y;
redraw = true;
}
}
else if (msg.is_left()) {
if (msg.is_down()) {
//设置基点为点击位置
xBase = msg.x;
yBase = msg.y;
//对每个物体进行检测,
for (int i = 0; i < N; i++) {
int dist = (xBase - circles[i].x) * (xBase - circles[i].x) + (yBase - circles[i].y) * (yBase - circles[i].y);
//移动选中(内圆)
if (dist < circles[i].radius * circles[i].radius) {
move_flag = true;
checkid = i;
break;
}
//缩放选中(外环)
else if (dist < (circles[i].radius + circles[i].edge) * (circles[i].radius + circles[i].edge)) {
zoom_flag = true;
checkid = i;
break;
}
}
}
else {
//动作标志位复位
checkid = -1;
move_flag = zoom_flag = false;
}
}
}
//空格键重置
while (kbmsg()) {
key_msg msg = getkey();
if (msg.key == key_space && msg.msg == key_msg_down) {
for (int i = 0; i < N; i++) {
circles[i].x = 100 + i * 200;
circles[i].y = 240;
circles[i].radius = 80.0;
}
zoom_flag = move_flag = false;
redraw = true;
}
}
//重绘
if (redraw) {
redraw = false;
cleardevice();
for (int i = N -1 ; i >= 0; i--)
draw(circles[i]);
setcolor(BLACK);
outtextxy(300, 0, "拖动内圆移动,拖动外环调整大小");
outtextxy(300, 22, "按空格键重置位置及大小");
}
}
closegraph();
return 0;
}
选择拖动是首次点击选中物体,然后鼠标移动改变物体参数,再次点击则确定修改。
#include
#include
struct Circle
{
int x, y;
double radius;
int edge;
};
void draw(const Circle& c)
{
setfillcolor(EGEARGB(0xFF, 0xa0, 0x00, 0xa0));
ege_fillellipse(c.x - c.radius - c.edge, c.y - c.radius - c.edge, 2 * (c.radius + c.edge), 2 * (c.radius + c.edge));
setfillcolor(EGEARGB(0xFF, 0xFF, 0x33, 0xFF));
ege_fillellipse(c.x - c.radius, c.y - c.radius, c.radius * 2, c.radius * 2);
}
int main()
{
initgraph(640, 480, INIT_RENDERMANUAL);
setbkcolor(WHITE);
delay_ms(0);
setcolor(BLACK);
setfont(20, 0, "楷体");
setbkmode(TRANSPARENT);
//开启抗锯齿
ege_enable_aa(true);
const int N = 3;
Circle circles[N] = {
{100, 240, 80.0, 10 },
{300, 240, 80.0, 10},
{500, 240, 80.0, 10}
};
int xBase, yBase; //记录鼠标左键点击的位置
int checkid = -1;
bool move_flag = false, zoom_flag = false;
bool redraw = true;
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
mouse_msg msg = getmouse();
//鼠标左键点击位置记录
if (msg.is_move() ) {
if (checkid != -1) {
//移动
if (move_flag) {
//根据基点和当前位置移动物体
circles[checkid].x += msg.x - xBase;
circles[checkid].y += msg.y - yBase;
}
//缩放
else {
//根据基点和当前位置分别到圆心的距离,计算缩放的大小
int x1 = msg.x - circles[checkid].x, y1 = msg.y - circles[checkid].y;
int x2 = xBase - circles[checkid].x, y2 = yBase - circles[checkid].y;
double dr = sqrt(x1 * x1 + y1 * y1) - sqrt(x2 * x2 + y2 * y2);
circles[checkid].radius += dr;
//缩放限制
if (circles[checkid].radius < circles[checkid].edge)
circles[checkid].radius = circles[checkid].edge;
}
//操作后,改变基点
xBase = msg.x;
yBase = msg.y;
redraw = true;
}
}
else if (msg.is_left()) {
if (msg.is_down()) {
if (checkid == -1) {
//设置基点为点击位置
xBase = msg.x;
yBase = msg.y;
//对每个物体进行检测,
for (int i = 0; i < N; i++) {
int dist = (xBase - circles[i].x) * (xBase - circles[i].x) + (yBase - circles[i].y) * (yBase - circles[i].y);
//移动选中(内圆)
if (dist < circles[i].radius * circles[i].radius) {
move_flag = true;
checkid = i;
break;
}
//缩放选中(外环)
else if (dist < (circles[i].radius + circles[i].edge) * (circles[i].radius + circles[i].edge)) {
zoom_flag = true;
checkid = i;
break;
}
}
}
else {
//动作标志位复位
checkid = -1;
move_flag = zoom_flag = false;
}
}
}
}
//空格键重置
while (kbmsg()) {
key_msg msg = getkey();
if (msg.key == key_space && msg.msg == key_msg_down) {
for (int i = 0; i < N; i++) {
circles[i].x = 100 + i * 200;
circles[i].y = 240;
circles[i].radius = 80.0;
}
zoom_flag = move_flag = false;
redraw = true;
}
}
//重绘
if (redraw) {
redraw = false;
cleardevice();
for (int i = N -1 ; i >= 0; i--)
draw(circles[i]);
setcolor(BLACK);
outtextxy(300, 0, "点击内圆移动,点击外环改变大小");
outtextxy(300, 22, "再次点击左键确认");
outtextxy(300, 44, "按空格键重置位置及大小");
}
}
closegraph();
return 0;
}