SimpleCG程序交互操作

2023-09-13 20:31:41

 前言

        之前所有示例程序都是属于展示型的,只是作为展示板输出使用,不涉及键盘和鼠标的输入交互,下面我们开始接触具有交互功能的程序。

        没有交互功能的程序可以满足一定需求,不过大部分的程序是不能脱离交互功能的。程序依据使用者的操作进行相应反应并进行相关输出从而达到互动,将让程序的魅力提高一个层次。一个典型的应用就是游戏,所以学习完本篇内容后将可以进行一些游戏程序的编写。当前SimpleCG库主要支持的输入操作是鼠标和键盘,下面看看具体如何操作吧。

一、消息处理基础

        想要在SimpleCG中处理Windows的交互操作,就是需要定义一个消息处理函数,消息处理函数名字可以随便定义,但形式是固定的。例如鼠标消息固定形式如下所示:

LRESULT OnLButtonDown( HWND hWnd, WPARAM wParam, int nX, int nY )
{
}

名字OnLButtonDown是可以自己定义的,一般也是按消息功能命名。定义完消息函数后,依据想要接收的消息进行注册,所谓注册也就是调用一个设置函数。例如想要接收左键点击消息,按如下方式设置:

SetMouseProcess( enumINMSG_LBUTTONDOWN, OnLButtonDown);

这里要注意的是,设置函数一定要在初始化阶段设置,一般是程序开始处设置。

二、鼠标操作

支持的鼠标消息如下所示:

    enumINMSG_MOUSEMOVE            //鼠标移动
	, enumINMSG_LBUTTONDOWN        //左键按下
	, enumINMSG_LBUTTONUP          //左键抬起
	, enumINMSG_LBUTTONDBLCLK      //左键双击
	, enumINMSG_RBUTTONDOWN        //右键按下
	, enumINMSG_RBUTTONUP          //右键抬起
	, enumINMSG_RBUTTONDBLCLK      //右键双击
	, enumINMSG_MBUTTONDOWN        //中键按下
	, enumINMSG_MBUTTONUP          //中键抬起
	, enumINMSG_MBUTTONDBLCLK      //中键双击
	, enumINMSG_MOUSEWHEEL         //滚轮消息

当需要处理相应消息时,只要按上面说到的两个步骤定义相应函数即可。下面用一个绘图功能的程序来演示一下鼠标左键消息的处理。

#include "../import/include/CGBoard.h"
#include "math.h"
#ifdef _DEBUG
#pragma comment(lib,"../import/lib/SimpleCG_MDd.lib")
#else
#pragma comment(lib,"../import/lib/SimpleCG_MD.lib")
#endif


int g_nWidth = 500;		//画面宽度
int g_nHeight= 400;		//画面高度

int g_bIsDrawing = 0;
POINT g_ptLast;
void DrawProcess()
{
}

LRESULT OnLButtonDown( HWND hWnd, WPARAM wParam, int nX, int nY )
{
	g_ptLast.x = nX;
	g_ptLast.y = nY;
	g_bIsDrawing = 1;
	return TRUE;
}
LRESULT OnLButtonUp( HWND hWnd, WPARAM wParam, int nX, int nY )
{
	
	if(g_bIsDrawing)
	{
		line(g_ptLast.x,g_ptLast.y,nX,nY);
		g_ptLast.x = nX;
		g_ptLast.y = nY;
		g_bIsDrawing = 0;
	}
	return TRUE;
}
LRESULT OnMouseMove( HWND hWnd, WPARAM wParam, int nX, int nY )
{
	if(g_bIsDrawing)
	{
		line(g_ptLast.x,g_ptLast.y,nX,nY);
		g_ptLast.x = nX;
		g_ptLast.y = nY;
	}
	return TRUE;
}

int _tmain(int argc, _TCHAR* argv[])
{
	//设置消息处理函数
	SetMouseProcess( enumINMSG_LBUTTONDOWN, OnLButtonDown);
	SetMouseProcess( enumINMSG_LBUTTONUP,	OnLButtonUp);
	SetMouseProcess( enumINMSG_MOUSEMOVE,	OnMouseMove);

	//初始化
	if( !ShowingBoard(g_nWidth,g_nHeight, DrawProcess))
		return 1;
	//关闭图库
	CloseBoard();
	return 0;
}

该程序可以用鼠标在窗口点击要绘制的地方,然后移动鼠标绘制出鼠标轨迹,直至左键抬起。

另外函数

LRESULT OnLButtonDown( HWND hWnd, WPARAM wParam, int nX, int nY )
{
}

 中各参数意义如下:

hWnd是窗口句柄,暂时不需要了解

wParam附带有消息产生时的按键信息,可通过如下方法判断。

MK_CONTROL 按下了 CTRL 键。
MK_LBUTTON 按下了鼠标左键。
MK_MBUTTON 按下了鼠标中键。
MK_RBUTTON 按下了鼠标右键。
MK_SHIFT 按下了 SHIFT 键。
MK_XBUTTON1 按下了第一个 X 按钮。
MK_XBUTTON2 按下了第二个 X 按钮。

例如通过代码

if(wParam&MKCONTROL)就可以判断CTRL键是否按下。

nX和nY是鼠标在窗口中的坐标

三、键盘消息

 键盘支持的消息如下:

    enumINMSG_KEYDOWN    //按键按下
	, enumINMSG_KEYUP    //按键放开

按键消息响应函数如下

//按键消息响应函数
LRESULT OnKeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
}

其中wParam代表当前虚拟键,各个虚拟键值可在此网站查找。

lParam是32位数含义如下:

0-15    当前消息的重复计数。 该值是由于用户按住键而自动重复击键的次数。 
16-23    扫描代码。 该值取决于 OEM。
24    指示键是扩展键,例如在增强型 101 键或 102 键键盘上显示的右侧 Alt 键和 Ctrl 键。 如果是扩展键,则值为 1;否则为 0。
25-28    保留;请勿使用。
29    上下文代码。 对于 WM_KEYDOWN 消息,该值始终为 0。
30    上一个键状态。 如果键在发送消息之前关闭,则值为 1;如果键已打开,则值为 0。
31    转换状态。 对于 WM_KEYDOWN 消息,该值始终为 0。

下面用一个可以用方向键控制移动的小球来演示按键的交互

#include "../import/include/CGBoard.h"
#include "math.h"
#ifdef _DEBUG
#pragma comment(lib,"../import/lib/SimpleCG_MDd.lib")
#else
#pragma comment(lib,"../import/lib/SimpleCG_MD.lib")
#endif


int g_nWidth = 500;		//画面宽度
int g_nHeight= 400;		//画面高度

int g_nXCircle = 100;
int g_nYCircle = 100;
void DrawProcess()
{
	setfillcolor(RGB(0xAA,0xAA,0xFF));
	fillcircle(g_nXCircle, g_nYCircle, 10 );
}

//按键消息响应函数
LRESULT OnKeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
	int nStepX = 0;
	int nStepY = 0;
	switch( wParam )
	{
	case VK_DOWN:
		nStepY = 1;
		break;
	case VK_UP:
		nStepY = -1;
		break;
	case VK_LEFT:
		nStepX = -1;
		break;
	case VK_RIGHT:
		nStepX = 1;
		break;
	}
	_clearcircle(g_nXCircle, g_nYCircle, 12 );
	g_nXCircle += nStepX;
	g_nYCircle += nStepY;
	fillcircle(g_nXCircle, g_nYCircle, 10 );
	return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
	//设置消息处理函数
	SetKeyboardProcess( enumINMSG_KEYDOWN,	OnKeyDown );

	//初始化
	if( !ShowingBoard(g_nWidth,g_nHeight, DrawProcess))
		return 1;
	//关闭图库
	CloseBoard();
	return 0;
}

有兴趣的同学可以试着运行看看效果。

四、接管所有消息处理

        当对Windows编程有了更深入了解后,需要处理除了以上消息之外的消息时,可以设置一个所有消息的处理函数。方法如下:

首先定义消息处理函数

LRESULT OnMessage( HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam )
{
	//此处输入消息处理过程

	return 0;
}

然后使用SetInputProcess进行设置

SetInputProcess( OnMessage );

此处需要注意的是使用了SetInputProcess( OnMessage );函数后,就要自己处理所有交互消息,所以SetMouseProcess和SetKeyboardProcess都将失效。

下面我们运用自定义消息处理函数对窗口关闭操作进行拦截的演示。代码如下:

// DemoMessage.cpp : 定义控制台应用程序的入口点。
//
#include "../import/include/CGBoard.h"
#include "math.h"
#ifdef _DEBUG
#pragma comment(lib,"../import/lib/SimpleCG_MDd.lib")
#else
#pragma comment(lib,"../import/lib/SimpleCG_MD.lib")
#endif


int g_nWidth = 500;		//画面宽度
int g_nHeight= 400;		//画面高度

int g_bIsDrawing = 0;
POINT g_ptLast;
int g_nLineWidth = 1;

int g_nXCircle = 100;
int g_nYCircle = 100;
void DrawProcess()
{
	setfillcolor(RGB(0xAA,0xAA,0xFF));
	fillcircle(g_nXCircle, g_nYCircle, 10 );
}


LRESULT OnMessage( HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam )
{
	if( nMessage == WM_CLOSE )
	{
		if(MessageBox(NULL,_T("是否退出"),_T("提示"),MB_YESNO) == IDYES )
			return 0;
		return 1;
	}
	return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
	//设置消息处理函数
	SetInputProcess( OnMessage );
	//初始化
	if( !ShowingBoard(g_nWidth,g_nHeight, DrawProcess))
		return 1;
	//关闭图库
	CloseBoard();
	return 0;
}

五、结语

        通过掌握以上介绍的内容,就可以编写出大部分可交互程序。不过要完全操控窗口,还需要熟练掌握更多的消息及参数。对于游戏编写,后面会给出一个简单框架。

SimpleCG库安装 

更多推荐

Attention is all you need 论文笔记

该论文引入Transformer,主要核心是自注意力机制,自注意力(Self-Attention)机制是一种可以考虑输入序列中所有位置信息的机制。RNN介绍引入RNN为了更好的处理序列信息,比如我吃苹果,前后的输入之间是有联系的。如图:引入Transformer的原因解决长距离依赖的问题:传统的RNN存在梯度消失和梯度

Java8 Stream 数据流,大数据量下的性能效率

Stream是JavaSE8类库中新增的关键抽象,它被定义于java.util.stream(这个包里有若干流类型:Stream<T>代表对象引用流,此外还有一系列特化流,如IntStream,LongStream,DoubleStream等),Java8引入的的Stream主要用于取代部分Collection的操作,

MapReduce & YARN 的部署

1、部署说明HadoopHDFS分布式文件系统,我们会启动:NameNode进程作为管理节点DataNode进程作为工作节点SecondaryNamenode作为辅助同理,HadoopYARN分布式资源调度,会启动:ResourceManager进程作为管理节点NodeManager进程作为工作节点ProxyServe

Python 数据分析学习路线

Python数据分析学习路线第一阶段:Python语言基础第二阶段:数据采集和持久化第三阶段:数据分析第四阶段:数据挖掘与机器学习书籍介绍参与方式第一阶段:Python语言基础在学习数据分析之前,首先需要掌握Python语言的基础知识,包括语法、常用数据结构、函数以及面向对象编程等。同时,还需要熟悉Python的标准库

MySQL索引,事务及存储引擎

目录MySQL索引创建索引的依据:索引的类型普通索引唯一索引主键索引组合索引全文索引查看索引删除索引事务事务的ACID特性原子性一致性隔离性持久性隔离级别设置隔离级别事务管理操作自动提交事务存储引擎MyISAMInnoDB的区别MyISAM表支持3种不同的存储格式静态(固定长度)表动态表压缩表如何避免死锁MySQL索引

Windows 10怎么清理磁盘空间?

现在,电脑是我们日常生活中很普遍的的一个工具,但在平常使用电脑时,我们会经常遇到磁盘空间已满的问题。而很多计算机新手都不知道电脑怎么清理磁盘空间,本将已Win10系统为例,教您如何清理电脑磁盘空间。方法1.清理储存对于磁盘已满的问题,我们经常遇到的就是C盘已满。C盘也就系统盘,它存放着大量的系统文件,当您安装软件时若是

密码学概论

1.密码学的三大历史阶段:第一阶段古典密码学依赖设备,主要特点数据安全基于算法的保密,算法不公开,只要破译算法密文就会被破解,在1883年第一次提出加密算法应该基于算法公开不影响密文和秘钥的安全;衡量加密算法的安全是所需要话费的时间,一般电脑需要300年才能破译认为强度是安全的第二阶段计算机的出现进入电子密码学阶段,加

实时车辆行人多目标检测与跟踪系统(含UI界面,Python代码)

算法架构:目标检测:yolov5目标跟踪:OCSort其中,Yolov5带有详细的训练步骤,可以根据训练文档,训练自己的数据集,及其方便。另外后续目标检测会添加yolov7、yolox,目标跟踪会添加ByteTrack、deepsort等经典算法,代码主要部分添加了详细注释,方便自己学习。一、简介本文将详细介绍如何使用

论文解读-DeepEdit:使用纳米孔直接RNA测序对A - to - I RNA编辑事件进行单分子检测和阶段划分

DOI:10.1186/s13059-023-02921-0期刊:GenomeBiology中科院分区:Q1影响因子:12.3作者LongxianChen;LiangOu;XinyunJing;YaweiKong;BingranXie;etal出版日期2023-04-17网址:https://genomebiology

kafka配置

生产者配置NAMEDESCRIPTIONTYPEDEFAULTVALIDVALUESIMPORTANCEbootstrap.servershost/port列表,用于初始化建立和Kafka集群的连接。列表格式为host1:port1,host2:port2,…,无需添加所有的集群地址,kafka会根据提供的地址发现其他

前后端分离--Vue的入门基础版

目录一.前后端分离二.Vue的简介三.Vue的入门案例四.Vue的生命周期一.前后端分离前后端分离是一种软件架构模式,将应用程序的前端(用户界面)和后端(数据处理和业务逻辑)独立开发、独立部署。在前后端分离的架构下,前端通过API(应用程序接口)与后端进行通信,实现数据交换和业务处理。前后端分离的优势包括:独立开发:前

热文推荐