游戏中时间的封装

游戏中时间的封装

时钟类GE_TIMER可用来取得游戏已进行的时间,计算出两个时间点之间的时间片大小,从而可在某一时间点处,自动更新某些游戏状态。此外,还可用来获取程序的帧频FSP(Frame Per Second)大小,检验3D渲染的速度,即游戏速度。

Windows API函数timeGetTime用来取得游戏开始后的时间,返回的时间值单位为ms(毫秒)。

The timeGetTime function retrieves the system time, in milliseconds. The system time is the time elapsed since Windows was started.

DWORD timeGetTime(VOID);

Parameters

This function does not take parameters.

Return Values

Returns the system time, in milliseconds.

但是这个函数的精度只有10ms左右,如果需要采用更为精确的时间,可使用小于1ms时间精度的Windows API函数QueryPerformanceCounter和QueryPerformanceFrequency,这两个函数直接使用了Windows 内核的精度非常高的定时器。不同的硬件和操作系统,定时器的频率稍有不同。

The QueryPerformanceFrequency function retrieves the frequency of the high-resolution performance counter,
if one exists. The frequency cannot change while the system is running.

Syntax

BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

Parameters

lpFrequency
[out] Pointer to a variable that receives the current performance-counter frequency, in counts per second.
If the installed hardware does not support a high-resolution performance counter, this parameter can be zero.

Return Value

If the installed hardware supports a high-resolution performance counter, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError. For example,
if the installed hardware does not support a high-resolution performance counter, the function fails. 

The QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter. 

Syntax

BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);

Parameters

lpPerformanceCount
[out] Pointer to a variable that receives the current performance-counter value, in counts.

Return Value

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError. 

Remarks

On a multiprocessor computer, it should not matter which processor is called. However, you can get different results on
different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL).
To specify processor affinity for a thread, use the SetThreadAffinityMask function.

这两个函数都使用了结构体 LARGE_INTEGER,我们来看看它的结构:

The LARGE_INTEGER structure is used to represent a 64-bit signed integer value.

Note  Your C compiler may support 64-bit integers natively. For example, Microsoft® Visual C++® supports the __int64 sized integer type.
For more information, see the documentation included with your C compiler.

typedef union _LARGE_INTEGER
{
     struct {    DWORD LowPart;    LONG HighPart;  }; 
     struct {    DWORD LowPart;    LONG HighPart;  } u;
     LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;

Members

LowPart 
Low-order 32 bits.

HighPart 
High-order 32 bits.


LowPart 
Low-order 32 bits. 
HighPart 
High-order 32 bits.

QuadPart 
Signed 64-bit integer.

Remarks

The LARGE_INTEGER structure is actually a union. If your compiler has built-in support for 64-bit integers,
use the QuadPart member to store the 64-bit integer. Otherwise, use the LowPart and HighPart members to store the 64-bit integer.

看的出来,它实际上是1个联合体。

提示:要正确编译运行,需要链接winmm.lib。
由于本人水平有限,可能存在错误,敬请指出。

源码下载

好了,现在看看GE_COMMON.h的定义,主要用来包含公用的头文件和宏定义:

/* ************************************************************************************
 [Include File]

 PURPOSE: 
    Include common header files and common macro.
************************************************************************************
*/

#ifndef GAME_ENGINE_COMMON_H
#define  GAME_ENGINE_COMMON_H

#define  DIRECTINPUT_VERSION 0x0800   //  let compile shut up

#include 
< windows.h >
#include 
< tchar.h >
#include 
< string .h >
#include 
< stdio.h >

#include 
< d3d9.h >
#include 
< d3dx9.h >
#include 
< dinput.h >
#include 
< dsound.h >

//  defines for small numbers
#define  EPSILON_E3  (float)(1E-3)
#define  EPSILON_E4  (float)(1E-4)
#define  EPSILON_E5  (float)(1E-5)
#define  EPSILON_E6  (float)(1E-6)

#define  Safe_Release(object) if((object) != NULL) { (object)->Release(); (object)=NULL; }

#define  FCMP(a, b) (fabs((a) - (b)) < EPSILON_E3 ? 1 : 0)

#endif

由于浮点数不能直接比较大小,所以定义了1个宏来比较浮点数的大小。

#define  FCMP(a, b) (fabs((a) -& nbsp;(b)) < EPSILON_E3 ? 1& nbsp;: 0)

再来看看GE_TIMER.h的定义:

/* ************************************************************************************
 [Include File]

 PURPOSE: 
    Encapsulate system time for game.
************************************************************************************
*/

#ifndef GAME_ENGINE_TIMER_H
#define  GAME_ENGINE_TIMER_H

class  GE_TIMER
{
private :
    
bool  _use_large_time;                //  flag that indicate whether use large time

    __int64 _one_second_ticks;          
//  ticks count in one second
    __int64 _tick_counts_start;          //  tick counts at start count time

    unsigned 
long  _time_start;           //  start time for timeGetTime()

    
int  _frame_count;                    //  frame count number
     float  _fps;                          //  frame per second
     float  _time1, _time2, _time_slice;   //  time flag and time slice

public :
    GE_TIMER();
    
~ GE_TIMER();
    
void  Init_Game_Time();
    
float  Get_Game_Play_Time();
    
void  Update_FPS();

    
float  Get_FPS() {  return  _fps; }
};

#endif

并非所有系统都支持内核的定时器读取,因此要定义一个
_use_large_time来标志是否使用这个高精度的定时器,否则将使用timeGetTime函数进行时间计算。

我们来看看构造函数和析构函数的定义:

// ------------------------------------------------------------------------------------
//  Constructor, initialize game time.
// ------------------------------------------------------------------------------------
GE_TIMER::GE_TIMER()
{
    Init_Game_Time();
}

// ------------------------------------------------------------------------------------
//  Destructor, do nothing.
// ------------------------------------------------------------------------------------
GE_TIMER:: ~ GE_TIMER()

}

看的出来,构造函数只是调用了 Init_Game_Time来初始化游戏时间,而析构函数什么都不做。

再来看看
Init_Game_Time 的定义:

// ------------------------------------------------------------------------------------
//  Initialize game time.
// ------------------------------------------------------------------------------------
void  GE_TIMER::Init_Game_Time()
{
    _frame_count 
=   0 ;
    _fps 
=   0 ;
    _time1 
=  _time2  =  _time_slice  =   0 ;

    
if (QueryPerformanceFrequency((LARGE_INTEGER * & _one_second_ticks))
    {
        _use_large_time 
=   true ;
        QueryPerformanceCounter((LARGE_INTEGER
* & _tick_counts_start);
    }
    
else
    {
        _use_large_time 
=   false ;
        _time_start 
=  timeGetTime();
    }
}

我们使用Get_Game_Play_Time来取得当前的游戏时间,来看看它的定义:

// ------------------------------------------------------------------------------------
//  Get time has escaped since game start.
// ------------------------------------------------------------------------------------
float  GE_TIMER::Get_Game_Play_Time()
{
    __int64 current_tick_counts;

    
if (_use_large_time)
    {
        QueryPerformanceCounter((LARGE_INTEGER
* & current_tick_counts);
        
return  (( float ) (current_tick_counts  -  _tick_counts_start)  /  _one_second_ticks)  *   1000
    }

    
return  ( float )(timeGetTime()  -  _time_start);
}

分两种情况进行处理,如果使用高精度时钟,将计算开始和结束时钟计数之差,除以时钟频率,再乘以1000,即获得时间片大小,单位为 ms。
否则直接利用timeGetTime函数计算时间片大小。

更新帧频通过 Update_FPS函数来进行,每5帧更新一次。

// ------------------------------------------------------------------------------------
//  Update FPS.
// ------------------------------------------------------------------------------------
void  GE_TIMER::Update_FPS()
{
    
//  increment frame count by one
    _frame_count ++ ;

    
if (_frame_count  %   5   ==   1 )
        _time1 
=  Get_Game_Play_Time()  /   1000 ;
    
else   if (_frame_count  %   5   ==   0 )
    {
        _time2 
=  Get_Game_Play_Time()  /   1000 ;
        _time_slice 
=  ( float ) fabs(_time1  -  _time2);     //  calculate time escaped
    }

    
//  update fps
     if ( !  FCMP(_time_slice,  0.0 ))
        _fps 
=   5   /  _time_slice;
}

完整的GE_TIMER.cpp实现如下所示:

/* ************************************************************************************
 [Implement File]

 PURPOSE: 
    Encapsulate system time for game.
************************************************************************************
*/

#include 
" GE_COMMON.h "
#include 
" GE_TIMER.h "

// ------------------------------------------------------------------------------------
//  Constructor, initialize game time.
// ------------------------------------------------------------------------------------
GE_TIMER::GE_TIMER()
{
    Init_Game_Time();
}

// ------------------------------------------------------------------------------------
//  Destructor, do nothing.
// ------------------------------------------------------------------------------------
GE_TIMER:: ~ GE_TIMER()

}

// ------------------------------------------------------------------------------------
//  Initialize game time.
// ------------------------------------------------------------------------------------
void  GE_TIMER::Init_Game_Time()
{
    _frame_count 
=   0 ;
    _fps 
=   0 ;
    _time1 
=  _time2  =  _time_slice  =   0 ;

    
if (QueryPerformanceFrequency((LARGE_INTEGER * & _one_second_ticks))
    {
        _use_large_time 
=   true ;
        QueryPerformanceCounter((LARGE_INTEGER
* & _tick_counts_start);
    }
    
else
    {
        _use_large_time 
=   false ;
        _time_start 
=  timeGetTime();
    }
}

// ------------------------------------------------------------------------------------
//  Get time has escaped since game start.
// ------------------------------------------------------------------------------------
float  GE_TIMER::Get_Game_Play_Time()
{
    __int64 current_tick_counts;

    
if (_use_large_time)
    {
        QueryPerformanceCounter((LARGE_INTEGER
* & current_tick_counts);
        
return  (( float ) (current_tick_counts  -  _tick_counts_start)  /  _one_second_ticks)  *   1000
    }

    
return  ( float )(timeGetTime()  -  _time_start);
}

// ------------------------------------------------------------------------------------
//  Update FPS.
// ------------------------------------------------------------------------------------
void  GE_TIMER::Update_FPS()
{
    
//  increment frame count by one
    _frame_count ++ ;

    
if (_frame_count  %   5   ==   1 )
        _time1 
=  Get_Game_Play_Time()  /   1000 ;
    
else   if (_frame_count  %   5   ==   0 )
    {
        _time2 
=  Get_Game_Play_Time()  /   1000 ;
        _time_slice 
=  ( float ) fabs(_time1  -  _time2);     //  calculate time escaped
    }

    
//  update fps
     if ( !  FCMP(_time_slice,  0.0 ))
        _fps 
=   5   /  _time_slice;
}

你可能感兴趣的:(游戏中时间的封装)