0.前言
锁帧对于游戏而言至关重要。如果不锁帧,游戏可能会运行的太快或者太慢,会影响到动画效果、位移等效果。
1.使用定时器
使用定时器设定ID以及延迟的时间。该定时器到时间将发送WM_TIEMR消息。
定时器函数SetTimer():1
2
3
4
5// 如果创建定时器失败,将返回0
UNIT SetTimer(HWND hwnd, // 窗口句柄
UINT nIDEvent, // 定时器事件ID
UINT nElapse, // 延迟时间(毫秒)
TIMEPROC lpTimerFunc); // 接收到时间的callback函数
其中,可以单独指定lpTimerFunc。如果不指定的,就只能用WinProc回调中的WM_Timer消息进行接收。
WM_TIMER消息中的wparam参数中包含定时器的ID,以便区分多个定时器。
最后,当一个定时器不再使用的时候,使用KillTimer()来删除。1
2BOOL KillTimer(HWND hwnd,
UINT uIDEvent); // 定时器事件ID
问题:
定时器尽管用毫秒作为延时,但是实际上很难精确到毫秒,因此是不准确的。
2.锁帧
使用定时程序锁定帧频。
使用查询系统时钟,然后运行差分测试来检查过去了多少时间。
GetTickCount()返回从Windows启动后的毫秒数:1
DWORD GetTickCount(void);
假设fps为30帧,那么,每次循环大约是33毫秒
1.锁帧方法一
1 | while(true) |
2.锁帧方法二
使用Sleep函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15while(true)
{
DWORD start_time=GetTickCount();
// msg相关
// ...
// 游戏主函数
Game_Main();
// 等待剩余时间
DWORD delta = GetTickCount() - start_time;
if (delta < 33)
{
Sleep(33 - delta);
}
}
注意:如果设置fps太低,比如每帧1000ms,一些消息事件就无法处理了。(当然一般也不会这么低的fps…)
注意:以上的方法都会导致,在空循环或者休眠的时间内不处理任何其他事情。
还可以用一下方法进行改进:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34DWORD start_time;
bool mainRuned = false;
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// 锁帧方式 3
if(!mainRuned)
{
mainRuned = true;
start_time = GetTickCount();
Game_Main(hwnd);
}
else
{
DWORD delta = GetTickCount() - start_time;
if (delta >= MS_PER_FRAME)
{
mainRuned = false;
}
}
// 运行其他功能
// ...
}
最后:函数总结
- SetTimer() : 设置定时器
- KillTimer() : 删除定时器
- GetTickCount() : 查询系统时钟
- Sleep() : 线程休眠