05/19/2020
性能计时器
目的
要正确的实现动画效果,我们必须记录时间,尤其是要精确测量动画帧之间的时间间隔。当帧速率高时,帧之间的时间间隔就会很短,所以需要一个高精确度计时器。
应用
- 平滑移动
- 动画
说明1:高精度
例子1:
#include <window.h> //WIN32 计时器
__int64 currCounts; //高精度计时器需要精度高的:64位
QueryPerformanceCounter((LARGE_INTEGER*)&currCounts); //单位:计数,获得当前每秒的计数,又叫频率
double mSecondsPerCount = 1.0 / (double)currCounts; // 单位:秒,每次计数的时间长度
例子2:
__int64 A =0;
QueryPerformanceCounter((LARGE_INTEGER*)&A);
__int64 B =0;
QueryPerformanceCounter((LARGE_INTEGER*)&B);
double deltaTime = (B-A)* mSecondPerCount; //两个时间点间隔时间,单位:秒
实现计时器
GameTimer 类头文件
class GameTimer
{
public:
GameTimer();
float getTotalTime()const; //总游戏时间
float getDeltaTime()const; //帧间隔时间
void reset(); //在消息循环之前调用,重置
void start(); //取消暂停时调用
void stop(); //暂停
void tick(); //每一帧的时候调用
private:
double mSecondsPerCount;
double mDeltaTime;
__int64 mBaseTime; //起点
__int64 mPausedTime; //暂停多久(start-stop)
__int64 mStopTime; //暂停点
__int64 mPrevTime; //上一次时间
__int64 mCurrTime; //当前时间
bool mIsStopped;
};
实现
计算DeltaTime:Tick函数
当渲染动画帧时,我们必须知道帧之间的时间间隔,以使我们根据逝去的时间长度来更新游戏中的物体。
注意:至少要达到每秒30帧的频率才能等到比较平滑的动画效果
平滑:物体移动没有卡顿的效果,连贯性!
实现
void GameTimer::Tick()
{
if(mIsStopped)
{
mDeltaTime = 0.0;
return;
}
__int64 currTime;
QueryPerformance((LARGE_INTEGER*)& currTime);
mCurrTime = currTime;
//Delta t
mDeltaTime = (mCurrTime-mPrevTime)*mSecondsPerCount;
mPrevTime = mCurrTime;
//确保不是负值,DXSDK中的CDXUTTimer提到,如果处理器进入了节点模式
//或切换到另一个处理器,mDeltaTime会变成负值
if(mDeltaTime < 0)
{
mDeltaTime = 0.0;
}
}
游戏主循环调用Tick
int D3DApp::run() //游戏主循环
{
// ....
mTimer.reset(); //GameTime类
while(/*直到窗口被关闭*/)
{
// ...window消息接受,处理消息
//游戏进行时
mTimer.tick(); //计算DeltaTime
if(!mAppPaused)
{
calculateFrameStats(); //计算每秒帧数并在窗口显示信息
updateScene(mTimer.getDeltaTime()); //每帧更新
drawScene(); //每帧绘制
}else{
sleep(100);
}
}
//... return
}
帧的统计数值 calculateFrameStats
- 计算每秒帧速,并计算每一帧渲染需要多少时间。FPS
- 计算某一个时间段t中处理的总帧数。 , 假设t=1,fps = n; 表示每秒总帧数
void D3DApp::calculateFrameStats()
{
static int frameCount = 0; //记录每秒的总帧数,每1秒之后,需要重置
static float timeElapsed = 0.0f;
frameCount++; // 帧总计数
//计算1s的平均值
if((mTimer.getTotalTime() - timeElapsed) >=1.0f)
{
float fps = (float)frameCount; //fps = frameCount / 1sec
float mspf = 1000.0f /fps; //每秒帧数
//windows cout
//cout << fps << mspf;
//重置时间
frameCount = 0;
timeElapsed += 1.0; //逝去的时间,每次1秒
}
}
reset
void GameTimer::reset()
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mBaseTime = currTime;
mPrevTime = currTime;
mStopTime = 0;
mIsStopped = false;
}
游戏时间
从应用程序开始运行时经过的时间总量,不包括暂停时间,这个叫做游戏时间。
用途
- 计时游戏:假如玩家有300秒完成一个关卡。关卡开始时间有一个 , 判断玩家是否超时。
- 灯光移动:不考虑暂停时间,不然动画移动会瞬间移动到其他位置
实现
成员变量
- mBaseTime:初始化为当前时间
- mPausedTIme: 累计游戏的暂停时间
- mStopTime:记录暂停时间
成员函数
stop
void GameTick::stop()
{
if(!mIsStopped)
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mStopTime = currTime; //记录暂停时间
mIsStopped = true;
}
}
start
void GameTimer::start()
{
__int64 startTime;
QueryPerformanceCounter((LARGE_INTEGER*)&startTime);
if(mIsStopped)
{
/*
|pauseTime |
--------|-----------|------------ |----------------->
BaseTime stopTime startTime
*/
mPausedTime += (startTime-mStopTime); //累计暂停时间
mPrevTime = startTime;
mStopTime = 0;
mIsStopped = false; //取消暂停状态
}
}
getTotalTime
返回了自调用Reset之后经过的时间总量,其中不包括暂停时间
float GameTimer::getTotalTime()const
{
/* 多重暂停,暂停部分是不被考虑到总时间的
|paused Time |
--------|-----------|------------ |----------|----------------|---->
BaseTime stopTime startTime stopTime currTime
*/
if(mIsStopped)
{
// | 区别 |
return (float)(((mStopTime-mPausedTime)-mBaseTime)*mSecondsPerCount);
}else
{
/*
|pauseTime |
--------|-----------|------------ |-----------|--------------->
BaseTime stopTime startTime currTime
*/
return (float)(((mCurrTime-mPausedTime)-mBaseTime)*mSecondsPerCount);
}
}
Introduction to 3D Game Progrmming with DirectX11 第四章