C++ 时间类问题

C++ 时间类问题,c++,winapi,timer,C++,Winapi,Timer,我正在设计一个时间类,它只在预定义的时间之后执行一个操作,但我仍然无法找到重置时间的方法。这是将时间重置为零还是暂时停止并恢复时间 所以,我的目标是每次满足条件A时重置时间,这样它就不会破坏delayTime函数,因为它仍然保留以前的时间,并最终导致错误的时间计算 if ( condition A ) { if ( time.delayTime( 5.0f ) ) { doActionA(); } } TimeClass.h #if

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

所以,我的目标是每次满足条件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

标准库

如前所述,只需使用。如果使用Visual C++编译器,则在HealSyrasePrimeCalk的分辨率上是实际的,它的强> > < /强>与QueRealExcCeCurter相同。

示例

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();
另一种方法是使用捕获状态和更新方法。正如我所看到的,你正在将其用于游戏,因此更新可以在游戏的更新方法中完成。假设事件具有类似{Active,Inactive}的状态,并且在经过的时间内有一个成员,那么每次调用update时,如果状态为Active,则添加自上次更新调用以来经过的时间。因此,ready函数只检查经过的时间是否大于定义的阈值

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;
    }
};
类事件{
std::chrono::高分辨率时钟::时间点lastUpdateCall;
双时差;
双延时;
布尔活跃;
公众:
事件(双时间间隔事件)
:timeBetweenEvents(timeBetweenEvents)
,lastUpdateCall(std::chrono::high_resolution_clock::now())
,elapsedTime(0.0)
,活动(真){}
bool ready(){
return elapsedTime>=事件之间的时间;
}
无效重置(){
elapsedTime=0.0;
}
无效设置活动(布尔状态){
活动=状态;
}
无效更新(){
auto currentTime=std::chrono::高分辨率时钟::now();
如果(活动){
auto dt=std::chrono::duration_cast(currentTime-lastUpdateCall).count();
elapsedTime+=dt;
}
lastUpdateCall=当前时间;
}
};

从您的问题来看,不太清楚您到底想要实现什么:时钟重置、暂停/恢复、间隔测量或定时触发器,或者所有这些。因此,我将描述所有这些

我只是想说清楚
  • 不要在成员函数(方法)中使用静态变量,这会使代码更难理解且容易出错。而是使用类成员变量(字段)
  • 不要以(毫秒)秒
    float
    格式存储时间。我的精确度太低了。不建议使用双倍
    double
    。相反,以API本机格式存储它:某种CPU标记:让我们将其包装在
    struct time\u point
    中,并仅在用户要求时转换为
    float

    struct time_point
    {
        LARGE_INTEGER value;
    };
    
您需要从底层API获得的函数包括:

  • 获取当前时间的函数:让我们调用它

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

(此外,您还可以将
时间点
设为类,并将此函数设为成员)

所有其他函数都可以是API不可知的

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

重置 还是要把时间重置为零

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

timeAtGameStart=0;
注释为不需要)

您刚才说的
start
now()
,所以
start
now()
之间的差异变为零

停止和恢复 还是暂时停止并恢复时间

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

  • 添加
    bool isPaused
    成员变量(不要忘记在构造函数中将其初始化为
    false
  • 添加
    timePaused
    成员变量(时间单位类型:
    time\u point
    ,在我的例子中)
  • 更改
    getGameTime()
    函数:暂停时,它将返回
    timePaused
    ,而不是当前时间
  • 当然,还引入了
    Pause()/Resume()
    函数
下面是示例代码(命名与您的略有不同)

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();
    }
}
设计秒表的另一种可能方法是:

  • 时钟继承
  • 将当前时间传递给
    IsAboutTime()
    以便无需
    Update()

快乐编码!=)

有一个标准的
标题,您正在使用。您可以添加暂停/播放功能。暂停保留暂停时间,播放添加(播放按时间-暂停时间)开始时间您能告诉我如何设计暂停和播放时间功能吗?我仍然不熟悉Windows API。你有Windows API解决方案吗?谢谢。你想用Windows API替换哪个部分?其中大部分是纯逻辑的,与API无关。只有通过直接使用QueryPerformanceCounter才能完成时间测量,就像您已经完成的那样。当然,您可以使用,但这主要是为在线程之间使用而设计的,因此不是作为
private:
      float Duration(const time_point& from, const time_point& to); // Here you divide by frequence
void TimeClass::reset()
{
    //timeAtGameStart = 0;
    timeAtGameStart = getGameTime();
}
    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()
    }
    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();
    }
}