This post is an overvies of the World Partition System.
1. Actors in World Partition
Actors in world partition need to be attached to a certain grid it belongs to, fortunately, it is an automatic befaviour and doesn’t require for extra work.
Actors can be added anywhere and are automatically assigned to a grid cell based on their Is Spatially Loaded setting, found in their Details panel’s World Partition section.
The first property is the “Runtime Grid“, we shall discuss about later, here, we just need to know that it determines in which the grid the actor is placed in, if it is “none” then the actor will be chosen by the partition system.
The second property is the “Is Spatially Loaded”, it determines whether the actor is spatially loaded.
- If enabled, the actor will be loaded if in range of any streaming source, and not assigned to a disabled data layer.
- If disabled, the actor will be loaded if not assigned to a disabled data layer.
For now, we don’t know what the data layer is, which we will discuss about in the next post, to be simple, if eliminate the influence by the data layer, enable “Is Spatially Loaded” means it will not be loaded until there is a streaming source nearby, while disable it means it will always be loaded.
Let’ make a experiment:
I placed two static mesh actors in grid(6, 7), the player controller will be placed in gird(-6, -7)
Set the “Is Spetially Loaded” as false. Start the game, there is no static mesh actors in the outliner.
That is because the player controller is now the streaming source and is too far away from the actor, the grid which contains the actor has not been loaded, move the player controller to grid(6, 7), you will find the actor in the outliner.
However, is disable “Is Spetially Loaded”, you can find the actors in the outliner if the distance between the actors and their neasest steaming source is the whole map size.
2. Streaming Source
So, how does streaming source work? Streaming of grid cells within the grid at runtime is determined by two factors:
- Streaming Sources
- Runtime Grid Settings
To visiable the streaming of grid cells within runtime, type wp.runtime.ToggleDrawRuntimeHash2D/3D in the console.
Streaming Source
We mentioned “Streaming Source” just before, so, what is a streming source?
Generally speaking, to appoint an actor as a streaming source, what you need to do is implment the following interface:
/** * Interface for world partition streaming sources */ struct ENGINE_API IWorldPartitionStreamingSourceProvider { virtual bool GetStreamingSource(FWorldPartitionStreamingSource& StreamingSource) const = 0; };
An example is the UWorldParitionSourceComponent:
bool UWorldPartitionStreamingSourceComponent::GetStreamingSource(FWorldPartitionStreamingSource& OutStreamingSource) const { if (bStreamingSourceEnabled) { AActor* Actor = GetOwner(); OutStreamingSource.Name = *Actor->GetActorNameOrLabel(); OutStreamingSource.Location = Actor->GetActorLocation(); OutStreamingSource.Rotation = Actor->GetActorRotation(); OutStreamingSource.TargetState = TargetState; OutStreamingSource.DebugColor = DebugColor; OutStreamingSource.TargetGrid = TargetGrid; OutStreamingSource.TargetHLODLayer = TargetHLODLayer; OutStreamingSource.Shapes = Shapes; OutStreamingSource.Priority = Priority; return true; } return false; }
Then register your IWorldPartitionStremingSourceProvider to UWorldParitionSubsystem:
UWorldPartitionSubsystem* WorldPartitionSubsystem = GetWorld()->GetSubsystem<UWorldPartitionSubsystem>(); check(WorldPartitionSubsystem); WorldPartitionSubsystem->RegisterStreamingSourceProvider(this);
Streaming is an asyncchronous method, you can use UWorldParitionSubsystem::IsStreamingCompleted to check if the streming is completed, there is also a shortcut method in UWorldPartitionStreamingSourceComponent:
bool UWorldPartitionSubsystem::IsStreamingCompleted(const IWorldPartitionStreamingSourceProvider* InStreamingSourceProvider) const { // Convert specified/optional streaming source provider to a world partition // streaming source and pass it along to each registered world partition FWorldPartitionStreamingSource StreamingSource; FWorldPartitionStreamingSource* StreamingSourcePtr = nullptr; if (InStreamingSourceProvider) { StreamingSourcePtr = &StreamingSource; if (!InStreamingSourceProvider->GetStreamingSource(StreamingSource)) { return true; } } for (UWorldPartition* RegisteredWorldPartition : RegisteredWorldPartitions) { if (!RegisteredWorldPartition->IsStreamingCompleted(StreamingSourcePtr)) { return false; } } return true; }
You can attach this component to your PlayerController or Character, which allows them to trigger the streaming of the world partition.
Some of the properties of UWorldPartitionStreamingSourceComponent is as followed:
Option | Description |
---|---|
Default Visualizer Loading Range | Determines the size of the debug visualizer grid when the visualizer is enabled. |
Target Grid | Determines the streaming grid affected by this source. |
Debug Color | Determines the color used for debugging. |
Target HLOD Layer | Determines the HLOD Layer affected by the streaming source. |
Shapes | Determines the shape list used to build a custom shape for this streaming source. If empty, will use a sphere with a radius equal to the grid loading range. |
Priority | Determines the priority of the streaming source. If a grid cell intersects multiple streaming sources, its priority will be the highest priority amongst all streaming sources. |
Streaming Source Enabled | Determines if this component is enabled. |
Target State | Determines which state the intersecting grid cell should be in (either Loaded or Activated). If a grid cell intersects multiple streaming sources, the target state will be the highest target value (where activated is greater than loaded). |
Note that Loaded and Activated are different states, Loaded means that the grid cell is now loaded bu still not visiable, Activated means loaded and visiable.
Runtime Grid Setting
The second factor that determines whether a grid cell is loaded or unloaded at runtime is the settings of the runtime grid itself. Runtime grid settings are located in the World Settings panel, in the World Partition Setup section.
A 2D Runtime Hash grid is provided by default. Using more than one grid can negatively impact performance.
Option | Description |
---|---|
Grid Name | Contains the name of the runtime grid. |
Cell Size | Determines the size of the grid cells that are used to generate the streaming Levels. In the example, the Cell Size is 256 square meters. |
Loading Range | Determines the range from a streaming source where cells are loaded. In the image above, the Loading Range is a 768 meter radius around a streaming source. |
Block on Slow Streaming | Blocks loading in situations where grid cells are not loading fast enough. |
Priority | Determines the priority of the streaming source. If a grid cell intersects multiple streaming sources, its priority will be the highest priority amongst all streaming sources. |
Debug Color | Determines the color of the grid lines that are shown when Preview Grids is enabled. |
Preview Grids | When enabled, displays the grid lines in the viewport. |
That is the overview of World Partition, in future posts, we are going to discuss about HLoDs, World Partition Nav, Data Layer and so on.
3. Data Layer
Data Layers is a system whihin world partitionm, which allows you organize your actors both in runtime and editor time, although the Data Layer usually used as a subsystem of world partition, there are still reasons for using Data Layers without using World Partition(But must enable World Partition).
Using Data Layer Assets and Data Layer Instances, you can dynamically load and unload layers in the Editor to help manage complex worlds. This system serves as a replacement to the previous Layers system found in older versions of Unreal Engine.
With Data Layers, you can separate gameplay elements and environment assets in the Editor. Artists can work on specific elements without interacting with gameplay triggers or objects. Designers can use dynamically loaded layers to design intriguing gameplay and elaborate level transitions.
At runtime, you can toggle Data Layers using Blueprints or C++ code to drive gameplay (such as quests, progression, and in-game events). They are an important tool for managing asset streaming in a World Partition workflow.
In Unreal Engine 5, using Data Layers requires for creating Data Layer asset, note that although there is only data-only member and some simple methods in Data Layer Asset, UDataLayer is not a UDataAssets, UDataLayer is an UObject rather than a UDataLayer with its own Factory class, the class is speicified by EditInlineNew for creating directly in the editor:
UCLASS(BlueprintType, editinlinenew) class ENGINE_API UDataLayerAsset : public UObject { GENERATED_UCLASS_BODY() friend class UDataLayerConversionInfo; #if WITH_EDITOR //~ Begin UObject Interface virtual void PostLoad() override; //~ End UObject Interface public: void SetType(EDataLayerType Type) { DataLayerType = Type; } void SetDebugColor(FColor InDebugColor) { DebugColor = InDebugColor; } #endif UFUNCTION(Category = "Data Layer", BlueprintCallable) EDataLayerType GetType() const { return DataLayerType; } UFUNCTION(Category = "Data Layer|Runtime", BlueprintCallable) bool IsRuntime() const { return DataLayerType == EDataLayerType::Runtime; } UFUNCTION(Category = "Data Layer|Runtime", BlueprintCallable) FColor GetDebugColor() const { return DebugColor; } private: /** Whether the Data Layer affects actor runtime loading */ UPROPERTY(Category = "Data Layer", EditAnywhere) EDataLayerType DataLayerType; UPROPERTY(Category = "Data Layer|Runtime", EditAnywhere) FColor DebugColor; };
Data layer can has a treeview like:
When parent data layer disabled, all of the children layer will be disabled at the same time.
As is seen from its souce code in C++, data layer could be runtime or editor, what makes them different is that runtime data layer could be loaded or unloaded by C++ or blueprint in runtime, an editor daya layer cannot be attached ro a runtime data layer.
Data Layers in Gameplay
Runtime data layer can helps you create complex, advanced gameplay feature, for example, Valley of the Ancient uses it to accomplish the transition of the Light World and the Dark World without changing level.
What’s more, you can attach a data layer to your gameplay mission if you are creating a RPG game, for example, a NPC only appears in game when you are doing a mission, you can enable the data layer of the mission, and disabled it when the mission finished.
Consider about this: You are making a RPG, when player accepts a mission, there will be a sign on the minimap on the HUD, the sign represents a moving actor 1 km away.
We created a UDynamicMinimapSignComponent to accomplish the sign, and attach it to the actor, but the actor is too far away, its grid cell is not loaded, so that the actor will be disabled.
How to fix this? as we discussed before, we can set Is Spatially Loaded to false. And set the actor to your mission data layer, enable it when the mission starts.
Data Layer Instances
Data Layer Instances are world-specific instances of a Data Layer Asset. Using instances, a Data Layer Asset can exist in two or more worlds and reference the same assets but specify different instance properties.
For example, you create a neighborhood point of interest (POI) in a world and assign those assets to an Editor Data Layer to keep them organized. You then decorate the POI using holiday-specific assets and assign those to a Runtime Data Layer to enable them to be loaded and unloaded during runtime. In the standard version of your world, you set a Data Layer Instance of the Runtime Data Layer to not be loaded by default and to toggle on a specific date to celebrate an in-game holiday. In the same project, you can then use a separate instance of the Runtime Data Layer that is always enabled to create a holiday-themed version of the POI separate from your standard world.