VC++游戏编程之定时器
- 格式:doc
- 大小:386.50 KB
- 文档页数:11
一、定时器
事实上游戏本身几乎都是以无限循环的方式不断地在游戏窗口进行窗口画面的重绘操作,即使画面没有任何变化,这个重绘的操作还是会不断地进行,一直到游戏结束为止。
这一节将介绍如何利用WINDOWS本身的定时器及游戏中常用的游戏循环来制作游戏的动态效果。
Timer定时器对象可以再每隔一段时间发出一个WM_TIMER时间消息,程序收到消息后,就可以执行一些操作。比如,可以设置定时器来播放静态的连续图片,就可以产生动画效果。这也是通常显示动画的一种方式。
Windows API中有这样的函数:SetTimer()为我们定义一个定时器。函数原型:
1. UINT_PTR SetTimer(
2.
HWND hWnd, // 窗口句柄
3. UINT_PTR nIDEvent, // 定时器代号
4. UINT uElapse, // 时间设定的值,单位为毫秒
5. TIMERPROC lpTimerFunc // 定时器响应函数
6. );
这里举一个小例子:
1. SetTimer(hwnd, // 窗口句柄
2. IDT_TIMER1, // 代号
3. 10000, // 10秒
4. (TIMERPROC) NULL); // 没有响应函数
5.
6. SetTimer(hwnd,
7. IDT_TIMER2,
8. 5000,
9. (TIMERPROC) NULL);
10.
11. case WM_TIMER:
12. switch (wParam)
13. {
14. case IDT_TIMER1:
15. // 执行10秒的操作
16. return 0;
17. case IDT_TIMER2: 18. // 执行5秒的操作
19. return 0;
20. }
创建后自然需要删除定时器。KillTimer()就是用来终止某个定时器的
1. BOOL KillTimer(
2. HWND hWnd, // 窗口句柄
3. UINT_PTR uIDEvent // 定时器代号
4. );
运用定时器使预先做好的连续的静态图片播放,形成动画的效果。
新建Win32程序,在VC++6.0中默认使用默认的生成窗口的代码。
在大致的了解了定时器的使用方法后,接下来将会运用定时器使预先准备好的几张连续摆动的位图产生动画效果。如图。
接下来,在程序头部添加全局变量:
1. HBITMAP girl[7]; // 用于7张人物的位图数组
2. HDC mdc,hmdc;
3. int num; // 用于计数循环
在InitInstance()函数中添加代码:
1. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
2. {
3. HWND hWnd;
4. char filename[20] = ""; 5. int i;
6.
7. hInst = hInstance; // 将实例句柄存储在全局变量中
8.
9. hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
10. CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
11.
12. if (!hWnd)
13. {
14. return FALSE;
15. }
16.
17. MoveWindow(hWnd, 10, 10, 600, 450, true);
18. ShowWindow(hWnd, nCmdShow);
19. UpdateWindow(hWnd);
20.
21. hmdc = GetDC(hWnd);
22. mdc = CreateCompatibleDC(hmdc);
23.
24. //将7张位图载入
25. for (i = 0; i < 7; i++)
26. {
27. sprintf(filename, "girl%d.bmp", i);
28. girl[i] = (HBITMAP)LoadImage(NULL,filename,IMAGE_BITMAP,640,480,LR_LOADFROMFILE);
29. }
30.
31. num = 0;
32. SetTimer(hWnd, 1, 500, NULL); // 设定0.5秒的定时器
33.
34. MyPaint(hmdc);
35.
36. return TRUE; 37. }
WndProc()中添加WM_TIMER消息:
1. case WM_TIMER:
2. MyPaint(hmdc);
3. break;
程序退出是需要析构所有创建的对象:
1. case WM_DESTROY:
2. //删除和释放工作
3. DeleteDC(mdc);
4. ReleaseDC(hWnd, hmdc);
5. for (i = 0; i < 7; i++)
6. {
7. DeleteObject(girl[i]);
8. }
9. KillTimer(hWnd, 1);
10.
11. PostQuitMessage(0);
12. break;
MyPaint()函数的实现如下:
1. void MyPaint(HDC hdc)
2. {
3. if(num == 7)
4. num = 0;
5.
6. SelectObject(mdc,girl[num]);
7. BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY);
8.
9. num++; 10. }
这样,程序就算完成了。运行,会看到小女孩在摆动的动画。
我们可以加快定时器的速率,使动画看起来更加连贯。
二、游戏循环
在上例中,通过Timer来设定动画的帧来显示游戏,这事实上市仅仅使用在小型游戏中的方式。一般来说,游戏本身需要显示流畅的游戏画面,使玩家感觉不到延迟的状态。我们为了是游戏显示得更加顺畅,每秒钟必须更新画面25次以上。这一秒钟,我们还需要处理其他大量的运算和必须进行的消息的处理,甚至音效的输出等。而用定时器来驱动,往往得不到我们想要的画面效果,会产生画面显示不流畅和游戏响应时间过长的情况。这里提出了一种叫做“游戏循环”的概念。
游戏循环是将原先程序中的消息循环加以修改,方法是判断其中的内容目前是否要处理的消息,如果有则进行,否则按照设定的时间间隔来重绘画面。
这里借助一个例子讲解:
在WinMain函数中 [cpp]
1. //游戏循环
2. while (msg.message != WM_QUIT) //检测消息
3. {
4. if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
5. {
6. TranslateMessage(&msg);
7. DispatchMessage(&msg);
8. }
9. else
10. {
11. tNow = GetTickCount(); // 获得系统开启时间
12. if (tNow - tPre >= 40)
13. MyPaint(hdc); // 绘图函数
14. }
15. }
msg.message收到不是WM_QUIT消息时,进行运行循环。PeekMessage检测是否有处理消息(包括WM_QUIT)。没有消息就返回0,否则返回非0。值得注意的是,不能用GetMessage()取代PeekMessage()。
因为GetMessage()函数只有取得WM_QUIT消息时才返回0,其他时候时返回非0或--1(发生错误时)
此例子和上面的例子是同样的原理,显示一个动画。
第11行:GetTickCount(); 函数会取得系统开始运行到目前所经过的时间单位是百万分之一秒。在这里取得时间的主要目的是可以搭配接下来的判断是,用来调整游戏运行的速度,使得游戏不会因为运行计算机速度的不同而跑得太快或太慢。
第11~12行:在第12行的条件式中,“tPre”记录前次绘图的时间,而“tNow—tPre”则是计算上一次绘图到这次循环运行之间相差多少时间。这里设置若相差40个单位时间以上则再次进行绘图的操作,通过这个数值的控制可以调整游戏运行的快慢。
这里设定40个单位时间(1微妙)的原因,因为每隔40个单位进行一次绘图的操作,那么1秒中大约重绘窗口1000/40=25次,刚好可以达到期望值。
由于循环的运行速度远比定时器发出时间信号来得快,因此使用游戏循环可以更精确的掌控程序的运行速度并提高每秒钟画面重绘的次数。
运行后,显示的也是一个动画。本动画由七副静态图循环粘贴形成。即,BITMAP man[7]
整个程序如下:
[cpp] 1. #include
2. #include
3.
4. // 全局变量