BestAns

路漫漫其修远兮,吾将上下而求索

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;
}

[本节作业]

  1. 画一个填充的三角形,要用鼠标点选三角形的三个顶点。提示:可以用 fillpolygon 函数画多边形。

  2. 写一个“格子涂色”的游戏,要求:屏幕上有16x8的格子,屏幕底部有类似画笔中的选色区(随便放上一些常用的颜色),鼠标点击选择区的颜色后,就作为当前颜色,然后再点屏幕上的格子,就可以用刚才的颜色填涂相应格子。

评论 (8) -

  • 作业2
    #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;
    }
  • 提示 GetMouseMsg 错误的话,请使用一下代码,
    并参考 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;
    }
  • 为什么我这里提示GetMouseMsg不正确  “deprecated ”  ,用的vs2019
    • 因为以前的文章是针对老版本的 easyx 写的,新版本有了新的接口函数,教程已经改成了新版本的函数。
      如果要用老版本函数,解决办法:https://codebus.cn/yangw/c4996
    • 提示 GetMouseMsg 错误的话,请使用一下代码,
      并参考 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;
      }
  • // 编译环境:Visual C++ 6.0~2019,EasyX_20210730
    // 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;
    }
  • 按右键退出,那个return 好像没有加0,反正我运行时报错了

添加评论