UE4 Subsystem Basics

Ajwad Imran

Ajwad Imran / July 03, 2021

3 min read

UE4 Subsystem

Epic Games has been converting existing systems into subsystems Unreal Engine 4 and 5. First introduced in version 4.22 the subsystems have come a long way. In this short article, I would show you how you could use subsystems to your advantage. If you are coming from Unity world then this is something similar to singletons but much more powerful.

Overview 📝

The subsystems allow you to extend the functionality of existing Unreal Engine systems e.g. Editor, GameInstance, etc. The Unreal Engine 5 came with a feature called Enhanced Input which is also a subsystem.

Some of the benefits of substems are:

  • Save programming time.
  • They help you avoid overriding engine classes.
  • Add additional code to already huge classes.
  • Easily accessible in blueprints.
  • Can be accessed through python scripts.
  • Provides modularity and can be easily reused in other projects.

The subsystems available in unreal engine 4.26 are:

UEngineSubsystem // Share the lifetime of Engine
UEditorSubsystem // share the lifetime of Editor
UGameInstanceSubsystem // Share the lifetime of game instance
ULocalPlayerSubsystem // Share the lifetime of local players
UWorldSubsystem  // Share the lifetime of UWorld

Example

So far the subsystems sound great but what if you want to start a subsystem before another subsystem? Luckily, devs at Epic thought about that and gave us a way to do just that. The complete source code for this example is available here.

Let's get on with our problem. I created two classes UAjwadGIS and URogerioGIS both derived from UGameInstanceSubsystem. We want to initialize URogerioGIS first and then continue to initialize other subsystems. First, let's implement URogerioGIS:

URogerioGIS.h

#pragma once

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

UCLASS()
class URogerioGIS : public UGameInstanceSubsystem
{
    GENERATED_BODY()

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

URogerioGIS.cpp

#include "RogerioGIS.h"

void URogerioGIS::Initialize(FSubsystemCollectionBase& Collection)
{
    UE_LOG(LogTemp, Warning, TEXT("Rogerio Subsystem init"));
}

void URogerioGIS::Deinitialize() {}

The implementation of UAjwadGIS.h is similar but in UAjwadGIS.cpp we will call the Collection's InitializeDependency function and pass it the URogerioGIS's class reference. If the collection encounter's Ajwad subsystem first then it will initialize Rogerio subsystem before initializing Ajwad substem.

UAjwadGIS.h

#pragma once

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

UCLASS()
class UAjwadGIS : public UGameInstanceSubsystem
{
    GENERATED_BODY()

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

UAjwadGIS.cpp

#include "AjwadGIS.h"
#include "RogerioGIS.h"
#include "Subsystems/SubsystemCollection.h"

void UAjwadGIS::Initialize(FSubsystemCollectionBase& Collection)
{
    Collection.InitializeDependency(URogerioGIS::StaticClass());
    UE_LOG(LogTemp, Warning, TEXT("Ajwad Subsystem init."));
}

void UAjwadGIS::Deinitialize() {}

Now if you compile your code and hit Play in the editor you should be able to see that "Rogerio Subsystem init" is logged before "Ajwad Subsystem init".

The benefit of using UGameInstanceSubsystem is that we don't have to override the GameInstance class to add our functionality. The subsystem offer's the same lifetime and much more flexibility.

Conclusion 👏🏼

New subsystems are being added to Unreal Engine constantly and the engine seems to be moving towards subsystem path due to their ease of use and benefits that they offer. I will link some helpful resources below.