C++短时间延迟实现及Windows平台Pong游戏帧率优化咨询
我正在通过使用windows.h库编写基础Pong游戏学习C++ Windows应用开发。但程序资源占用过高,因更新频率远超显示器刷新率,多数生成的帧无法显示。尝试用Sleep()函数延迟,但实际延迟约15ms,而理想需求是约5ms。使用timeBeginPeriod(1)和timeEndPeriod(1)提升时钟精度时出现编译错误。请问是否有其他方法小幅增加帧时间,或是将帧率设置为与显示器刷新率一致?
错误代码
#include <windows.h> int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){ timeBeginPeriod(1); while (game_running){ // main code goes here Sleep(1); } timeEndPeriod(1); }
编译错误
undefined reference to __imp_timeBeginPeriod 和 undefined reference to __imp_timeEndPeriod
1. 修复timeBeginPeriod的编译错误
timeBeginPeriod和timeEndPeriod属于winmm.lib库,编译时必须链接该库:
- 若使用VS编译器,在项目设置的「附加依赖项」中添加
winmm.lib; - 若使用g++编译,添加链接参数
-lwinmm。
另外注意:你提供的代码中timeBeginPeriod(1)和Sleep(1)缺少分号,这也会导致编译失败,需要补全。
2. 实现帧率与显示器同步(推荐方案)
比起手动加延迟,更合理的方式是让游戏帧率和显示器刷新率绑定,彻底避免无效渲染,同时降低资源占用,有两种可行方式:
方式一:开启垂直同步(VSync)
如果使用DirectX渲染,创建SwapChain时开启垂直同步选项,每次调用Present时会自动等待显示器垂直刷新,将帧率锁定到显示器刷新率。
方式二:手动计算帧间隔
通过高精度计时器计算每帧耗时,然后等待到目标帧时间,精准匹配显示器刷新率:
#include <windows.h> int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){ // 获取主显示器当前刷新率 DEVMODE dm; dm.dmSize = sizeof(DEVMODE); EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm); DWORD refreshRate = dm.dmDisplayFrequency; // 计算单帧目标耗时(毫秒) const DWORD targetFrameTime = 1000 / refreshRate; LARGE_INTEGER freq, start, end; QueryPerformanceFrequency(&freq); double ticksPerMs = (double)freq.QuadPart / 1000; while (game_running) { QueryPerformanceCounter(&start); // 游戏逻辑、渲染代码 // ... QueryPerformanceCounter(&end); DWORD elapsedMs = (DWORD)((end.QuadPart - start.QuadPart) / ticksPerMs); // 等待到目标帧耗时 if (elapsedMs < targetFrameTime) { Sleep(targetFrameTime - elapsedMs); } } return 0; }
3. 替代Sleep的高精度延迟方案
如果确实需要小于15ms的延迟,除了timeBeginPeriod,还可以用CreateWaitableTimer实现高精度等待(无需依赖winmm.lib):
void HighPrecisionSleep(DWORD ms) { HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL); LARGE_INTEGER li; li.QuadPart = -( (__int64)ms * 10000 ); // 转换为100纳秒单位的相对时间 SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE); WaitForSingleObject(timer, INFINITE); CloseHandle(timer); }
内容的提问来源于stack exchange,提问作者Daniel Wilson

