Skip to content

Adding a subsystem

Recipe for declaring a new Game Instance or World subsystem in Rephrased_Demo. Subsystems are singletons the engine creates automatically — you do not spawn them.

Prerequisites: Actors, components, subsystems, C++ extensions, Coding conventions.

Choose a scope

Base class Lifetime Use when
UGameInstanceSubsystem Survives level loads Global sentence logic, cross-level managers (USentenceValidator, UEffectExecutor)
UWorldSubsystem One instance per loaded world Per-level registries and caches (URuneableObjectRegistry, URuneEffectHistory)

If the data should reset when the level changes, pick World. If it should persist across level transitions within one play session, pick Game Instance.

1. Add the header

Place new subsystem headers under Source/Rephrased_Demo/ (or Source/Rephrased_Demo/RuneSystem/Subsystems/ if the subsystem belongs to the rune stack).

Game Instance example:

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyFeatureSubsystem.generated.h"

UCLASS()
class REPHRASED_DEMO_API UMyFeatureSubsystem : public UGameInstanceSubsystem
{
    GENERATED_BODY()

public:
    virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    virtual void Deinitialize() override;

    UFUNCTION(BlueprintCallable, Category = "My Feature")
    void DoWork();
};

World example: inherit UWorldSubsystem instead and include Subsystems/WorldSubsystem.h. See URuneableObjectRegistry in Source/Rephrased_Demo/RuneSystem/Subsystems/RuneableObjectRegistry.h.

2. Implement Initialize / Deinitialize

#include "MyFeatureSubsystem.h"

void UMyFeatureSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
    Super::Initialize(Collection);
    // Bind delegates, cache subsystems — world and game instance exist here.
}

void UMyFeatureSubsystem::Deinitialize()
{
    // Unbind, clear caches.
    Super::Deinitialize();
}

If your subsystem must run after another, declare the dependency in Initialize:

Collection.InitializeDependency<USentenceValidator>();

See existing subsystems in Source/Rephrased_Demo/ for patterns.

3. Access from gameplay code

Game Instance:

if (UGameInstance* GI = GetWorld()->GetGameInstance())
{
    if (UMyFeatureSubsystem* Sub = GI->GetSubsystem<UMyFeatureSubsystem>())
    {
        Sub->DoWork();
    }
}

World:

if (UMyFeatureSubsystem* Sub = GetWorld()->GetSubsystem<UMyFeatureSubsystem>())
{
    Sub->DoWork();
}

4. Build rules

  • New .h / .cpp files under Source/Rephrased_Demo/ are picked up automatically — no module list edit for files in the game module.
  • After adding UCLASS, UPROPERTY, or UFUNCTION, close the editor and rebuild from Visual Studio. Live Coding cannot add new reflected members.
  • First compile after a new class often takes a few minutes; incremental builds afterward are faster.

5. Verify

  1. Build Development Editor | Win64 succeeds.
  2. Launch editor, open Rune Showcase, press Play.
  3. Confirm your subsystem logs or behavior in Output Log / gameplay as expected.

Change history

  • 2026-05-23 — Gabriel Li — Subsystem recipe from onboarding audit follow-up.