在游戏编写中,我遇到一个现象:在连续按键的相应过程中,WM_TIMER消息一直没有得到相应。
查阅网上的资料后发现,WM_TIMER的消息属于最低优先级的消息,当线程的队列中没有其他消息时,才检索该消息。而且设置的定时器不精确(大概57ms左右的精度),所以会出现了延迟的结果。于是,我想看看有没有其他的定时器方式,大致有以下几个方式:
1.WM_TIMER:SetTimer,OnTimer,KillTimer这样的使用方式(最简单的);
2.等待定时器(WaitableTimer):
(1)一种是,多线程+WaitableTimer实现定时器功能(未使用响应函数的方法),相关代码如下:
int CWaitableTimerTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)(2)另外一种是,多线程+WaitableTimer实现定时器功能(使用响应函数的方法),相关代码如下:
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
// 创建定时器线程,并立即执行
m_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TimerThreadProc, (LPVOID)this, 0, &ThreadID);
return 0;
}
// 定时器线程执行函数
void TimerThreadProc(LPVOID lpArg)
{
// 多线程+WaitableTimer实现定时器功能(未使用定时响应函数版本)
CWaitableTimerTestView *pView = (CWaitableTimerTestView *)lpArg;
// 创建等待定时器
pView->m_hTimer = CreateWaitableTimer(NULL, FALSE, _T("MyWaitableTimer"));// 自动归零
if (NULL == pView->m_hTimer)
{
AfxMessageBox(_T("Can't create waitable timer!"));
return;
}
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -2000000LL;// 200ms=200*10^6ns=2*10^6(100ns)
// 设置等待定时器
if (FALSE == SetWaitableTimer(pView->m_hTimer, &liDueTime, 200, NULL, NULL, FALSE))
{
AfxMessageBox(_T("SetWaitableTimer Failed!"));
return;
}
while (pView->m_bTimerThreadRun)
{
if (WaitForSingleObject(pView->m_hTimer, INFINITE) != WAIT_OBJECT_0)
{
pView->MessageBox("WaitForSingleObject failed ");
}
else
{
pView->data++;
}
}
CancelWaitableTimer(pView->m_hTimer);
CloseHandle(pView->m_hTimer);
return;
}
// 定时器线程执行函数
void TimerThreadProc(LPVOID lpArg)
{
// 多线程+WaitableTimer实现定时器功能(使用定时响应函数版本)
CWaitableTimerTestView *pView = (CWaitableTimerTestView *)lpArg;
// 创建等待定时器
pView->m_hTimer = CreateWaitableTimer(NULL, FALSE, _T("MyWaitableTimer"));// 自动归零
if (NULL == pView->m_hTimer)
{
AfxMessageBox(_T("Can't create waitable timer!"));
return ;
}
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -2000000LL;// 200ms=200*10^6ns=2*10^6(100ns)
while (pView->m_bTimerThreadRun)
{
// 设置等待定时器
if (FALSE == SetWaitableTimer(pView->m_hTimer, &liDueTime, 200, (PTIMERAPCROUTINE)TimerAPCProc, (LPVOID)lpArg, FALSE))
{
AfxMessageBox(_T("SetWaitableTimer Failed!"));
return;
}
else// 如果没有,就不会调用TimerAPCProc
{
SleepEx(
INFINITE, // Wait forever
TRUE); // Put thread in an alertable state
}
}
CancelWaitableTimer(pView->m_hTimer);
CloseHandle(pView->m_hTimer);
return;
}
// 远程调用
VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
{
CWaitableTimerTestView *pView = (CWaitableTimerTestView*)lpArg;
if (WaitForSingleObject(pView->m_hTimer, INFINITE) != WAIT_OBJECT_0)
{
pView->MessageBox("WaitForSingleObject failed ");
}
else
{
pView->data++;
}
}
3.多线程+Sleep延时函数:(与2类似)
另一个线程一直运行,每个一定时间运行一次,Sleep(mSec);
注意此种方式在调用OnDraw(GetDC)方法时,会出现pDoc重复获取的assert不合法、闪烁及绘制错误等问题
// 定时器线程执行函数
void TimerThreadProc(LPVOID lpArg)
{
// 多线程+Sleep(msec)实现定时器功能
CWaitableTimerTestView *pView = (CWaitableTimerTestView *)lpArg;
while (pView->m_bTimerThreadRun)
{
pView->data++;
Sleep(200);
}
}
4.采用多线程+WM_TIMER消息混合方式:
多线程固定时间计算各图像元素的位置,而WM_TIMER通知定时绘图显示,在WM_KEYDOWN消息频繁时,由于WM_KEYDOWN处理过程中也要采用OnDraw方法重绘整个屏幕,故也可以达到定时移动背景和敌机相关的定时移动。5.采用多媒体定时器,更加精确。(还未深入研究过)
6.DirectX中的定时器(还未深入研究过)
以上是我目前为止查到能在MFC中使用的定时器方式,欢迎大家讨论补充,互相学习,共同进步!