The preview video for UE5 introduces a modular development mechanism for GamePlay, similar to mod-style development and management of game features and resources, referred to as “Game Feature”. This has already been enabled in UE4.27 and UE5. I think this new modular gameplay format is great, so I’ve conducted a technical research.
This article introduces the activation process and operational mechanism of Game Features, and at the end shares a demo based on Game Features. Additionally, I implemented a mechanism in HotPatcher that allows features to be packaged independently; Game Features do not need to be pre-packaged into the base package and can be downloaded and loaded on demand during runtime, achieving true modular loading.
Currently, the UE5 EA version still has some imperfections, and related content will be supplemented as the engine updates.
The problem that Game Feature aims to solve is the modularization of Game Play, aggregating certain functions and associated resources as a separate feature that can be loaded when needed and unloaded when not needed. This allows for dynamic management of game runtime resources without relying on a complete set of game resources at startup.
This is similar to a hot update mechanism, because Game Feature is essentially a set of resource packages; however, UE adds dynamic execution logic on top of the resource package, allowing one to conveniently add functionalities to existing actors, and some small gameplay can be made into a standalone Game Feature, which even has a flavor of ECS. But unlike hot updates, the main idea of Game Feature is to dynamically add and unload functionalities at runtime rather than replace existing resources and functionalities.
The implementation of UE’s Game Feature is based on the Plugin
system, where each Game Feature can be regarded as a Content Only plugin that manages a number of resources and pluggable game logic through GameFeatureData
to manage resources and behaviors of the feature.
However, currently, it seems that UE’s implementation only treats it as a modular management mechanism without the ability to package it separately. Through research, it’s also possible to implement dynamic downloading and enabling of Game Features. I realized packaging of Game Features based on HotPatcher, allowing any Game Feature to be packaged separately, downloaded as needed, and enabled in the game. The specific introduction can be found in 独立打包GameFeature.
General Introduction
Activation and Creation
First, enable the Game Feature
and Modular Feature
plugins:
After enabling, there will be a GameFeature
setting option in the project settings:
Creating a new GameFeature requires creating a plugin, and you can see the Game Feature(Content Only)
plugin template:
This will create a plugin located in the project’s Plugins/GameFeatures
directory:
1 | C:\Users\lipengzha\Documents\Unreal Projects\ThirdPerson_UE5\Plugins\GameFeatures>tree /a /f |
Its uplugin content is as follows:
1 | { |
Create a resource of GameFeatureData
in the root directory of the plugin’s Content; note that it must have the same name as the plugin:
In the EditPlugin options, you can set the initial state of the current Game Feature. If set to Active, it will automatically load and activate by default when the engine starts. It is recommended to set it to Registered
during editor editing; if set to Installed
, the engine will not load the plugin at startup, making it invisible in the Content Browser and unable to be packaged:
If the Initial State
of the Game Feature
is Active
, it will auto-load upon engine startup, as shown in the call stack:
The states a Game Feature can have are:
1 | /** The states a game feature plugin can be in before fully active */ |
Common operations on the business side include:
- load
- activate
- deactivate
- unload
To call them, you need to pass the PluginURL
, which is the path to the uplugin
file, and you can obtain it by using:
1 | GameFeatureSubsystem->GetPluginURLForBuiltInPluginByName(PluginName,OutPluginURL); |
These functions are not exposed to Blueprints, so you can create a wrapper (note to add module dependencies for GameFeatures
and Projects
):
1 | // .h |
While editing in the engine, you can set the Feature to Active
, which will auto-load it at engine startup and display it in the Content Browser; otherwise, it will not display or be editable if it hasn’t been loaded.
Feature Actions
You can add Action
and resources managed by the current GameFeature in the configuration of Game Feature to implement the necessary modular functionality.
For example, with Add Component
, you can automatically create a component on a specified class:
When the Feature is loaded and set to Active, it will automatically add that component to all instances of the specified Actor Class.
Note, you need to pre-add the target Actor to Receiver
:
When the Feature is Deactive, that component will be removed from the Actor, allowing custom functionality to be executed in the component’s BeginPlay
/EndPlay
.
Based on the function library above, I’ve created a test loading Feature UMG:
Feature loading test:
I created a BP_Component
component and added it to BP_Pawn
; in the component’s BeginPlay
/EndPlay
, I create and remove a UMG:
Runtime effect:
Game Features Settings
In UE5’s Project Settings
- Game Features
, you can set GameFeatureManagerClass
, which is used to launch the GameFeature
in the project, allowing you to control which Features
are loaded.
We can also inherit UGameFeaturesProjectPolicies
to implement our own control flow:
1 | void UGameFeaturesUtilsProjectPolicies::InitGameFeatureManager() |
By passing a predicate in LoadBuiltInGameFeaturePlugins
, you can control the loading of Features, and during Feature loading, this callback will decide whether to load:
1 | void UGameFeaturesSubsystem::LoadBuiltInGameFeaturePlugin(const TSharedRef<IPlugin>& Plugin, FBuiltInPluginAdditionalFilters AdditionalFilter) |
Other Resources
Epic has also released two videos introducing Game Features:
- Feature Introduction: Modular Game Functions in UE5: Plug and Play, The Way of Unreal (Official Subtitles)
- English Live Broadcast: Modular Game Features (Official Subtitles)
Demo
To demonstrate the usage of Game Feature, I created a demo using the Game Feature mechanism to implement a simple gameplay example:
- Load the Feature while randomly creating 30 collectible items in the scene.
- A countdown of 20 seconds for the player to collect items in the scene.
- Score recording.
- Clear all created Actors when the timer ends.
All functionalities and resources are within the GF_Feature Game Feature plugin, making it an independent module for plug-and-play in the game.
Demo video:
Download the Demo: GameFeatureDemo_BlankUE5.7z
Packaging
Into the Base Package
After creating the Game Feature plugin, restarting the project will prompt an Add entry to PrimaryAssetTypesToScan
. Clicking it will automatically add it to the project’s Asset Manager
, where there will be a GameFeatureData
option to manage packaged Game Features:
When packaging, you need to set the Init State
of the Game Feature to Registered
to ensure that the Feature’s plugin will be loaded during packaging; otherwise, the Feature cannot be packaged.
Independent Packaging of Game Feature
In the previous section, after adding the configuration of GameFeatureData to AssetManager, when packaging the base package, the Features set to Registered
will be packaged along with it.
However, since this is Modular Gameplay, is it feasible not to package Game Features into the base package and to download and load them on demand at runtime? The answer is definitely yes, because Game Feature is essentially a Content Only Plugin, so you can package the resources within the plugin separately and mount the plugin during runtime.
Based on HotPatcher, I implemented a module for independently packaging Game Features that can be packaged into a pak for any Content Only plugin, mounted at runtime.
It can package the needed plugin information, including:
- The plugin’s uplugin
- All resources within the plugin
- The AssetRegistry of resources in the plugin
- The ShaderLibrary compiled for resources in the plugin
The engine relies on this data to load plugins and their resources. I will analyze the specific loading process and code details when I have time.
Taking the GF_Feature
in the demo as an example, the files included after packaging are:
1 | D:\UnrealProjects\BlankExample\Package\GF_Feature\Windows>"C:\Program Files\Epic Games\UE_5.0ea\Engine\Binaries\Win64\UnrealPak.exe" -list GF_Feature_Windows_001_P.pak |
You can mount this pak to the game at runtime so that UFS can find the Feature’s files.
However, there are three issues that need to be resolved at runtime:
- Dynamically downloaded Feature plugins for the engine to recognize.
- Allow the engine to index resources in the plugin.
- Load the Shader Library for resources in the plugin.
In previous notes, I analyzed the engine’s plugin loading process. By default, a upluginmanifest
file is generated with information about all plugins in the project, which allows the engine to determine which plugins are enabled at startup. Relevant wiki: upluginmanifest, and the related code in the engine can be found in the PluginManager.cpp
‘s ReadAllPlugins
function.
The PluginManager also provides a method to load a specific plugin uplugin:
1 | /** |
You can dynamically add plugins that are not in the upluginmanifest
to the engine, and this interface is also called when the engine adds plugins:
Note: Plugins with C++ code cannot be added; only Content Only plugins are allowed.
Using the GF_Feature
as an example:
1 | IPluginManager::Get().AddToPluginsList(TEXT("../../../BlankExample/Plugins/GameFeatures/GF_Feature/GF_Feature.uplugin")); |
This adds GF_Feature
to the PluginManager as an enabled plugin in the game.
Next, you need to call the following function to register the GF plugin in the plugin list:
1 | IPluginManager::Get().MountNewlyCreatedPlugin(TEXT("GF_Feature")); |
Finally, to enable UE to find the resources in the plugin, the AssetRegistry.bin from the plugin needs to be added to the engine’s AssetRegistry; I encapsulated a method in the plugin:
1 | bool UFlibPakHelper::LoadAssetRegistry(const FString& LibraryName, const FString& LibraryDir); |
Taking GF_Feature
as an example:
1 | UFlibPakHelper::LoadAssetRegistry(TEXT("GF_Feature"),TEXT("../../../BlankExample/Plugins/GameFeatures/GF_Feature/")); |
This deserializes and adds all resource information in the plugin to the AssetRegistry module for Game Feature loading.
Note: The AssetRegistry is not necessary in normal games, but when Game Feature is activated, it will check whether resources exist in the AssetRegistry when loading plugin resources. Therefore, it is mandatory to load AssetRegistry data when Game Feature is enabled.
Lastly, for the Shader Library in the plugin, by default, the compiled Shaders for resources in the plugin are stored as a similarly named Shader Library. I also encapsulated a loading function:
1 | bool UFlibPakHelper::LoadShaderbytecode(TEXT("GF_Feature"),TEXT("../../../BlankExample/Plugins/GameFeatures/GF_Feature/")); |
For the detailed content of the Shader Library, you can refer to my previous article: UE Hot Update: Shader Update Strategy.
Once the three steps above are completed, you can use GameFeaturesSubsystem
to LoadGameFeature
, as if it has always existed in the base package.