Soft class reference showing up as a hard reference or possibly a transient property getting stored

Hello,

What seemed to be a simple solution turned out to be a bigger problem. We tried to change some hard references to soft references, but also make sure the objects stay loaded after we load them once in our game. So in the structs where we used to have a hard reference, we created one soft reference and transient hard reference. We set the soft reference and everything looked correct. Once we loaded the soft referenced object, we stored the reference in a mutable variable to prevent it from being garbage collected even when nothing else references it. Imagine some game object that is created from time to time, but not persistent in the game. Basically a quick and dirty caching mechanism. But after a while we noticed that blueprint that should be using only soft reference, show hard references in the Reference Viewer window. It looks like when we run the game in PIE and store the loaded object in the transient variable, a hard refence is added into the blueprint dependency list, and if we save the blueprint after that happens, the hard dependency is there forever.

Please see attached project with really simple example how it happens. If you open BP_TestAsset, you will see one property that should be a soft reference. But when you open Reference viewer, you will see that BP_TestAsset hard references that object. It’s because the “caching” part runs when the level starts.

Q: Why does the blueprint store the hard refence in it’s dependency list even when the property is marked transient? I would like the understand why it’s happening, so we can avoid a similar situation in future.

Thank you

Steps to Reproduce
Create a blueprint type struct that has two variables similar to these:

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Equipment)

TSoftClassPtr<AActor> ActorToSpawn;

UPROPERTY(Transient)

mutable TSubclassOf<AActor> CachedActorToSpawn;

Store that structure in a blueprintable class.

Create a C++ function which loads the ActorToSpawn and stores the value in the CachedActorToSpawn.

Open the editor, create a blueprint inheriting from your new C++ class and the set the value of the ActorToSpawn to some actor from your project.

Check the reference viewer - there will be a soft reference from your blueprint to that actor.

Now run the C++ function which load the actor and stores it in a cache.

Check the reference viewer again - there will be hard reference where it used to be the soft reference.

See BP_TestAsset in the attached project for example.

Hi Jiri,

The short story is that the BP asset is acting as a CDO so anything that is set on it will get saved. From the C++ standpoint, you would be creating and assigning a subobject in the constructor of the class. Instances of BPs (in the case of actors) will respect the transient nature of the properties.

In this case, the base class is a DataAsset which can be created directly as an asset. Those will respect the transient nature of properties. Things get trickier if you want your content people to extend a base PrimaryDataAsset using BP. A workaround would be to override the PreSave and clear the CachedActorToSpawn property.

Regards,

Martin

Hi Martin,

Thank you for the explanation.