所以,我们采用自动调节速度的方式来调整移动光标的速度。为了让速度的变化更加直观,我们绘制了速度走势曲线图,如下图所示:
【周期性调整光标的移动速度】
从速度走势图中可以看出,总体上速度是呈周期性的变化。速度走势图中显示的是每次移动的距离,自然,移动的距离越大,速度越大。这个效果的实现,分为键盘光标的响应、鼠标光标的移动、智能调整速度和绘制速度曲线四部分。
键盘光标的响应:
在WM_KEYDOWN消息中,对比wParam携带的虚拟键码,即可对四个光标键进行响应。
鼠标光标的移动:
要说鼠标光标的移动,实际上就是设置鼠标光标的位置。透过现象看本质,移动光标就是不同的改变光标,随着时间的推移,在视觉上就形成了动感,就是光标移动的效果。我们使用函数SetCursorPos函数,传入xy坐标值即可设置。鼠标光标的坐标是屏幕坐标,不过我们对此并不关心。我们是实现的基于相对当前鼠标光标位置的移动。所以我们要获取当前光标的位置,调用函数GetCursorPos,并用POINT结构体变量来接受获得的光标位置的坐标。
智能调整速度:
其实我们就是利用数学的线程方程实现z=ax+y。我们累计y的值,然后当y到达21的时候,就增加x的值,也就是增加斜率,也就是加速变大的效果。这样速度会越来越快。当速度加速度到达了6的时候,然后开始递减加速度,降低速度的加速度。这里就是起一个演示的作用。你可以自己实现更多的速度控制。为了制造更多的随机的速度走势,我们在斜率x和y上都做了随机值。但是总体上,还是这个方程式的曲线走势。
绘制速度曲线:
移动的间距每次都会存入STL的vector容器,然后在WM_PAINT消息时绘制出来。所以在每次按下光标键移动鼠标光标的时候,就通知客户区重绘,即调用InvalidateRect。不过我们不需要客户区擦除背景,因为之前的速度曲线是重复的,没必要擦除,所以InvalidateRect第三个参数设置为FALSE。我们将vector容器中的移动的间距取出来轴y坐标,以i为x轴坐标,坐标系是客户区坐标系,所以是倒立的。然后就从原点不停的划线,连接各个点,即可形成走势曲线。
如果你不想要这样的走势曲线,而想画成柱状图那样的曲线,只要每次将直线的起始点移动到客户区的顶部即可。柱状图如下图所示:
【使用树状图来代替走势图】
下面是完整的代码:#include "windows.h"
#include <tchar.h>
#include <vector>
using namespace std;
TCHAR tip[]=_T("C++技术网www.cjjjs.com");
// - 项目是Unicode字符集
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static int iAcc = 0;
static int iAcc2 = 0;
static int step=0;
static vector<int> speedlist;
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_KEYDOWN:
{
POINT pt;
GetCursorPos(&pt);
//移动速度自动调节
iAcc2++;//累计iAcc2
step=iAcc*rand()%3+iAcc2+rand()%10;//移动的幅度计算
if (iAcc2>20)
{
//Acc2累计到20,则加速一级
iAcc++;//线性加速一级
iAcc2=0;//重新计数
}
if (iAcc>5)
{
//如果速度太快,控制速度,回归速度
iAcc--;//线性递减
}
speedlist.push_back(step);
switch(wParam)
{
case VK_UP:
pt.y += -step;
break;
case VK_DOWN:
pt.y += step;
break;
case VK_LEFT:
pt.x += -step;
break;
case VK_RIGHT:
pt.x += step;
break;
}
SetCursorPos(pt.x,pt.y);//设置光标位置
}
InvalidateRect(hwnd,NULL,FALSE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd,&ps);
GetClientRect(hwnd,&rect);
for (int i=0;i<speedlist.size();i++)
{
MoveToEx(hdc,i*2,0,NULL);//去掉注释,可以形成柱状图
LineTo(hdc,i*2,speedlist[i]*5);
}
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
break;//跳出到默认处理
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,LPSTR lpCmd,int iShow)
{
TCHAR ClassName[] = _T("MyClass");
TCHAR title1[] = _T("C++技术网http://www.cjjjs.com");
WNDCLASS wndClass;
wndClass.cbClsExtra=0;
wndClass.cbWndExtra=0;
wndClass.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WinProc;
wndClass.lpszClassName = ClassName;
wndClass.lpszMenuName=NULL;
wndClass.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
if(!RegisterClass(&wndClass))return 0;
HWND hwnd = CreateWindow(ClassName,title1,WS_OVERLAPPEDWINDOW,0,0,350,400,NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);
MSG msg;
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}