性能计时器所用的时间单位叫做计数。QueryPerformanceCounter函数获得性能计时器测量的当前时刻值,返回64位整数
QueryPerformanceFrequency函数获取计时器的频率 (计数/秒)
每个计数所代表的秒数就是性能计时器频率的倒数,由此可以得出将计数转换为秒的方法
注意:在不同的处理器上同时调用QueryPerformanceCounter可能得到不同结果,因此我们可以使用SetThreadAffinityMask函数,防止应用程序的主程序切换到其他处理器,此时调用两次QueryPerformanceCounter就能得到正确的计数差值。
实现GameTimer类
构造函数:
GameTimer::GameTimer():mSecondsPerCount(0.0),mDeltaTime(-1.0),mBaseTime(0),mPausedTime(0),mPrevTime(0),mCurrTime(0),mStopped(false)
{
__int64 countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec); //检索性能计数器的频率(计数/秒)
mSecondsPerCount = 1.0 / (double)countsPerSec; //计数代表的秒数,就是性能计时器频率的倒数(秒/计数)
}
渲染动画帧时,需要知道每帧之间的时间间隔,以此来根据时间的流逝对游戏对象进行更新。
void GameTimer::Tick()
{
if (mStopped)
{
mDeltaTime = 0.0;
return;
}
//获得本帧开始显示的时刻
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mCurrTime = currTime;
//本帧与前一帧的时间差
mDeltaTime = (mCurrTime - mPrevTime) * mSecondsPerCount;
//准备计算本帧与下一帧的时间差
mPrevTime = mCurrTime;
//使时间差为非负值
if (mDeltaTime < 0.0)
{
mDeltaTime = 0.0;
}
}
void GameTimer::Reset()
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mBaseTime = currTime;
mPrevTime = currTime; //把前一帧的值初始化为当前时刻,因为在第一帧动画之前没有动画,所以要在消息循环开始之前初始化此值,不然会是乱码
mStopTime = 0;
mStopped = false;
}
把前一帧的值初始化为当前时刻,因为在第一帧动画之前没有动画,所以要在消息循环开始之前初始化此值,不然会是乱码
总时间:从应用程序执行开始,不计其中暂停的时间的总和。
float GameTimer::TotalTime()const
{
if (mStopped) //如果现在是暂停状态,那现在暂停的这段时间可以不计,然后排除之前暂停的时间即可
{
return (float)(((mStopTime - mPausedTime) - mBaseTime) * mSecondsPerCount);
}
else //不是暂停状态,那排除之前暂停的时间即可
{
return (float)(((mCurrTime - mPausedTime) - mBaseTime) * mSecondsPerCount);
}
}
#pragma once
#ifndef GAMETIMER_H
#define GAMETIMER_H
class GameTimer
{
public:
GameTimer();//构造函数
float TotalTime()const;//用秒作为单位,返回除去暂停时间的总时间,
float DeltaTime()const;//用秒作为单位,返回两帧之间的时间差
void Reset();//开始消息循环之前调用
void Start();//解除计时器暂停时调用
void Stop(); //暂停计时器时调用
void Tick(); //每帧调用
private:
double mSecondsPerCount ; //计数代表的秒数
double mDeltaTime; //两帧之间的时间差
__int64 mBaseTime; //基础时刻,应用程序开始运行的时刻
__int64 mPausedTime; //暂停时间,应用程序暂停的时间和
__int64 mStopTime; //暂停时刻,应用程序上一次暂停的时刻
__int64 mPrevTime; //上一帧的时刻
__int64 mCurrTime; //现在的时刻
bool mStopped; //暂停状态标记
};
#endif
#include <Windows.h>
#include "GameTimer.h"
GameTimer::GameTimer():mSecondsPerCount(0.0),mDeltaTime(-1.0),mBaseTime(0),mPausedTime(0),mPrevTime(0),mCurrTime(0),mStopped(false)
{
__int64 countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec); //检索性能计数器的频率(计数/秒)
mSecondsPerCount = 1.0 / (double)countsPerSec; //计数代表的秒数,就是性能计时器频率的倒数(秒/计数)
}
void GameTimer::Tick()
{
if (mStopped)
{
mDeltaTime = 0.0;
return;
}
//获得本帧开始显示的时刻
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mCurrTime = currTime;
//本帧与前一帧的时间差
mDeltaTime = (mCurrTime - mPrevTime) * mSecondsPerCount;
//准备计算本帧与下一帧的时间差
mPrevTime = mCurrTime;
//使时间差为非负值
if (mDeltaTime < 0.0)
{
mDeltaTime = 0.0;
}
}
void GameTimer::Reset()
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mBaseTime = currTime;
mPrevTime = currTime; //把前一帧的值初始化为当前时刻,因为在第一帧动画之前没有动画,所以要在消息循环开始之前初始化此值,不然会是乱码
mStopTime = 0;
mStopped = false;
}
void GameTimer::Stop()
{
//如果已经是停止状态,那就什么都不做
if (!mStopped)
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);//获取当前时刻时间
//保存当前时刻值为停止之间,并设置标志为true,指示计时器已停止
mStopTime = currTime;
mStopped = true;
}
}
void GameTimer::Start()
{
__int64 startTime;
QueryPerformanceCounter((LARGE_INTEGER*)&startTime);
//如果暂停状态继续计时
if (mStopped)
{
mPausedTime += (startTime - mStopTime); //累加暂停时间
mPrevTime = startTime;
//不再是停止状态
mStopTime = 0;
mStopped = false;
}
}
float GameTimer::TotalTime()const
{
if (mStopped) //如果现在是暂停状态,那现在暂停的这段时间可以不计,然后排除之前暂停的时间即可
{
return (float)(((mStopTime - mPausedTime) - mBaseTime) * mSecondsPerCount);
}
else //不是暂停状态,那排除之前暂停的时间即可
{
return (float)(((mCurrTime - mPausedTime) - mBaseTime) * mSecondsPerCount);
}
}
float GameTimer::DeltaTime()const
{
return (float)mDeltaTime;
}