What is a Subsystem? What should it be used for?

Unreal 4.22 introduced the new “subsystem” class, but there’s not much documentation as to what it exactly is or how to use it. The release notes simply say:

Subsystems are automatically instanced classes with managed lifetimes which provide easy to use extension points without the complexity of modifying or overriding engine classes, while simultaneously getting Blueprint and Python exposure out of the box.

But that doesn’t really answer my question too much. Say I want to replicate ShooterGameInstance from the ShooterGame example using subsystems. Is it as easy as just migrating all the ShooterGameInstance code into a class inheriting from UGameInstanceSubsystem? How do I “tell” the GameInstance that it’s using a certain subsystem without “modifying or overriding” an engine class?

I understand how to access the subsystem from C++/Blueprints, but I don’t understand when you’d want to use one or how to “point” a GameInstance/LocalPlayer/etc. at a certain subsystem, or what a subsystem is supposed to “do.”

Okay, poking through engine code has led to a couple discoveries:

  • Each object which can have a subsystem has a FSubsystemCollection associated with it. This FSubsystemCollection takes a template, so to use my GameInstance example, UGameInstance contains FSubsystemCollection<UGameInstanceSubsystem>.
  • When the GameInstance Init function gets called, the FSubsystemCollection looks at everything which inherits from UGameInstanceSubsystem (or whatever subsystem it’s using at the time). If USubsystem->ShouldCreateSubsystem returns true, then it’ll create the associated subsystem as a new object, parented to whatever the FSubsystemCollection says its parent should be ( UGameInstance, in our case). So in reality, all our subsystems get created as children of our main system.
  • The end result is that you can call UGameInstance->GetSubsystem with any valid child class of UGameInstanceSubsystem and it’ll return an instance of that class. It’s similar to having a component on an actor.
  • By itself, a subsystem doesn’t actually do anything. Nothing gets called on it, and it doesn’t override any functions. You have Initialize and Deinitialize to start and stop the subsystem, but otherwise it’s an open book as to what you do with it. It just makes code clearer for non-actor objects which could benefit from a component-like interface.
2 Likes

Excuse me, could you describe it in more detail, or give me another example? I still don’t quite understand the usage and convenience of subsystems.

It just makes code clearer for non-actor objects which could benefit from a component-like interface.
Sorry, I didn’t catch that. Could you elaborate via an example for such a thing?
Other than Online modules and systems that run game-wide across all actors subsystems just seem like they don’t have any other real use cases, except if you want to encapsulate something really strongly since subsystems can contain and mutate local state. That’s one cool think I really do like about it but that’s basically it, right? Except the automatically managed lifetime etc.

They are good for singletons. (see Ben UI’s article: https://benui.ca/unreal/subsystem-singleton/)
They should also work for Service Locators (if it works for Singletons, it should work for Service Locators).
I am curious if they are good candidates for managing Object Pools, if you stored the object references in a Map object it would work. You’ll take a hit on the Map’s lookup, but I don’t know if the hit would be a concern, I suppose it depends on the game and how many object pools the subsystem needs to store. Also I expect you would want to use weak object references, but if the pool manager is a UWorldSubsystem it probably wouldn’t need weak references. Perhaps ask someone with more experience regarding object pools, though, that was just a thought I had, but I haven’t actually tried it out and may be missing some other drawbacks to the approach besides the Map lookup. Most likely having a pool of objects on the Actor that needs those objects would be better since you wouldn’t need the Map lookup in that case.
I expect any kind of singleton-like solution where you need an object to be accessible in a more global way would fit the mold of a Subsystem. You could use it to write an Event Bus where the Subsystem is the Bus that the events are fired from. If not an Event Bus, then a kind of global Observable manager, where functions on the Subsystem are called from one object to notify registered observers stored as weak references in an array of “observers”.
I hope that gives some ideas. For anyone needing to study programming patterns, I recommend the book “Game Programming Patterns”, which is free online:
http://gameprogrammingpatterns.com/
Also, there is a book coming out in 2024 that covers Game Programming Patterns for Unreal Engine 5: Game Development Patterns with Unreal Engine 5: Build maintainable and scalable systems with C++ and Blueprint
Who knows, the author might use Subsystems as solutions for some of the patterns.

I use subsystems to extend functionality in an accessible way, so that I do not have to create gigantic code heavy subclasses of primary Unreal classes like GameMode and GameInstance.

You can also use custom UInterfaces with subsystem to create stub and test versions of complex classes, such as external APIs. You check if you’re building development vs shipping and instantiate your local development version of the subsystem instead, since all functions pass through your custom UInterface.

Something like an object pool that would be a good WorldSubsytem. You can get the subsystem anywhere you’d need it, C++ or Blueprint without hard references. As for Weak Pointers, etc, use Unreal’s version and you should be fine. Most of the work and checking should occur in your object pool factory function(s).