怎样获得高精度计时器

 在Managed DirectX Kick Start第六章中作者用了一个Utility类来获取游戏中所需的精确时间间隔,但并没有对这个类是怎么实现的进行阐述,我在这里就对这个类的设计进行补充说明,顺便也了解一下在C#中如何调用Win32 API函数以及大师设计类的思想。
      这个类保存在Utility.cs文件中,它实现了一个能精确到1毫秒的计时器。当然.NET中没有这样的函数,因此我们需要使用非托管的Win32函数,在这之前别忘了在using从句中加入

 
      
using  System.Runtime.InteropServices; 
那么在让我们来看看我们要用到函数的原型
1.BOOL QueryPerformanceFrequency( [out]  LARGE_INTEGER *lpFrequency );
该函数位于kernel32.dll,用参数lpFrequency获得当前高精度执行计数器的频率(如果系统支持的话),否则其值为0,它在C#中的声明如下:
 
      
[System.Security.SuppressUnmanagedCodeSecurity]         
[DllImport(
" kernel32 " )]          
private   static   extern   bool  QueryPerformanceFrequency( ref   long  PerformanceFrequency);
2.BOOL QueryPerformanceCounter( [out]  LARGE_INTEGER *lpPerformanceCount );
该函数位于kernel32.dll,用参数lpPerformanceCount获得当前高精度执行计数器从开机到现在的计数值(如果系统支持的话),否则其值为0,它在C#中的声明如下:
 
      
[System.Security.SuppressUnmanagedCodeSecurity]          
[DllImport(
" kernel32 " )]         
private   static   extern   bool  QueryPerformanceCounter( ref   long  PerformanceCount);

3.DWORD timeGetTime(VOID); //当我们的系统不支持高精度时使用它,以使程序在不同机器上运行时获得统一的时间系统。
该函数位于winmm.lib,它返回Windows开机时到当前以毫秒为单位时间值。它在C#中的声明如下:
[System.Security.SuppressUnmanagedCodeSecurity]         
[DllImport(
" winmm.dll " )]       
public   static   extern   int  timeGetTime();
    首先声明了一个DirectXTimer枚举类以控制我们要实现的高精度时钟
public   enum  DirectXTimer    
{        
       Reset, 
//重置时钟        
       Start,  //开启时钟       
       Stop,  //暂停或停止时钟        
       Advance,  //使时钟提前0.1秒       
       GetAbsoluteTime,  //获得开机到当前的时间        
       GetApplicationTime, //获得最近一次时钟开启到当前的时间间隔        
       GetElapsedTime //获得两次调用间的时间间隔   
}
;

这样做的好处是在下面的设计中只需要构造一个函数,使代码看起来更见解明了
接着让我们来好好理解一下所需要的静态成员变量,它们中大部分的含义从名字上就可以看出
我在这里只对一些含义不那么明了的量做一些解释。
// 以下以time结尾都是时刻值        
private   static   bool  isTimerInitialized  =   false ;        
private   static   bool  m_bUsingQPF  =   false ;        
private   static   bool  m_bTimerStopped  =   true ;        
private   static   long  m_llQPFTicksPerSec  =   0 ;        
private   static   long  m_llStopTime  =   0 ;        
private   static   long  m_llLastElapsedTime  =   0 ;        
private   static   long  m_llBaseTime  =   0 // 高精度时钟启动的时刻        
// 以下是不使用高精度时钟时的相应变量        
private   static   double  m_fLastElapsedTime  =   0.0 ;        
private   static   double  m_fBaseTime  =   0.0 ;        
private   static   double  m_fStopTime  =   0.0 ;
这样理解下面的实现代码就轻松多了,整个Utility只有一个函数Timer
public   static   float  Timer(DirectXTimer command)        
{            
        
if (!isTimerInitialized)            
        
{               
              isTimerInitialized 
= true;               
              
// Use QueryPerformanceFrequency() to get frequency of timer.  If QPF is                
              
// not supported, we will timeGetTime() which returns milliseconds.                
              long qwTicksPerSec = 0;                
              m_bUsingQPF 
= QueryPerformanceFrequency(ref qwTicksPerSec);                
              
if (m_bUsingQPF)                    
                  m_llQPFTicksPerSec 
= qwTicksPerSec;            
        }
            
        
if (m_bUsingQPF)            
        
{                
              
double time;                
              
double fElapsedTime;                
              
long qwTime = 0;                              
              
if (m_llStopTime != 0 && command != DirectXTimer.Start && command != DirectXTimer.GetAbsoluteTime)                           
                     qwTime 
= m_llStopTime;  //大部分正确使用的情况下,这个if条件不会被足                                                         
                                                             
//这样做可增强程序的健壮性,以应付特殊情况                
              else                    
                    QueryPerformanceCounter(
ref qwTime);//取得该函被调用时的计数值                
              
// Return the elapsed time                
              if (command == DirectXTimer.GetElapsedTime)                
              
{                    
                    fElapsedTime 
= (double) (qwTime - m_llLastElapsedTime) / (double) m_llQPFTicksPerSec; 
                    m_llLastElapsedTime 
= qwTime;                    
                    
return (float)fElapsedTime;                
              }
                        
              
// Return the current time                
              if (command == DirectXTimer.GetApplicationTime)                
              
{                    
                   
double fAppTime = (double) (qwTime - m_llBaseTime) / (double) m_llQPFTicksPerSec;                    
                   
return (float)fAppTime;                
              }
                        
              
// Reset the timer                
              if (command == DirectXTimer.Reset)                
              
{                    
                    m_llBaseTime 
= qwTime;                    
                    m_llLastElapsedTime 
= qwTime;                   
                    m_llStopTime 
= 0;                    
                    m_bTimerStopped 
= false;                    
                    
return 0.0f;                
               }
                        
               
// Start the timer                
               if (command == DirectXTimer.Start)                
               
{                    
                    
if (m_bTimerStopped)                        
                         m_llBaseTime 
+= qwTime - m_llStopTime;                    
                    m_llStopTime 
= 0;                    
                    m_llLastElapsedTime 
= qwTime;                    
                    m_bTimerStopped 
= false;                    
                    
return 0.0f;                
                }
                        
                
// Stop the timer                
                if (command == DirectXTimer.Stop)                
                
{                    
                     
//这个判断看上去很多于但可以处理错误的调用,初学者应该从中得到启示                    
                     if (!m_bTimerStopped)                    
                     
{                        
                           m_llStopTime 
= qwTime;                        
                           m_llLastElapsedTime 
= qwTime;                        
                           m_bTimerStopped 
= true;                    
                     }
                   
                     
return 0.0f;                
                }
                        
                
// Advance the timer by 1/10th second                
                if (command == DirectXTimer.Advance)                
                
{                    
                     m_llStopTime 
+= m_llQPFTicksPerSec/10;                    
                     
return 0.0f;                
                }
                
                
if (command == DirectXTimer.GetAbsoluteTime)                
                
{                    
                      time 
= qwTime / (double) m_llQPFTicksPerSec;                    
                      
return (float)time;                
                 }
                
                 
return -1.0f// Invalid command specified            
        }
            
        
//这里开始是没有使用高精度时钟的代码,实现方法和上面几乎一样            
        else            
        
{                
                 
// Get the time using timeGetTime()                
                 double time;                
                 
double fElapsedTime;                            
                 
// Get either the current time or the stop time, depending                
                 
// on whether we're stopped and what command was sent                
                 if (m_fStopTime != 0.0 && command != DirectXTimer.Start && command != DirectXTimer.GetAbsoluteTime)                    
                       time 
= m_fStopTime;                
                 
else                    
                       time 
= timeGetTime() * 0.001;                        
                 
// Return the elapsed time                
                 if (command == DirectXTimer.GetElapsedTime)                
                 
{                       
                       fElapsedTime 
= (double) (time - m_fLastElapsedTime);                    
                       m_fLastElapsedTime 
= time;                    
                       
return (float) fElapsedTime;                
                 }
                        
                 
// Return the current time                
                 if (command == DirectXTimer.GetApplicationTime)                
                 
{                    
                       
return (float) (time - m_fBaseTime);                
                 }
                        
                 
// Reset the timer                
                 if (command == DirectXTimer.Reset)                
                 
{                    
                       m_fBaseTime 
= time;                    
                       m_fLastElapsedTime  
= time;                    
                       m_fStopTime 
= 0;                    
                       m_bTimerStopped 
= false;                    
                       
return 0.0f;                
                 }
                        
                 
// Start the timer                
                 if (command == DirectXTimer.Start)                
                 
{                    
                       
if (m_bTimerStopped)                        
                             m_fBaseTime 
+= time - m_fStopTime;                    
                       m_fStopTime 
= 0.0f;                    
                       m_fLastElapsedTime  
= time;                    
                       m_bTimerStopped 
= false;                    
                       
return 0.0f;                
                 }
                        
                 
// Stop the timer                
                 if (command == DirectXTimer.Stop)                
                 
{                    
                       
if (!m_bTimerStopped)                    
                       
{                        
                             m_fStopTime 
= time;                        
                             m_fLastElapsedTime  
= time;                        
                             m_bTimerStopped 
= true;                    
                       }
                    
                       
return 0.0f;                
                 }
                        
                 
// Advance the timer by 1/10th second                
                 if (command == DirectXTimer.Advance)                
                 
{                    
                       m_fStopTime 
+= 0.1f;                    
                       
return 0.0f;                
                 }
                
                 
if (command == DirectXTimer.GetAbsoluteTime)                
                 
{                   
                       
return (float) time;                
                 }
                
                 
return -1.0f// Invalid command specified            
        }
        
}


其实在DirectX的实例框架程序中也有个实现高精度时钟的类,它位于SDK Root/Samples/Managed/Common/dxmutmisc.cs或wdxmutmisc.cs(.NET2.0)中其名为FrameworkTimer,在理解了上面的Ulitity.Timer后相信理解FrameworkTimer也会变得简单许多吧

你可能感兴趣的:(游戏编程)