Editor Subsystems (C++)
The two editor subsystems — UOverrideGameModeGameInstanceSubsystem applies the override, UOverrideGameModeUISubsystem builds the toolbar widget.
The editor module ships two subsystems:
UOverrideGameModeGameInstanceSubsystem— applies the override in PIE worlds and restores the original game mode on PIE end.UOverrideGameModeUISubsystem— builds the level editor toolbar widget.
Both classes live under Private/, so they're implementation details
rather than publicly extendable API. You shouldn't need to reference
either from project code.
- Module:
QuickGameModeOverrideEditor(Editor)
UOverrideGameModeGameInstanceSubsystem
A UGameInstanceSubsystem. Spawns inside the editor's PIE
GameInstance, swaps the world's DefaultGameMode on Initialize,
and restores it on Deinitialize.
UCLASS()
class QUICKGAMEMODEOVERRIDEEDITOR_API UOverrideGameModeGameInstanceSubsystem
: public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
private:
UPROPERTY()
TSubclassOf<AGameModeBase> OrgGameMode;
};ShouldCreateSubsystem
return !(IsRunningCommandlet() || IsRunningCookCommandlet());Spawns in normal editor sessions and PIE. Skipped during commandlet runs (which includes the cook commandlet) so cooking doesn't get a patched game mode.
Initialize
const UQuickGameModeOverrideSettings* Settings = GetDefault<UQuickGameModeOverrideSettings>();
if (Settings->bOverrideGameMode && Settings->OverrideGameModeClass.IsSet())
{
if (TSubclassOf<AGameModeBase> OverrideClass = Settings->OverrideGameModeClass.GetValue())
{
if (UWorld* World = GetWorld())
{
AWorldSettings* WorldSettings = World->GetWorldSettings();
OrgGameMode = WorldSettings->DefaultGameMode;
WorldSettings->DefaultGameMode = OverrideClass;
}
}
}Captures the world's current DefaultGameMode into OrgGameMode,
then swaps in the override.
Deinitialize
if (UWorld* World = GetWorld())
{
if (AWorldSettings* WorldSettings = World->GetWorldSettings())
{
if (OrgGameMode)
{
WorldSettings->DefaultGameMode = OrgGameMode;
OrgGameMode = nullptr;
}
}
}Restores the captured game mode if one was captured. The if (OrgGameMode) guard makes the restore a no-op when the override was
disabled at PIE start.
Why a GameInstance subsystem rather than the engine subsystem?
UGameInstanceSubsystem instances are scoped to a single
GameInstance — that includes the PIE GameInstance. Because the
lifetime ends when PIE ends, Deinitialize is the natural place to
restore the original game mode. An engine subsystem would persist for
the editor session and would need its own end-of-PIE hook.
UOverrideGameModeUISubsystem
A UEditorSubsystem that owns the toolbar widget.
UCLASS()
class QUICKGAMEMODEOVERRIDEEDITOR_API UOverrideGameModeUISubsystem : public UEditorSubsystem
{
GENERATED_BODY()
using FOnComboBoxChange = SComboBox<TSharedPtr<FString>>::FOnSelectionChanged;
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
private:
void PIELifeCycleEvent(bool, bool bBegin) const;
void BuildToolBar();
TSharedPtr<SWidget> ParentWidget;
};ShouldCreateSubsystem
return !(IsRunningCommandlet() || IsRunningCookCommandlet());Same gate as the GameInstance subsystem — no toolbar in commandlets.
Initialize
BuildToolBar();
FEditorDelegates::BeginPIE.AddUObject(this, &UOverrideGameModeUISubsystem::PIELifeCycleEvent, true);
FEditorDelegates::EndPIE.AddUObject(this, &UOverrideGameModeUISubsystem::PIELifeCycleEvent, false);Builds the toolbar widget once, then registers PIE lifecycle hooks.
BuildToolBar
Extends LevelEditor.LevelEditorToolBar.User with a single composite
entry containing:
- A vertical separator.
- An
SCheckBoxbound tobOverrideGameMode, withOnCheckStateChangedwriting through to the settings and callingSaveDefault(). - An
SClassPropertyEntryBoxfiltered toAGameModeBase. The picker is disabled when the checkbox is unchecked (IsEnabled_Lambdareads the bool back from settings). On selection it writes the class throughOverrideGameModeClassand callsSaveDefault(). Picking None resets the optional. - A trailing vertical separator.
The widget root (SBorder) is captured into ParentWidget so PIE
visibility hooks can collapse / restore it.
PIELifeCycleEvent
ParentWidget->SetVisibility(bBegin ? EVisibility::Collapsed : EVisibility::Visible);Collapses the widget when PIE begins, restores it when PIE ends. This avoids accidental edits during play.
Spawn order summary
| Subsystem | Spawned in | Purpose |
|---|---|---|
UOverrideGameModeUISubsystem | Editor (not commandlets) | Builds the toolbar widget. |
UOverrideGameModeGameInstanceSubsystem | Every GameInstance (not commandlets) | Patches PIE worlds and restores on PIE end. |
The plugin is editor-only by design — there is no runtime subsystem that activates in packaged builds.