Add FPS limit and pause option when game is unfocused

This commit is contained in:
Wojtek Figat
2025-11-27 10:55:02 +01:00
parent b07d74d28d
commit 394860656a
6 changed files with 65 additions and 29 deletions

View File

@@ -43,6 +43,18 @@ public:
API_FIELD(Attributes="EditorOrder(20), Limit(0.1f, 1000.0f, 0.01f), EditorDisplay(\"General\")")
float MaxUpdateDeltaTime = 0.1f;
/// <summary>
/// Limits maximum game framerate when application window loses focus. Use 0 to disable this feature.
/// </summary>
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Unfocused\")")
float UnfocusedMaxFPS = 30;
/// <summary>
/// Enables pausing game when application window loses focus.
/// </summary>
API_FIELD(Attributes="EditorOrder(110), EditorDisplay(\"Unfocused\")")
bool UnfocusedPause = false;
public:
/// <summary>
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.

View File

@@ -40,15 +40,11 @@
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MMethod.h"
#include "Engine/Scripting/ManagedCLR/MException.h"
#include "Engine/Core/Config/PlatformSettings.h"
#endif
namespace EngineImpl
{
bool IsReady = false;
#if !USE_EDITOR
bool RunInBackground = false;
#endif
String CommandLine = nullptr;
int32 Fps = 0, FpsAccumulatedFrames = 0;
double FpsAccumulated = 0.0;
@@ -168,9 +164,6 @@ int32 Engine::Main(const Char* cmdLine)
Platform::BeforeRun();
EngineImpl::InitMainWindow();
Application::BeforeRun();
#if !USE_EDITOR && (PLATFORM_WINDOWS || PLATFORM_LINUX || PLATFORM_MAC)
EngineImpl::RunInBackground = PlatformSettings::Get()->RunInBackground;
#endif
LOG_FLOOR();
LOG_FLUSH();
Time::Synchronize();
@@ -353,20 +346,8 @@ void Engine::OnUpdate()
UpdateCount++;
const auto mainWindow = MainWindow;
#if !USE_EDITOR
// Pause game if window lost focus and cannot run in a background
bool isGameRunning = true;
if (mainWindow && !mainWindow->IsFocused())
{
isGameRunning = EngineImpl::RunInBackground;
}
Time::SetGamePaused(!isGameRunning);
#endif
// Determine if application has focus (flag used by the other parts of the engine)
HasFocus = (mainWindow && mainWindow->IsFocused()) || Platform::GetHasFocus();
HasFocus = (MainWindow && MainWindow->IsFocused()) || Platform::GetHasFocus();
// Simulate lags
//Platform::Sleep(100);

View File

@@ -6,12 +6,29 @@
#include "Engine/Platform/Platform.h"
#include "Engine/Core/Config/TimeSettings.h"
#include "Engine/Serialization/Serialization.h"
#if !USE_EDITOR
#include "Engine/Engine/Engine.h"
#endif
namespace
{
bool FixedDeltaTimeEnable;
float FixedDeltaTimeValue;
float MaxUpdateDeltaTime = 0.1f;
#if USE_EDITOR
constexpr bool _gamePausedUnfocsed = false;
#define GetFps(fps) fps
#else
bool _gamePausedUnfocsed = false;
float UnfocusedMaxFPS = 0.0f;
bool UnfocusedPause = false;
float GetFps(float fps)
{
if (UnfocusedMaxFPS > 0 && !Engine::HasFocus && fps > UnfocusedMaxFPS)
fps = UnfocusedMaxFPS;
return fps;
}
#endif
}
bool Time::_gamePaused = false;
@@ -52,15 +69,16 @@ void TimeSettings::Apply()
Time::DrawFPS = DrawFPS;
Time::TimeScale = TimeScale;
::MaxUpdateDeltaTime = MaxUpdateDeltaTime;
#if !USE_EDITOR
::UnfocusedMaxFPS = UnfocusedMaxFPS;
::UnfocusedPause = UnfocusedPause;
#endif
}
void Time::TickData::Synchronize(float targetFps, double currentTime)
{
OnReset(targetFps, currentTime);
Time = UnscaledTime = TimeSpan::Zero();
DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero();
LastLength = static_cast<double>(DeltaTime.Ticks) / TimeSpan::TicksPerSecond;
LastBegin = currentTime - LastLength;
LastEnd = currentTime;
NextBegin = targetFps > ZeroTolerance ? LastBegin + (1.0f / targetFps) : 0.0;
}
@@ -115,7 +133,7 @@ void Time::TickData::OnTickEnd()
void Time::TickData::Advance(double time, double deltaTime)
{
float timeScale = TimeScale;
if (_gamePaused)
if (_gamePaused || _gamePausedUnfocsed)
timeScale = 0.0f;
LastBegin = time;
UnscaledDeltaTime = TimeSpan::FromSeconds(deltaTime);
@@ -194,9 +212,11 @@ void Time::SetGamePaused(bool value)
{
if (_gamePaused == value)
return;
_gamePaused = value;
if (_gamePausedUnfocsed)
return;
// Reset ticking
const double time = Platform::GetTimeSeconds();
Update.OnReset(UpdateFPS, time);
Physics.OnReset(PhysicsFPS, time);
@@ -249,7 +269,24 @@ void Time::Synchronize()
bool Time::OnBeginUpdate(double time)
{
if (Update.OnTickBegin(time, UpdateFPS, MaxUpdateDeltaTime))
#if !USE_EDITOR
// Pause game if window lost focus (based on game settings)
bool gamePausedUnfocsed = !Engine::HasFocus && UnfocusedPause;
if (gamePausedUnfocsed != _gamePausedUnfocsed)
{
_gamePausedUnfocsed = gamePausedUnfocsed;
if (!gamePausedUnfocsed)
{
// Reset ticking
const double time = Platform::GetTimeSeconds();
Update.OnReset(UpdateFPS, time);
Physics.OnReset(PhysicsFPS, time);
Draw.OnReset(DrawFPS, time);
}
}
#endif
if (Update.OnTickBegin(time, GetFps(UpdateFPS), MaxUpdateDeltaTime))
{
Current = &Update;
return true;
@@ -259,7 +296,7 @@ bool Time::OnBeginUpdate(double time)
bool Time::OnBeginPhysics(double time)
{
if (Physics.OnTickBegin(time, PhysicsFPS, _physicsMaxDeltaTime))
if (Physics.OnTickBegin(time, GetFps(PhysicsFPS), _physicsMaxDeltaTime))
{
Current = &Physics;
return true;
@@ -269,7 +306,7 @@ bool Time::OnBeginPhysics(double time)
bool Time::OnBeginDraw(double time)
{
if (Draw.OnTickBegin(time, DrawFPS, 1.0f))
if (Draw.OnTickBegin(time, GetFps(DrawFPS), 1.0f))
{
Current = &Draw;
return true;

View File

@@ -43,8 +43,10 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API
/// <summary>
/// Enables game running when application window loses focus.
/// [Deprecated in v1.12]
/// </summary>
API_FIELD(Attributes="EditorOrder(1010), DefaultValue(false), EditorDisplay(\"Other\", \"Run In Background\")")
DEPRECATED("Use UnfocusedPause from TimeSettings.")
bool RunInBackground = false;
/// <summary>

View File

@@ -40,8 +40,10 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API
/// <summary>
/// Enables game running when application window loses focus.
/// [Deprecated in v1.12]
/// </summary>
API_FIELD(Attributes="EditorOrder(1010), EditorDisplay(\"Other\", \"Run In Background\")")
DEPRECATED("Use UnfocusedPause from TimeSettings.")
bool RunInBackground = false;
/// <summary>

View File

@@ -43,8 +43,10 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API
/// <summary>
/// Enables game running when application window loses focus.
/// [Deprecated in v1.12]
/// </summary>
API_FIELD(Attributes="EditorOrder(1010), DefaultValue(false), EditorDisplay(\"Other\", \"Run In Background\")")
DEPRECATED("Use UnfocusedPause from TimeSettings.")
bool RunInBackground = false;
/// <summary>