How long is a TSoftObjectPtr valid?

According to docs if you do an async load on a reference from TSoftObjectPtr the resulting reference is guaranteed only upto the point the delegate is executed.

However this means that you need to store that soft/weak reference to a hard reference if you want it to persist past the delegate call or cache it for later use.

The obvious downside is that you now have to maintain two variables one the TSoftObjectPtr reference that was used to assign that asset reference and then another hard reference (UPROPERTY or Shared ptr) to actually keep it safe after its loaded. This might not be ideal and causes problems with naming conventions of variables

Is there a way to keep the TSoftObjectPtr loaded persistently and avoid using a second variable as hard reference ?

I’ve always felt the TSoftObjectPtr isn’t meant to be the normal reference to the object, more a helper. You have to ground it somewhere to avoid GC. There are some hack’y ways around this but you’re better off storing a hard reference to the object.

That is true, but it also means that you gotta maintain two vars for each object , one for soft reference and one for hard. I looked into encapsulating the template with a shared ptr but the way engine handles TSoftObjectPtr as UPROPERTY internally it makes the change even more unfeasible than having two vars.

This should be a requested feature where a container should exist than can be used as UPROPERTY template in editor as well as be convertible to hard reference when needed. Until then we gotta keep hidden hard refs

That’s pretty much the point of it IIRC?

When you load something, you need to keep a hard reference to it somewhere or the engine will unload it. The SoftPtr exists so that you don’t keep the item loaded when it’s not in use, same way weak pointers are cleared when they are the only reference to something.

Exactly, the TSoftObjectPtr is just a clean helper for loading content. You still need to keep the hard reference to the object being loaded. If you really want to avoid having the hard reference, you could try the FGCObject approach and use AddReferencedObjects() to keep the soft reference alive. That ugly and it really defeats the purpose of soft references but it should work.

Well yeah , but I’m talking about something that uses SoftPtr as a part of it and handles storing that hard reference internally automatically such that you can have the advantages of both the async loading and direct assignment in editor without having to manually assign a hard reference for each asset you need.

Especially because you cannot create templatized UPROPERTY yourself without doing some major additions in various parts of core of the engine and more often than not you’d want the async loaded content to stay around so that case is not adequetly covered with async loading containers.

You don’t need a templated UProperty, you can just store it as a UObject* (which all TSoftObjectPtrs must be) and that should be it.




UPROPERTY()
TSoftObjectPtr MyAsset;

UPROPERTY()
UObject* MyLoadedAsset; // Set the loaded asset returned by MyAsset to this.



1 Like

I think he’s looking for a way to avoid the double declaration + Bookkeeping.

FAssetData;
https://api.unrealengine.com/INT/API/Runtime/AssetRegistry/FAssetData/index.html

.GetAsset() will load the object and return a pointer to it.
.ToSoftObjectPath() can be used to create a SoftObjectPtr().

Yes! it feels like boilerplate when you have declare variable for hard reference again and then assign to it in the delegate call, especially in cases where you want the object to persist after load and only looking for async load for saving load time hitches instead of unloading it before time.

Maybe the use case engine developers had in mind was to load something aync , use it within the delegate and then leave it to the GC to dispose of it.

I could bundle all dynamically loadable objects in a container uclass and then use a soft reference to that container which will reduce double declaration to only 1 var but with many classes would bloat the code fast.

Hello! Wow I’m so excited to share this with you. I hope it can still help you. I had been scratching my head over this same problem. I wanted to async load large assets at specific points in time and ensure they stayed in memory. It would also be ideal for this memory to be freed when the Actor that was using the asset gets destroyed. I can’t understand why there isn’t practically any documentation or forums talking about this. Anyways, I really hope this helps with the book keeping! Steps to follow:

Step 1: Define a Streamable manager in a Singleton, ideally your game instance class.

Step 2: On your component / Actor, have a property like this:

Step 3: Load your soft object pointer like this asynchronously. In the component / Class where you have declared your handle.


It is key that you set the parameter which corresponds to ManageActiveHandle to true. What this will do is ensure the handle does not get cleared after assets have been loaded. You must clear it explicitly. In my case that is not even necessary because it will get cleared when my Actor / Component gets destroyed, just like with any hard reference. Just check here in the documentation what the parameter implies.

Step 4:
After this is done, all is well and good. Now you know, whenever you want to access your async loaded asset, you can simply do it with a Get(), and assume the weakObjectPtr always will point to it.

I hope this helps you! This makes the code cleaner and in case you have multiple SoftObjectPtrs of different types in a component / class, you only have to declare a single handle to make sure they all remain loaded in memory.

Bare in mind you have to first group all these softObjectPaths together and call ResquestAsyncLoad for the entire list, like so:

Anyways! Good luck! I hope you get to read this and it avoids some suffering :slight_smile: