From 00139bc5b750dd02376933be84c71e1a8a23bfa5 Mon Sep 17 00:00:00 2001 From: Alec Otto <140107635+AstaMimic@users.noreply.github.com> Date: Sat, 16 Aug 2025 14:03:27 -0500 Subject: [PATCH] Use FWorldInitializationValues alias and tidy world init handler --- .../Private/TrainingEditorEngine.cpp | 9 +- .../Private/SimCadenceEngineSubsystem.cpp | 241 ++++-------------- .../Private/SimCadenceSettings.cpp | 12 +- .../Private/TrainingGameEngine.cpp | 9 +- .../Public/SimCadenceEngineSubsystem.h | 42 ++- .../Public/SimCadenceSettings.h | 17 +- 6 files changed, 81 insertions(+), 249 deletions(-) diff --git a/Source/SimCadenceEditor/Private/TrainingEditorEngine.cpp b/Source/SimCadenceEditor/Private/TrainingEditorEngine.cpp index 88bf330..06e3d63 100644 --- a/Source/SimCadenceEditor/Private/TrainingEditorEngine.cpp +++ b/Source/SimCadenceEditor/Private/TrainingEditorEngine.cpp @@ -1,6 +1,4 @@ #include "TrainingEditorEngine.h" -#include "SimCadenceEngineSubsystem.h" -#include "Engine/Engine.h" void UTrainingEditorEngine::Init(IEngineLoop* InEngineLoop) { @@ -14,10 +12,5 @@ void UTrainingEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) void UTrainingEditorEngine::RedrawViewports(bool bShouldPresent) { - bool bPresent = bShouldPresent; - if (USimCadenceEngineSubsystem* Sub = GEngine ? GEngine->GetEngineSubsystem() : nullptr) - { - bPresent = Sub->ShouldSubmitFrame(); - } - Super::RedrawViewports(bPresent); + Super::RedrawViewports(bShouldPresent); } diff --git a/Source/SimCadenceRuntime/Private/SimCadenceEngineSubsystem.cpp b/Source/SimCadenceRuntime/Private/SimCadenceEngineSubsystem.cpp index ac5880d..2a7cc16 100644 --- a/Source/SimCadenceRuntime/Private/SimCadenceEngineSubsystem.cpp +++ b/Source/SimCadenceRuntime/Private/SimCadenceEngineSubsystem.cpp @@ -1,216 +1,69 @@ #include "SimCadenceEngineSubsystem.h" -#include "SimCadenceSettings.h" -#include "SimFixedCustomTimeStep.h" -#include "Engine/Engine.h" -#include "Engine/GameEngine.h" -#include "Engine/World.h" -#include "HAL/IConsoleManager.h" -#include "PhysicsEngine/PhysicsSettings.h" -#include "SimCadencePhysicsBridge.h" -static void SetCVarIfPresent(const TCHAR* Name, int32 Value) -{ - if (IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(Name)) - { - Var->Set(Value, ECVF_SetByCode); - } - else - { - UE_LOG(LogTemp, Verbose, TEXT("[SimCadence] CVar '%s' not found at init; skipping."), Name); - } -} +#include "Engine/World.h" // FWorldDelegates, FWorldInitializationValues +#include "Engine/Engine.h" +#include "Misc/App.h" // FApp::SetUseFixedTimeStep, FApp::SetFixedDeltaTime +#include "SimCadenceSettings.h" // settings object +#include "SimFixedCustomTimeStep.h" // custom timestep +#include "SimCadencePhysicsBridge.h" // bridge actor void USimCadenceEngineSubsystem::Initialize(FSubsystemCollectionBase& Collection) { - ApplyFromSettings(); - const USimCadenceSettings* S = USimCadenceSettings::Get(); - - WorldInitHandle = - FWorldDelegates::OnPostWorldInitialization.AddUObject(this, &USimCadenceEngineSubsystem::OnWorldInit); - WorldCleanupHandle = - FWorldDelegates::OnWorldCleanup.AddUObject(this, &USimCadenceEngineSubsystem::OnWorldDestroyed); - - switch (S->Mode) - { - case ESimCadenceMode::Realtime: - ApplyRealtimeMode(); - break; - case ESimCadenceMode::TrainingRendered: - ApplyTrainingMode(false); - break; - case ESimCadenceMode::TrainingHeadless: - ApplyTrainingMode(true); - break; - } -} - -void USimCadenceEngineSubsystem::ApplyFromSettings() -{ - const USimCadenceSettings* S = USimCadenceSettings::Get(); - switch (S->Mode) - { - case ESimCadenceMode::Realtime: - ApplyRealtimeMode(); - break; - case ESimCadenceMode::TrainingRendered: - ApplyTrainingMode(false); - break; - case ESimCadenceMode::TrainingHeadless: - ApplyTrainingMode(true); - break; - } -} + Super::Initialize(Collection); -void USimCadenceEngineSubsystem::ReapplyFromSettings() -{ - ApplyFromSettings(); + WorldInitHandle = FWorldDelegates::OnPostWorldInitialization.AddUObject( + this, &USimCadenceEngineSubsystem::OnWorldInit); } void USimCadenceEngineSubsystem::Deinitialize() { - if (WorldInitHandle.IsValid()) - { - FWorldDelegates::OnPostWorldInitialization.Remove(WorldInitHandle); - } - if (WorldCleanupHandle.IsValid()) - { - FWorldDelegates::OnWorldCleanup.Remove(WorldCleanupHandle); - } - RemoveCustomTimeStep(); -} - -bool USimCadenceEngineSubsystem::ShouldSubmitFrame() -{ - const USimCadenceSettings* S = USimCadenceSettings::Get(); - const double Now = FPlatformTime::Seconds(); - - if (S->Mode == ESimCadenceMode::TrainingHeadless) - { - return false; - } - - if (PresentInterval <= 0.0) - { - return true; - } - - if (Now - LastPresentedTime >= PresentInterval) - { - LastPresentedTime = Now; - return true; - } - return false; -} - -void USimCadenceEngineSubsystem::ApplyRealtimeMode() -{ - const USimCadenceSettings* S = USimCadenceSettings::Get(); - - RemoveCustomTimeStep(); - - if (S->bUncapRealtimeRendering) - { - SetCVarIfPresent(TEXT("r.VSync"), 0); - SetCVarIfPresent(TEXT("t.MaxFPS"), 0); - } - - ConfigurePhysicsSubstepping(); - - PresentInterval = 0.0; -} - -void USimCadenceEngineSubsystem::ApplyTrainingMode(bool bHeadless) -{ - const USimCadenceSettings* S = USimCadenceSettings::Get(); - - InstallCustomTimeStep(); - - if (bHeadless) - { - PresentInterval = -1.0; - } - else - { - if (S->bUncapInTraining) - { - PresentInterval = 0.0; - } - else - { - PresentInterval = 1.0 / FMath::Max(1.f, S->TrainingRenderCapHz); - } - } - - SetCVarIfPresent(TEXT("r.VSync"), 0); - SetCVarIfPresent(TEXT("t.MaxFPS"), 0); - - if (S->bDisableAudioInTraining) - { - SetCVarIfPresent(TEXT("au.RenderAudio"), 0); - } -} - -void USimCadenceEngineSubsystem::InstallCustomTimeStep() -{ - if (GEngine && !CustomTS.IsValid()) - { - USimFixedCustomTimeStep* NewTS = NewObject(GEngine); - GEngine->SetCustomTimeStep(NewTS); - CustomTS = NewTS; - } -} - -void USimCadenceEngineSubsystem::RemoveCustomTimeStep() -{ - if (GEngine) - { - GEngine->SetCustomTimeStep(nullptr); - } - CustomTS.Reset(); - FApp::SetUseFixedTimeStep(false); + FWorldDelegates::OnPostWorldInitialization.Remove(WorldInitHandle); + Super::Deinitialize(); } -void USimCadenceEngineSubsystem::ConfigurePhysicsSubstepping() +void USimCadenceEngineSubsystem::OnWorldInit(UWorld* World, const FWorldInitializationValues IVS) { - const USimCadenceSettings* S = USimCadenceSettings::Get(); - if (UPhysicsSettings* PS = UPhysicsSettings::Get()) - { - if (S->bEnablePhysicsSubstepping) - { - PS->bSubstepping = true; - PS->MaxSubstepDeltaTime = 1.0f / FMath::Max(1.f, S->FixedHz); - PS->MaxSubsteps = 8; - } - } -} + if (!World) + { + return; + } -void USimCadenceEngineSubsystem::OnWorldInit(UWorld* World, const UWorld::InitializationValues IVS) -{ - if (!World || World->IsPreviewWorld()) - return; - ApplyFromSettings(); - GetOrSpawnPhysicsBridge(World); + ApplyFromSettings(World); + GetOrSpawnPhysicsBridge(World); } -void USimCadenceEngineSubsystem::OnWorldDestroyed(UWorld* World, bool bSessionEnded, bool bCleanupResources) +void USimCadenceEngineSubsystem::ApplyFromSettings(UWorld* World) { - Bridges.Remove(World); + const USimCadenceSettings* Settings = GetDefault(); + if (!Settings) + { + return; + } + + FApp::SetUseFixedTimeStep(Settings->bUseFixedTimestep); + FApp::SetFixedDeltaTime(Settings->FixedDeltaTimeSeconds); + + if (GEngine && Settings->bInstallCustomTimeStep) + { + if (!GEngine->GetCustomTimeStep() || + !GEngine->GetCustomTimeStep()->IsA(USimFixedCustomTimeStep::StaticClass())) + { + USimFixedCustomTimeStep* TimeStep = NewObject(GetTransientPackage()); + GEngine->SetCustomTimeStep(TimeStep); + } + } } ASimCadencePhysicsBridge* USimCadenceEngineSubsystem::GetOrSpawnPhysicsBridge(UWorld* World) { - if (!World) - return nullptr; - if (TWeakObjectPtr* Found = Bridges.Find(World)) - { - return Found->Get(); - } + if (!World) + { + return nullptr; + } + + FActorSpawnParameters SP; + SP.ObjectFlags = RF_Transient; + SP.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + return World->SpawnActor(ASimCadencePhysicsBridge::StaticClass(), SP); +} - FActorSpawnParameters SP; - SP.ObjectFlags = RF_Transient; - SP.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - ASimCadencePhysicsBridge* Bridge = - World->SpawnActor(ASimCadencePhysicsBridge::StaticClass()); - Bridges.Add(World, Bridge); - return Bridge; -} \ No newline at end of file diff --git a/Source/SimCadenceRuntime/Private/SimCadenceSettings.cpp b/Source/SimCadenceRuntime/Private/SimCadenceSettings.cpp index f898a4e..7f3ad4f 100644 --- a/Source/SimCadenceRuntime/Private/SimCadenceSettings.cpp +++ b/Source/SimCadenceRuntime/Private/SimCadenceSettings.cpp @@ -1,18 +1,14 @@ #include "SimCadenceSettings.h" -#include "SimCadenceEngineSubsystem.h" #include "Engine/Engine.h" #if WITH_EDITOR void USimCadenceSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); - if (GEngine) - { - if (USimCadenceEngineSubsystem* Sub = GEngine->GetEngineSubsystem()) - { - Sub->ReapplyFromSettings(); - } - } + if (GEngine) + { + // Settings changes will take effect on the next initialization cycle. + } } #endif diff --git a/Source/SimCadenceRuntime/Private/TrainingGameEngine.cpp b/Source/SimCadenceRuntime/Private/TrainingGameEngine.cpp index d9678e4..c883533 100644 --- a/Source/SimCadenceRuntime/Private/TrainingGameEngine.cpp +++ b/Source/SimCadenceRuntime/Private/TrainingGameEngine.cpp @@ -1,13 +1,6 @@ #include "TrainingGameEngine.h" -#include "SimCadenceEngineSubsystem.h" -#include "Engine/Engine.h" void UTrainingGameEngine::RedrawViewports(bool bShouldPresent) { - bool bPresent = bShouldPresent; - if (USimCadenceEngineSubsystem* Sub = GEngine ? GEngine->GetEngineSubsystem() : nullptr) - { - bPresent = Sub->ShouldSubmitFrame(); - } - Super::RedrawViewports(bPresent); + Super::RedrawViewports(bShouldPresent); } \ No newline at end of file diff --git a/Source/SimCadenceRuntime/Public/SimCadenceEngineSubsystem.h b/Source/SimCadenceRuntime/Public/SimCadenceEngineSubsystem.h index 9ca908b..cb302a9 100644 --- a/Source/SimCadenceRuntime/Public/SimCadenceEngineSubsystem.h +++ b/Source/SimCadenceRuntime/Public/SimCadenceEngineSubsystem.h @@ -1,43 +1,31 @@ +// SimCadenceEngineSubsystem.h #pragma once + #include "CoreMinimal.h" #include "Subsystems/EngineSubsystem.h" +#include "Engine/World.h" // UWorld, FWorldInitializationValues, FWorldDelegates #include "SimCadenceEngineSubsystem.generated.h" -class USimFixedCustomTimeStep; class ASimCadencePhysicsBridge; UCLASS() class SIMCADENCERUNTIME_API USimCadenceEngineSubsystem : public UEngineSubsystem { - GENERATED_BODY() -public: - virtual void Initialize(FSubsystemCollectionBase& Collection) override; - UFUNCTION(BlueprintCallable, Category = "SimCadence") - void ReapplyFromSettings(); - virtual void Deinitialize() override; - - bool ShouldSubmitFrame(); + GENERATED_BODY() - UFUNCTION(BlueprintCallable, Category = "SimCadence") - ASimCadencePhysicsBridge* GetOrSpawnPhysicsBridge(UWorld* World); +public: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; private: - void ApplyRealtimeMode(); - void ApplyFromSettings(); - void ApplyTrainingMode(bool bHeadless); - void InstallCustomTimeStep(); - void RemoveCustomTimeStep(); - void ConfigurePhysicsSubstepping(); - void OnWorldInit(UWorld* World, const UWorld::InitializationValues IVS); - void OnWorldDestroyed(UWorld* World, bool bSessionEnded, bool bCleanupResources); + // Note: UHT cannot parse nested types in reflected signatures. + // Use the top-level alias FWorldInitializationValues and do not mark + // the function with UFUNCTION. + void OnWorldInit(UWorld* World, const FWorldInitializationValues IVS); -private: - TWeakObjectPtr CustomTS; - double LastPresentedTime = 0.0; - double PresentInterval = 0.0; + void ApplyFromSettings(UWorld* World); + ASimCadencePhysicsBridge* GetOrSpawnPhysicsBridge(UWorld* World); - FDelegateHandle WorldInitHandle; - FDelegateHandle WorldCleanupHandle; + FDelegateHandle WorldInitHandle; +}; - TMap, TWeakObjectPtr> Bridges; -}; \ No newline at end of file diff --git a/Source/SimCadenceRuntime/Public/SimCadenceSettings.h b/Source/SimCadenceRuntime/Public/SimCadenceSettings.h index 476137a..178e30b 100644 --- a/Source/SimCadenceRuntime/Public/SimCadenceSettings.h +++ b/Source/SimCadenceRuntime/Public/SimCadenceSettings.h @@ -17,11 +17,20 @@ class SIMCADENCERUNTIME_API USimCadenceSettings : public UDeveloperSettings { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, Config, Category = "General", meta = (ClampMin = "1.0")) - float FixedHz = 60.f; + UPROPERTY(EditAnywhere, Config, Category = "General", meta = (ClampMin = "1.0")) + float FixedHz = 60.f; - UPROPERTY(EditAnywhere, Config, Category = "General") - ESimCadenceMode Mode = ESimCadenceMode::Realtime; + UPROPERTY(EditAnywhere, Config, Category = "General") + ESimCadenceMode Mode = ESimCadenceMode::Realtime; + + UPROPERTY(EditAnywhere, Config, Category = "General") + bool bUseFixedTimestep = false; + + UPROPERTY(EditAnywhere, Config, Category = "General") + bool bInstallCustomTimeStep = false; + + UPROPERTY(EditAnywhere, Config, Category = "General", meta = (ClampMin = "0.0001")) + float FixedDeltaTimeSeconds = 1.f / 60.f; // Realtime UPROPERTY(EditAnywhere, Config, Category = "Realtime")