267 lines
6.2 KiB
C++
267 lines
6.2 KiB
C++
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
|
|
|
#include "Time.h"
|
|
#include "EngineService.h"
|
|
#include "Engine/Core/Math/Math.h"
|
|
#include "Engine/Core/Config/TimeSettings.h"
|
|
#include "Engine/Platform/Platform.h"
|
|
#include "Engine/Physics/PhysicsSettings.h"
|
|
|
|
namespace
|
|
{
|
|
bool FixedDeltaTimeEnable;
|
|
float FixedDeltaTimeValue;
|
|
}
|
|
|
|
bool Time::_gamePaused = false;
|
|
DateTime Time::StartupTime;
|
|
float Time::UpdateFPS = 30.0f;
|
|
float Time::PhysicsFPS = 60.0f;
|
|
float Time::DrawFPS = 60.0f;
|
|
float Time::TimeScale = 1.0f;
|
|
Time::TickData Time::Update;
|
|
Time::FixedStepTickData Time::Physics;
|
|
Time::TickData Time::Draw;
|
|
Time::TickData* Time::Current = nullptr;
|
|
|
|
class TimeService : public EngineService
|
|
{
|
|
public:
|
|
|
|
TimeService()
|
|
: EngineService(TEXT("Time"), -850)
|
|
{
|
|
FixedDeltaTimeEnable = false;
|
|
FixedDeltaTimeValue = 0.0f;
|
|
|
|
#if USE_EDITOR
|
|
// Disable gameplay in Editor on startup
|
|
Time::_gamePaused = true;
|
|
#endif
|
|
}
|
|
};
|
|
|
|
TimeService TimeServiceInstance;
|
|
|
|
void Time::TickData::OnBeforeRun(float targetFps, double currentTime)
|
|
{
|
|
Time = UnscaledTime = TimeSpan::Zero();
|
|
DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero();
|
|
LastLength = static_cast<double>(DeltaTime.Ticks) / Constants::TicksPerSecond;
|
|
LastBegin = currentTime - LastLength;
|
|
LastEnd = currentTime;
|
|
}
|
|
|
|
void Time::TickData::OnReset(float targetFps, double currentTime)
|
|
{
|
|
DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero();
|
|
LastLength = static_cast<double>(DeltaTime.Ticks) / Constants::TicksPerSecond;
|
|
LastBegin = currentTime - LastLength;
|
|
LastEnd = currentTime;
|
|
}
|
|
|
|
bool Time::TickData::OnTickBegin(float targetFps, float maxDeltaTime)
|
|
{
|
|
// Check if can perform a tick
|
|
const double time = Platform::GetTimeSeconds();
|
|
double deltaTime;
|
|
if (FixedDeltaTimeEnable)
|
|
{
|
|
deltaTime = (double)FixedDeltaTimeValue;
|
|
}
|
|
else
|
|
{
|
|
deltaTime = Math::Clamp(time - LastBegin, 0.0, (double)maxDeltaTime);
|
|
const double minDeltaTime = targetFps > ZeroTolerance ? 1.0 / (double)targetFps : 0.0;
|
|
if (deltaTime < minDeltaTime)
|
|
return false;
|
|
}
|
|
|
|
// Update data
|
|
Advance(time, deltaTime);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Time::TickData::OnTickEnd()
|
|
{
|
|
const double time = Platform::GetTimeSeconds();
|
|
LastEnd = time;
|
|
LastLength = time - LastBegin;
|
|
}
|
|
|
|
void Time::TickData::Advance(double time, double deltaTime)
|
|
{
|
|
float timeScale = TimeScale;
|
|
if (_gamePaused)
|
|
timeScale = 0.0f;
|
|
LastBegin = time;
|
|
UnscaledDeltaTime = TimeSpan::FromSeconds(deltaTime);
|
|
UnscaledTime += UnscaledDeltaTime;
|
|
DeltaTime = TimeSpan::FromSeconds(deltaTime * (double)timeScale);
|
|
Time += DeltaTime;
|
|
TicksCount++;
|
|
}
|
|
|
|
bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime)
|
|
{
|
|
// Check if can perform a tick
|
|
double time = Platform::GetTimeSeconds();
|
|
double deltaTime, minDeltaTime;
|
|
if (FixedDeltaTimeEnable)
|
|
{
|
|
deltaTime = (double)FixedDeltaTimeValue;
|
|
minDeltaTime = deltaTime;
|
|
}
|
|
else
|
|
{
|
|
deltaTime = Math::Clamp(time - LastBegin, 0.0, (double)maxDeltaTime);
|
|
minDeltaTime = targetFps > ZeroTolerance ? 1.0 / (double)targetFps : 0.0;
|
|
if (deltaTime < minDeltaTime)
|
|
return false;
|
|
}
|
|
Samples.Add(deltaTime);
|
|
|
|
// Check if last few ticks were not taking too long so it's running slowly
|
|
const bool isRunningSlowly = Samples.Average() > 1.5 * minDeltaTime;
|
|
if (!isRunningSlowly)
|
|
{
|
|
// Make steps fixed size
|
|
const double diff = deltaTime - minDeltaTime;
|
|
time -= diff;
|
|
deltaTime = minDeltaTime;
|
|
}
|
|
|
|
// Update data
|
|
Advance(time, deltaTime);
|
|
|
|
return true;
|
|
}
|
|
|
|
Time::TickData* Time::GetHighestFrequency(float& fps)
|
|
{
|
|
const auto updateFps = UpdateFPS;
|
|
const auto drawFps = DrawFPS;
|
|
const auto physicsFps = PhysicsFPS;
|
|
|
|
if (physicsFps >= drawFps && physicsFps >= updateFps)
|
|
{
|
|
fps = physicsFps;
|
|
return &Physics;
|
|
}
|
|
|
|
if (drawFps >= physicsFps && drawFps >= updateFps)
|
|
{
|
|
fps = drawFps;
|
|
return &Draw;
|
|
}
|
|
|
|
fps = updateFps;
|
|
return &Update;
|
|
}
|
|
|
|
void Time::SetGamePaused(bool value)
|
|
{
|
|
if (_gamePaused == value)
|
|
return;
|
|
|
|
_gamePaused = value;
|
|
|
|
const double time = Platform::GetTimeSeconds();
|
|
Update.OnReset(UpdateFPS, time);
|
|
Physics.OnReset(PhysicsFPS, time);
|
|
Draw.OnReset(DrawFPS, time);
|
|
}
|
|
|
|
float Time::GetDeltaTime()
|
|
{
|
|
auto* data = Current ? Current : &Update;
|
|
return data->DeltaTime.GetTotalSeconds();
|
|
}
|
|
|
|
float Time::GetGameTime()
|
|
{
|
|
auto* data = Current ? Current : &Update;
|
|
return data->Time.GetTotalSeconds();
|
|
}
|
|
|
|
float Time::GetUnscaledDeltaTime()
|
|
{
|
|
auto* data = Current ? Current : &Update;
|
|
return data->UnscaledDeltaTime.GetTotalSeconds();
|
|
}
|
|
|
|
float Time::GetUnscaledGameTime()
|
|
{
|
|
auto* data = Current ? Current : &Update;
|
|
return data->UnscaledTime.GetTotalSeconds();
|
|
}
|
|
|
|
float Time::GetTimeSinceStartup()
|
|
{
|
|
return (DateTime::Now() - StartupTime).GetTotalSeconds();
|
|
}
|
|
|
|
void Time::SetFixedDeltaTime(bool enable, float value)
|
|
{
|
|
FixedDeltaTimeEnable = enable;
|
|
FixedDeltaTimeValue = value;
|
|
}
|
|
|
|
void Time::OnBeforeRun()
|
|
{
|
|
// Initialize tick data (based on a time settings)
|
|
const double time = Platform::GetTimeSeconds();
|
|
Update.OnBeforeRun(UpdateFPS, time);
|
|
Physics.OnBeforeRun(PhysicsFPS, time);
|
|
Draw.OnBeforeRun(DrawFPS, time);
|
|
}
|
|
|
|
bool Time::OnBeginUpdate()
|
|
{
|
|
if (Update.OnTickBegin(UpdateFPS, TimeSettings::Instance()->MaxUpdateDeltaTime))
|
|
{
|
|
Current = &Update;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Time::OnBeginPhysics()
|
|
{
|
|
if (Physics.OnTickBegin(PhysicsFPS, PhysicsSettings::Instance()->MaxDeltaTime))
|
|
{
|
|
Current = &Physics;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Time::OnBeginDraw()
|
|
{
|
|
if (Draw.OnTickBegin(DrawFPS, 1.0f))
|
|
{
|
|
Current = &Draw;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Time::OnEndUpdate()
|
|
{
|
|
Update.OnTickEnd();
|
|
Current = nullptr;
|
|
}
|
|
|
|
void Time::OnEndPhysics()
|
|
{
|
|
Physics.OnTickEnd();
|
|
Current = nullptr;
|
|
}
|
|
|
|
void Time::OnEndDraw()
|
|
{
|
|
Draw.OnTickEnd();
|
|
Current = nullptr;
|
|
}
|