Timer定时器对象能够再每隔一段时间发出一个时间消息,程序收到消息后,就能够执行一些操做。好比,能够设置定时器来播放静态的连续图片,就能够产生动画效果。这也是一般显示动画的一种方式。canvas
Windows API中有这样的函数:SetTimer()为咱们定义一个定时器。函数原型:windows
UINT_PTR SetTimer( HWND hWnd, // 窗口句柄 UINT_PTR nIDEvent, // 定时器代号 UINT uElapse, // 时间设定的值,单位为毫秒 TIMERPROC lpTimerFunc // 定时器响应函数 );
这里举一个小例子:数组
SetTimer(hwnd, // 窗口句柄 IDT_TIMER1, // 代号 10000, // 10秒 (TIMERPROC) NULL); // 没有响应函数 SetTimer(hwnd, IDT_TIMER2, 5000, (TIMERPROC) NULL); case WM_TIMER: switch (wParam) { case IDT_TIMER1: // 执行10秒的操做 return 0; case IDT_TIMER2: // 执行5秒的操做 return 0; }
建立后天然须要删除定时器。KillTimer()就是用来终止某个定时器的函数
BOOL KillTimer( HWND hWnd, // 窗口句柄 UINT_PTR uIDEvent // 定时器代号 );
运用定时器使预先作好的连续的静态图片播放,造成动画的效果。
新建Win32程序,在VS2008中默认使用默认的生成窗口的代码。动画
这里顺便提一提,在VS中使用多字节字符集设置能够在解决方案资源管理器中右击方案,选择属性,打开的属性页里能够设置。ui
如图:spa
接下来,在程序头部添加全局变量:code
HBITMAP girl[7]; // 用于7张人物的位图数组 HDC mdc,hmdc; int num; // 用于计数循环
在InitInstance()函数中添加代码:对象
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; char filename[20] = ""; int i; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } MoveWindow(hWnd, 10, 10, 600, 450, true); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); hmdc = GetDC(hWnd); mdc = CreateCompatibleDC(hmdc); //将7张位图载入 for (i = 0; i < 7; i++) { sprintf(filename, "girl%d.bmp", i); girl[i] = (HBITMAP)LoadImage(NULL,filename,IMAGE_BITMAP,640,480,LR_LOADFROMFILE); } num = 0; SetTimer(hWnd, 1, 500, NULL); // 设定0.5秒的定时器 MyPaint(hmdc); return TRUE; }
WndProc()中添加WM_TIMER消息:blog
case WM_TIMER: MyPaint(hmdc); break;
程序退出是须要析构全部建立的对象:
case WM_DESTROY: //删除和释放工做 DeleteDC(mdc); ReleaseDC(hWnd, hmdc); for (i = 0; i < 7; i++) { DeleteObject(girl[i]); } KillTimer(hWnd, 1); PostQuitMessage(0); break;
MyPaint()函数的实现以下:
void MyPaint(HDC hdc) { if(num == 7) num = 0; SelectObject(mdc,girl[num]); BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY); num++; }
这样,程序就算完成了。运行,会看到小女孩在摆动的动画。
咱们能够加快定时器的速率,使动画看起来更加连贯。
这里是源代码文件:点击
2、游戏循环
在上例中,经过Timer来设定动画的帧来显示游戏,这事实上市仅仅使用在小型游戏中的方式。通常来讲,咱们为了是游戏显示得更加顺畅,每秒钟必须更新画面25次以上。在此之中,咱们还须要处理其余大量的游戏操做。而用定时器来驱动,每每得不到咱们想要的画面效果。这里提出了一种叫作“游戏循环”的概念。
游戏循环是将原先程序中的消息循环加以修改,方法是判断其中的内容目前是否要处理的消息,若是有则进行,不然按照设定的时间间隔来重绘画面。
这里借助一个例子讲解:
在WinMain函数中
//游戏循环 while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { tNow = GetTickCount(); // 得到系统开启时间 if (tNow - tPre >= 100) MyPaint(hdc); // 绘图函数 } }
msg.message收到不是WM_QUIT消息时,进行运行循环。PeekMessage检测是否有处理消息(包括WM_QUIT)。没有消息就返回0,不然返回非0。值得注意的是,不能用GetMessage()取代PeekMessage()。
此例子和上面的例子是一样的原理,显示一个动画。
运行后,显示的也是一个动画。本动画由七副静态图循环粘贴造成。即,BITMAP man[7]
整个程序以下:
#include <windows.h> #include <stdio.h> // 全局变量 HINSTANCE hInst; HBITMAP man[7]; HDC hdc,mdc; HWND hWnd; // 分别记录上次绘图时间,本次准备绘图时间,记录每秒开始的时间 DWORD tPre, tNow, tCheck; int num, frame, fps; // 函数声明 ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void MyPaint(HDC hdc); // 入口函数 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; ZeroMemory(&msg, sizeof(MSG)); MyRegisterClass(hInstance); if( !InitInstance(hInstance, nCmdShow) ) { return FALSE; } //游戏循环 while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { tNow = GetTickCount(); // 得到系统开启时间 if (tNow - tPre >= 100) MyPaint(hdc); // 绘图函数 } } return msg.wParam; } //注册窗口类函数 ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = "canvas"; wcex.hIconSm = NULL; return RegisterClassEx(&wcex); } // 初始化窗口 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { char filename[20] = ""; int i; hInst = hInstance; hWnd = CreateWindow("canvas", "游戏循环" , WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } MoveWindow(hWnd,10,10,600,450,true); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); hdc = GetDC(hWnd); mdc = CreateCompatibleDC(hdc); // 循环载入各个静态图 for(i=0;i<7;i++) { sprintf(filename,"man%d.bmp",i); man[i] = (HBITMAP)LoadImage(NULL,filename,IMAGE_BITMAP,640,480,LR_LOADFROMFILE); } num = 0; frame = 0; MyPaint(hdc); return TRUE; } // 绘制图形 void MyPaint(HDC hdc) { char str[40] = ""; if(num == 7) num = 0; frame++; // 更新画面次数加1 if(tNow - tCheck >= 1000) { fps = frame; frame = 0; tCheck = tNow; } SelectObject(mdc,man[num]); sprintf(str,"每秒钟显示 %d 个画面",fps); TextOut(mdc,0,0,str,strlen(str)); BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY); tPre = GetTickCount(); // 记录这次绘图时间 num++; } // 消息循环 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int i; switch (message) { case WM_DESTROY: // 清理回收 DeleteDC(mdc); for(i=0;i<7;i++) DeleteObject(man[i]); ReleaseDC(hWnd,hdc); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
相信有了第一部分的例子,对于这部分的例子理解并不难。