Configuring Subsystems via editor?

Hey there!

I was very excited about the [relatively] new Subsystems classes. They are actually very useful!

But I have and issue with them - it’s pretty inconvenient to configure the subsystems.
Particularly, two things:

  1. There is no way to decide which subsystems should be instanced and which are not
  2. No way to expose UPROPERTY’es from a Subsystem to set them via Editor

1st can be solved manually via overriding ShouldCreateSubsystem, but it’s not really convenient…
2nd can be solved via implementing some Setup/Configure method which should be called from outside, but it negates the main Subsystem pros about encapsulating the logic and getting rid of the managing code.

Am I missing something?

For the 2nd issue, it would be great to able to make a derived Blueprint classes to change the default parameters of the subsystem (while keeping only single instance of the Subsystem and able to access the subsystem by it’s C++ class).

Any other ideas?

2 Likes

Hello,

  1. Apart from the solution you mention overriding ‘ShouldCreateSubsystem’, you also have some control by choosing which subsystem you subclass, as they all have different life-cycles. Are you sure subsystems are the right choice for your intents?
    https://docs.unrealengine.com/en-US/…ems/index.html

  2. I can create variables and edit them in Blueprint derived subsystem classes just as I can do with any other classes.



MySubsystemClass.h

...
UCLASS(Blueprintable)
...
protected (or public):

UPROPERTY(EditAnywhere)
int32 MyInt = 0;


@Kontur Thank you very much for response!

  1. Yeah I know about the life-cycles. What I meant is I can have multiple different subsystems of the same life cycle and sometimes it can be useful to chose which to activate and which not. For example, the World Subsystems can be different for different maps…

  2. Hmm I should check that again. I have tried to make a Blueprintable subsystem but it was not working (editor crashing). I was thinking it’s not supposed to be blueprintable. Also, how can I refer to the BP-derived subsystem from C++ code? By default it’s fetched by it’s class, will it work if I have both UMySubsystem and BP derived from UMySubsystem? Also, will they be both instanced?

Any luck with this issue? i’m running into the same thing right now.

I’ve tried exposing some variables for the subsystem to editor as well without any luck. I ended up creating Data Asset with all necessary properties for the subsystem. Then I exposed variable to store the Data Asset for that particular subsystem in Game Instance. Inside GameInstance I fetch the subsystem and pass the Data Asset to the Subsystem.

The only other solution I can think of is using StaticConstructorHelpers and reference the Data Asset directly in a Subsystem. Never tried it tho, not sure if its achievable.

Cheers,
Robert

So I found a pretty dirty hack to get this to work. You create a blueprint of the subsystem and inside Initialize you load the CDO of the blueprint and copy over any data you need, in ShouldCreateSubsystem you check if ‘this’ is a blueprint type and return false as to not create 2 instances of the subsystem. Its super hacky and the data asset is a cleaner approach but i figured this is a self contained way to do it.

1 Like

If you mark the base class with the *Abstract *property, it wont try to create the subsystem at runtime. Also adding the property *Blueprintable *allows you to create a blueprint from it and it will try to initialize at runtime. At least for GameInstanceSubsystem this worked when I tried it.

4 Likes

Thank you so much for this answer, I have been searching for hours how to use my Blueprint-derived Subsystem instead of the C++ one (so I could use the editor to assign properties). I marked my GameInstanceSubsystem derived C++ class as Abstract, Blueprintable and it worked! They should really put this in the Docs.

1 Like

I’ve tried exactly the same, but the Blueprint-derived Subsystem is not getting initialized. I’ve marked the C++ base class as Abstract, Blueprintable. I’m using UE 4.26.1. Which engine version do you use? Am I missing something?

Hello Zaratusa,

I’m also using 4.26.1 and I just made a working example of a gameinstance subsystem working with blueprint. Here’s my code:



#pragma once

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

UCLASS(Abstract, Blueprintable)
class MYGAME_API UUiManager : public UGameInstanceSubsystem
{
GENERATED_BODY()

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

UFUNCTION(BlueprintImplementableEvent)
void InitEventCalledInBlueprint();
};

#include "UiManager.h"

void UUiManager::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
InitEventCalledInBlueprint();
}


Then I made a blueprint class inheriting from the parent class “Ui Manager” in my case. I then call the InitEventCalledInBlueprint just to prove it’s being called (screenshot).

I hope it helps!

2 Likes

Hello,
I was trying the same. I’ve marked the C++ base class as Abstract, Blueprintable. And Blueprint subsystem is not working. It is working only if you open blueprint in editor :slight_smile: But not if you play game.

3 Likes

If you have a C++ UGameInstance class, be sure to call Super::Init() and Super::Shutdown() in their respective methods in your UGameInstace derived class. Without the above snippets, UGameInstanceSubSystems will not be called.

1 Like

Wow, this is amazing an helped me a lot,

Making the C++ class abstract so the Blueprint is used is genius!

Thanks you!

Hello.
I have the same problem, BP subsystem only works if you ever opened it once in the editor, and it does not work in standalone game.
Have you solved the problem?

Nevermind. I solved the problem by using Async Load Class Asset node to load the blueprint of GameInstanceSubsystem in the Init event of GameInstance blueprint.
After that when in PIE or in standalone the subsystem blueprint works.

You can probably expose your UProperties in project settings: CustomSettings - Old UE4 Wiki
Shouldn’t need to create a new module, just use the one your subsystem is defined in. And you can probably use the subsystem class as the “settings” class directly, so you’re directly setting default values on the subsystem class properties.

2 Likes

Did you try compiling for mobile? I also tried doing that in a blueprint that inherits GameInstance but I think that broke the build.

Also having the same problem.

OK got it working, the build problem and the game instance no initiating was solved by the tips above.

After that I couldn’t call anything or register events in the inherited BP, solved by using UWorldSubsystem instead of UGameInstanceSubsystem. I don’t understand why it didn’t work with GameInstance but I’ll go with it for the moment.

I had the same problem with World Subsystem, but found a semi-standard way to ensure the blueprints are initialized through a Native subsystem. I did this for World Subsystems but it should work for other types.

/** Settings Object that you need to register on your game/plugin module's startup. */
UCLASS(config = Game, defaultconfig)
class UBlueprintSubsystemSettings : public UObject
{
	GENERATED_BODY()
public:
	UPROPERTY(Config, EditAnywhere)
	TArray<TSubclassOf<UWorldSubsystem>> BlueprintSubsystems;
};

/**
 * Blueprint subsystems are not discovered by the world on load.
 * This subsystem only exists to load the other subsystems in the world
 */
UCLASS()
class UWorldSubsystemBPInitializer : public UWorldSubsystem 
{
	GENERATED_BODY()
	virtual void Initialize(FSubsystemCollectionBase& Collection) override
	{
		const UBlueprintSubsystemSettings* Settings = GetDefault<UBlueprintSubsystemSettings>();
		for(auto Subsystem : Settings->BlueprintSubsystems)
		{
			Collection.InitializeDependency(Subsystem);
		}
	}
};

You need to register your custom settings in your module to get it to show up in the project settings.

void FYourGameModule::StartupModule()
{
	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
	{
		auto SettingsSection = SettingsModule->RegisterSettings("Project", "Game", "Subsystems",
																NSLOCTEXT("YourModule","BlueprintSubsystemSettingsName", "Blueprint World Subsystems"),
																NSLOCTEXT("YourModule","BlueprintSubsystemSettingsDesc",
																		"Add Blueprint Subsystems"),
																GetMutableDefault<UBlueprintSubsystemSettings>());

		// Register the save handler to your settings, you might want to use it to
		// validate those or just act to settings changes.
		if (SettingsSection.IsValid())
		{
			SettingsSection->OnModified().BindRaw(this, &FYourGameModule::HandleBlueprintSubsytemSettingsSaved);
		}
	}
}

void FYourGameModule::ShutdownModule()
{
	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
	{
		SettingsModule->UnregisterSettings("Project", "Game", "Subsystems");
	}
}

bool FYourGameModule::HandleBlueprintSubsytemSettingsSaved()
{
	UBlueprintSubsystemSettings* Settings = GetMutableDefault<UBlueprintSubsystemSettings>();
	bool Resave = false;

	// You can put any validation code in here and resave the settings in case an invalid
	// value has been entered

	if (Resave)
	{
		Settings->SaveConfig();
	}

	return true;
}
5 Likes