Directx11学习笔记【七】 游戏定时器的实现
无论是在动画还是在一些游戏中,定时器总是必不可少的,游戏的帧数动画的播放等都离不开定时器的控制。这里以dx11龙书中提供的定时器为例,具体看看是怎么实现一个简单但精度高使用方便的定时器的。
这个定时器使用的windows中的高精度计时函数QueryPerformanceCounter和QueryPerformanceFrequency
BOOL QueryPerformanceCounter( LARGE_INTEGER* lpPerformanceCount );
BOOL QueryPerformanceFrequency( LARGE_INTEGER* lpFrequency );
QueryPerformanceCounter函数以计数为单位,因为不同的机器中两次计数之间的差值可能不同,所以就需要QueryPerformanceFrequency函数 返回1s内计数的个数
__int64 countsPerSec; QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec); m_secondsPerCount = 1.0 / countsPerSec;
通过这样便可以得到系统两次计数之间的时间差,这是定时器把时间转换为秒的基准。
下面看看这个定时器类GameTimer的具体实现
GameTimer.h
1 #ifndef GAMETIMER_H 2 #define GAMETIMER_H 3 4 class GameTimer 5 { 6 public: 7 GameTimer(); 8 float TotalTime() const;//返回定时器从计时开始到现在的总时间,不包括暂停时间 9 float DeltaTime() const;//返回本次计时与上次计时之间的时间差 10 11 void Reset();//重置 12 void Start();//暂停后恢复计时 13 void Stop();//暂停 14 void Tick();//计时一次,每一帧调用 15 16 private: 17 double m_secondsPerCount;//系统相关,系统两次计数之间的时间差,是定时器把时间转换为s的基准 18 double m_deltaTime;//距离上次计时的时间差 19 20 __int64 m_baseTime;//定时器开始工作的时间点 21 __int64 m_pausedTime;//暂停的总时间 22 __int64 m_stopTime;//暂停的开始时间 23 __int64 m_prevTime;//上次计时的时间点 24 __int64 m_currTime;//本次计时的时间点 25 26 bool m_isStopped;//是否暂停 27 }; 28 29 #endif //GAMETIMER_H
GameTimer.cpp
1 #include "GameTimer.h" 2 #include <windows.h> 3 4 GameTimer::GameTimer() : m_secondsPerCount(0.0), m_deltaTime(-1.0), m_baseTime(0), m_pausedTime(0), 5 m_prevTime(0), m_currTime(0), m_isStopped(false) 6 { 7 __int64 countsPerSec; 8 QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec); 9 m_secondsPerCount = 1.0 / countsPerSec; 10 } 11 12 13 float GameTimer::TotalTime() const 14 { 15 if (m_isStopped) 16 { 17 return static_cast<float>((m_stopTime - m_baseTime - m_pausedTime)*m_secondsPerCount); 18 } 19 else 20 { 21 return static_cast<float>((m_currTime - m_baseTime - m_pausedTime)*m_secondsPerCount); 22 } 23 } 24 25 float GameTimer::DeltaTime() const 26 { 27 return static_cast<float>(m_deltaTime); 28 } 29 30 void GameTimer::Tick() 31 { 32 if (m_isStopped) 33 { 34 m_deltaTime = 0.0; 35 return; 36 } 37 //得到这一帧的时间 38 __int64 currentTime; 39 QueryPerformanceCounter((LARGE_INTEGER*)¤tTime); 40 m_currTime = currentTime; 41 42 //两次计时之间的时间 43 m_deltaTime = (m_currTime - m_prevTime) * m_secondsPerCount; 44 45 m_prevTime = m_currTime; 46 47 if (m_deltaTime < 0.0) 48 m_deltaTime = 0.0; 49 } 50 51 void GameTimer::Reset() 52 { 53 __int64 currentTime; 54 QueryPerformanceCounter((LARGE_INTEGER*)(¤tTime)); 55 56 m_baseTime = currentTime; 57 m_prevTime = currentTime; 58 m_stopTime = 0; 59 m_isStopped = false; 60 } 61 62 void GameTimer::Start() 63 { 64 __int64 startTime; 65 QueryPerformanceCounter((LARGE_INTEGER*)&startTime); 66 67 if (m_isStopped) 68 { 69 //更新暂停的总时间 70 m_pausedTime += (startTime - m_stopTime); 71 72 m_prevTime = startTime; 73 74 //不再暂停 75 m_stopTime = 0; 76 m_isStopped = false; 77 } 78 } 79 80 void GameTimer::Stop() 81 { 82 if (!m_isStopped) 83 { 84 __int64 currentTime; 85 QueryPerformanceCounter((LARGE_INTEGER*)¤tTime); 86 87 m_stopTime = currentTime; 88 m_isStopped = true; 89 } 90 }
注释已经很详细了,类中函数和变量的作用也很明显。构造函数主要是获得根机器相关的变量,得到两次计数之间的时间差。
定时第一次工作是应调用Reset()初始化,以后在每一帧中调用Tick()更新计时器的各种变量。通过TotalTime()和DeltaTime()可以得到相关时间信息,用Stop()和Start()
分别暂停和恢复定时器。