There are quite long time ago but I’ve got the same problem in Unreal Engine 5. I’m using Unreal Engine version 5.3.2 and found that any Unreal assets that that have TSoftObjectPtr into it are not being unload in the editor because of editor are being owner of it’s instance so it will not unload by garbage collector. But in the game build, any Unreal assets doesn’t have the owner so when soft pointer are reference to it, it will synchronous load and being collected by garbage collector later in few seconds and will be synchronous load again each time they want to use. So the problem is if you’re using TSoftObjectPtr, after being loaded from asynchronous or synchronous, the loaded object should be put into TObjectPtr for hard reference the loaded object to prevent the garbage collector to collect it.
For example for the bad usage of TSoftObjectPtr that cause spike freeze lag:
.h
UCLASS(BlueprintType)
class MY_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Soft object pointer to the asset in the project.
UPROPERTY(EditAnywhere)
TSoftObjectPtr<UMyDataAsset> MyAssetSoftPtr;
// Get the assets.
UFUNCTION(BlueprintPure)
UMyDataAsset* GetMyAsset();
}
.cpp
UMyDataAsset* AMyActor::GetMyAsset()
{
if (UMyDataAsset* MyAsset = MyAssetSoftPtr->LoadSynchronous()) // Call load synchronous whatever the object is loaded or not, will always return the reference of the object in the project. But in the build, the weak reference that being loaded in the 'MyAssetSoftPtr' may stale and become nullptr and may load again when it's needed. This will cause spike lag when the game engine calls 'FlshAsyncLoading'.
{
return MyAsset; // Return the loaded object.
}
UE_LOG(LogTemp, Error, TEXT("Cannot get UMyDataAsset."));
return nullptr;
}
And the fix issue of spike lag by cache the hard reference to TObjectPtr:
.h
UCLASS(BlueprintType)
class MY_API AMyActor : public UDataAsset
{
GENERATED_BODY()
public:
// Soft object pointer to the asset in the project.
UPROPERTY(EditAnywhere)
TSoftObjectPtr<UMyDataAsset> MyAssetSoftPtr;
// Use 'BeginPlay' to initialize load object.
virtual void BeginPlay() override;
// Get the assets.
UFUNCTION(BlueprintPure)
UMyDataAsset* GetMyAsset();
private:
// Cached reference of object that may loaded from soft pointer.
UPROPERTY()
TObjectPtr<UMyDataAsset> MyAsset;
}
.cpp
void AMyActor::BeginPlay()
{
Super::BeginPlay();
MyAsset = MyAssetSoftPtr->LoadSynchronous(); // Load the object from any initial events such as 'BeginPlay'
}
UMyDataAsset* AMyActor::GetMyAsset()
{
if (MyAsset) // Always check if it's nullptr.
{
return MyAsset; // Using the cache loaded object from soft pointer.
}
UE_LOG(LogTemp, Error, TEXT("Cannot get UMyDataAsset."));
return nullptr;
}