时间类问题

时间:2013-10-04 11:35:45

标签: c++ winapi timer

我正在设计一个时间类,它只会在预定义的时间后执行操作,但我仍然无法找到重置时间的方法。无论如何将时间重置为零或暂时停止并恢复时间?

所以,我的目标是每次条件A遇到时重置时间,以便它不会搞砸delayTime函数,它仍然保持前一次并结束错误的时间计算。

if ( condition A )
{
    if ( time.delayTime( 5.0f ) )
    {           
        doActionA();
    }
}

TimeClass.h

#ifndef _TIMECLASS_H_
#define _TIMECLASS_H_

#include <windows.h>


class TimeClass
{
public:
    TimeClass();
    ~TimeClass();
    bool delayTime( float second );
    float getSecond();
    void reset();

private:
    float getGameTime();

    float currentTime;
    UINT64 ticks;
    float time;
    float  timeAtGameStart;
    UINT64 ticksPerSecond;
};

#endif

TimeClass.cpp

#include "TimeClass.h"


TimeClass::TimeClass()
{
    // We need to know how often the clock is updated
    if( !QueryPerformanceFrequency((LARGE_INTEGER *)&ticksPerSecond) )
        ticksPerSecond = 1000;

    // If timeAtGameStart is 0 then we get the time since
    // the start of the computer when we call GetGameTime()
    timeAtGameStart = 0;
    timeAtGameStart = getGameTime();
}


float TimeClass::getGameTime()
{
    // This is the number of clock ticks since start
    if( !QueryPerformanceCounter((LARGE_INTEGER *)&ticks) )
        ticks = (UINT64)timeGetTime();

    // Divide by frequency to get the time in seconds
    time = (float)(__int64)ticks/(float)(__int64)ticksPerSecond;

    // Subtract the time at game start to get
    // the time since the game started
    time -= timeAtGameStart;

    return time;
}


bool TimeClass::delayTime( float second )
{
    currentTime = getGameTime();
    static float totalTime = second + getGameTime();

    if ( currentTime >= totalTime )
    {
        totalTime = second + getGameTime();
        return true;
    }
    else
    {
        return false;
    }
}


float TimeClass::getSecond()
{
    currentTime = getGameTime();
    static float totalTime = 1 + getGameTime();

    if ( currentTime >= totalTime )
    {
        totalTime = 1 + getGameTime();
        return currentTime;
    }
    else
    {
        return currentTime;
    }
}


void TimeClass::reset()
{
    timeAtGameStart = 0;
    timeAtGameStart = getGameTime();
}


TimeClass::~TimeClass()
{
}

2 个答案:

答案 0 :(得分:2)

标准图书馆

如上所述,只需使用std::chrono::high_resolution_clock即可。如果使用Visual C ++编译器,则high_resolution_clock的分辨率实际为bug report,而 NOT 与QueryPerformanceCounter相同。

示例

auto t1 = std::chrono::high_resolution_clock::now();
// Do something
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
double dt = duration.count();

<强> TimeClass

您现在面临的主要问题是您使用的是静态变量。您正在寻找一种方法来重置除delayTime之外的另一个函数的静态变量。如果你要引入成员变量去除静态变量,它会使事情变得容易很多(注意:函数中的静态变量是不好的做法,因为它们保持状态=&gt;对多线程不好)。那么这样的事情呢?

class Event {
    std::chrono::high_resolution_clock::time_point lastEventOccurred;
    double timeBetweenEvents;
public:
    Event(double timeBetweenEvents)
       : timeBetweenEvents(timeBetweenEvents)
       , lastEventOccurred(std::chrono::high_resolution_clock::now()) { }

    bool ready() {
       auto currentTime = std::chrono::high_resolution_clock::now();
       auto elapsedTime = std::chrono::duration_cast<std::chrono::duration<double>>(currentTime - lastEventOccurred).count();

       if(elapsedTime > timeBetweenEvents) {
           reset();
           return true;
       } else
           return false;
    }

    void reset() {
       lastEventOccurred = std::chrono::high_resolution_clock::now();
    }
};

用法:

Event evtDelay = Event(5.0);

if(condition A) {
    if(evtDelay.ready()) {
        doActionA();
    }
} else if(condition B) {
    evtDelay.reset();
}

另一种方法是使用捕获状态和更新方法。我可以看到你正在将它用于游戏,因此可以在游戏的更新方法中进行更新。假设事件将具有{Active,Inactive}状态和已用时间的成员,则每次调用update时,如果状态为Active,则添加自上次更新调用以来经过的时间。因此,就绪功能仅检查经过的时间是否大于定义的阈值。

class Event {
    std::chrono::high_resolution_clock::time_point lastUpdateCall;
    double timeBetweenEvents;
    double elapsedTime;
    bool active;
public:
    Event(double timeBetweenEvents)
       : timeBetweenEvents(timeBetweenEvents)
       , lastUpdateCall(std::chrono::high_resolution_clock::now()) 
       , elapsedTime(0.0) 
       , active(true) { }

    bool ready() {
       return elapsedTime >= timeBetweenEvents;
    }

    void reset() {
       elapsedTime = 0.0;
    }

    void setActive(bool state) { 
       active = state; 
    }

    void update() {
       auto currentTime = std::chrono::high_resolution_clock::now();

       if(active) {
          auto dt = std::chrono::duration_cast<std::chrono::duration<double>>(currentTime - lastUpdateCall).count();
          elapsedTime += dt;
       }

       lastUpdateCall = currentTime;
    }
};

答案 1 :(得分:1)

从您的问题来看,目前还不清楚您想要实现的目标:时钟重置,暂停/恢复,间隔测量或定时触发,或所有这些。所以我将描述所有这些。 =)

只是为了说清楚

  • 不要在成员函数(方法)中使用静态变量,这会使您的代码更难理解且容易出错。而是使用类成员变量(字段)
  • 不要以(毫秒)float格式存储时间。我的精度太低了。甚至不建议使用double。相反,将其存储为API原生格式:某种CPU滴答:让我们将其包装在struct time_point中,并仅在用户请求时转换为float

    struct time_point
    {
        LARGE_INTEGER value;
    };
    

您需要从基础API获得的功能是:

  • 获取当前时间的功能:让我们称之为

    private: 
         time_point now(); // Here you wrap QueryPerformanceCounter() or `boost::chrono::...::now();` stuff
    
  • 用于重新解释两个time_point到可用单位之间的差异(持续时间)(例如纳秒long long(毫秒)float

    private:
          float Duration(const time_point& from, const time_point& to); // Here you divide by frequence
    

(另外,你可以让time_point类成为这个函数的成员)

所有其他功能都可以与API无关。

遵循此设计,您可以从用户中抽象出底层API,并使您的应用程序更具可移植性。

重置

  

无论如何都要将时间重置为零

但是,等等,你已经实现了“将时间重置为零”:

void TimeClass::reset()
{
    //timeAtGameStart = 0;
    timeAtGameStart = getGameTime();
}

timeAtGameStart = 0;评论,因为它不需要)

您只是说startnow(),因此startnow()之间的差异变为零。

停止并恢复

  

或暂时停止并恢复时间?

您可以通过以下方式实现“时间停止/恢复”效果

  • 添加bool isPaused成员变量(不要忘记在构造函数中将其初始化为false
  • 添加timePaused成员变量(您的时间单位的类型:time_point在我的情况下)
  • 更改getGameTime()功能:暂停时会返回timePaused而不是当前时间
  • 当然还有Pause()/Resume()个功能

以下是示例代码(命名与您的名称略有不同)

    void Pause()
    {
        if (!isPaused)
        {
            timePaused = clock::now();
            isPaused = true;
        }
    }

    void Resume()
    {
        if (isPaused)
        {
            isPaused = false;
            start -= clock::now() - timePaused;
        }
    }

    float Now() const 
    { 
        if (isPaused)
        {
            return Duration(start, timePaused);
        }
        return Duration(start, clock::now()); // basically it is your getGameTime()
    }

Delta(基本区间测量)

有时您会想知道程序中某些地方之间经过了多长时间。例如,要在游戏中测量FPS,您需要知道帧之间的增量时间。 为实现这一点,我们需要成员:

    time_point prevTick;    // time of previous tick
    time_point currTick;    // time of current tick

            void Tick() // user must call it each frame
    { 
        prevTick = currTick;
        currTick = clock::now();
    }

    const float GetDelta() const  // user must call it to know frame time
    { 
        return Duration(prevTick, currTick);
    }

秒表(高级间隔测量)

现在,我们需要一些定时触发器。将此触发器与计时器分开是个好主意,所以:

  • 您可以使用不同的启动时间和不同的触发间隔进行多次触发
  • 所有这些触发器必须彼此独立
  • 全局时钟不会被更改,因此所有触发器仍然有效

所以让我们将它包装到课堂上:

class Stopwatch
{
private:
    float m_Interval;
    float m_Time;
    float m_PrevTime;
public:
    Stopwatch(float interval_ms = 1000.0f) :
        m_Interval(interval_ms),
        m_Time(0.0f),
        m_PrevTime(m_Time)
    {}

    float GetRefreshInterval() const { return m_Interval; }
    void SetRefreshInterval(float interval_ms) { m_Interval = interval_ms; }

    // Update as frequent as possible
    inline void Update(float dt)
    {
        m_Time += dt;
    }

    // Check if time interval reached
    inline bool IsAboutTime()
    {
        if (m_Time >= m_Interval)
        {
            m_PrevTime = m_Time;
            m_Time = 0.0f;
            return true;
        }
        return false;
    }

    // exact value of last interval
    inline float GetLastTime() const
    {
        return m_PrevTime;
    }
};

用法示例:

Stopwatch stopwatch(5000.0f); // will trigger each 5 seconds 
...

stopwatch.Update(timer.GetDelta()); // GetDelta() returns time difference between update frames
if ( condition A )
{
    if ( stopwatch.IsAboutTime() )
    {           
        doActionA();
    }
}

设计秒表的另一种可能方法是:

  • 继承自clock
  • 将当前时间传递给IsAboutTime(),因此无需Update()

快乐的编码! =)