Delayed asset: drag & drop assets in inspector without automatically loading in memory

Some time ago I made this very little plugin, which allows to easily drag & drop in the inspector assets without causing them to be loaded in memory by Unity when the scene loads. Maybe there’s already something like that somewhere, but I just couldn’t find anything. I have decided to make it open source in case somebody finds it useful, or wants to test it, improve something, etc.:

As you may know, when you drop an asset to some object’s slot in the inspector, when the scene is loaded the asset (and all assets to which it has references, specially in the case of prefabs) will be loaded in memory automatically. It’s not a problem if the assets are small or are going to be used immediately, but for big assets, or many of them, that won’t be needed until later in the game, it’s an issue, since they will use a big chunk of memory even if they are not used.

The solution is to not use direct references to those assets, put them inside a Resources folder (so that Unity includes them in builds even if not directly referenced), and use “Resources.Load” to load them using a path. But this is not very convenient, you’d need to be careful when moving or renaming the assets to make sure to modify the paths used to load them, and of course you miss the nice drag & drop functionality in the inspector.

What this little plugin does is just that, allows you to directly drop in the inspector the assets that are in a Resources folder, plus allows you to move and rename them without having to keep track of anything (with a limitation explained in the readme file), while still avoiding Unity loading the asset in memory until you decide to do so.

Edit: now it’s possible to use assets that aren’t inside a Resources folder, with the help of the new class “DelayedAssetProxy”. Basically, a “DelayedAssetProxy” acts as a wrapper to hold the original assets; the “DelayedAssetProxy” must itself be inside a Resources folder, but the original asset can be anywhere.

The usage is explained in the readme file, but basically you’d use the type “DelayedAsset” for fields, instead of the normal asset type:

[SerializeField] DelayedAsset thing;
[SerializeField] DelayedAsset[] manyThings;

You can restrict the type of assets that are allowed on that slot by using the attribute “DelayedAssetType”:

[SerializeField] [DelayedAssetType(typeof(GameObject))] DelayedAsset gameObjectReference;
[DelayedAssetType(typeof(Material))] public DelayedAsset materialReference;

Edit: now those slots will also allow assigning a “DelayedAssetProxy” which holds an asset of the desired type.

And to load the original asset you have to use the “Load” method, and “Unload” to unload it from memory:

[SerializeField] [DelayedAssetType(typeof(Transform))] DelayedAsset transformReference;

void Awake()
{
    // Load the original asset:
    Transform transf = (Transform)transformReference.Load();

    if (transf != null)
    {
        // If existing, add an instance to the scene:
        Instantiate(transf.gameObject);
  
        // Unload the original asset if desired:
        transformReference.Unload();
    }
}
5 Likes

The fact that this was posted only recently is errie because this IS something I’ve always wanted to find a nicer solution to, and I just happened to give it a random google search again.
In other words, thank you :slight_smile:

1 Like

Glad somebody finds it useful! If you just downloaded the Unity package file from the repository, you may want to redownload it again, since last time I forgot to update it with something I had added, the method “LoadAsync”, in case you want to have it. If you find any issue, let me know.

I have updated the plugin to add the “DelayedAssetProxy” class, which can be used if it’s not possible to have the original assets inside a Resources folder, acting as a wrapper to the original assets that can be anywhere.

The way to use it is to create a “DelayedAssetProxy” file inside a Resources folder (right-click in the project window, select “Create → Delayed Asset Proxy”), and assign the original asset to the “Asset” slot. Now it’ll be possible to assign the “DelayedAssetProxy” file to any “DelayedAsset” slot that accepts the original asset, and only of the specified type if it was set. For example:

[SerializeField] [DelayedAssetType(typeof(Texture))] DelayedAsset delayedTexture;

will accept a texture asset, or a “DelayedAssetProxy” holding a texture. No additional code is needed to load the original asset, it’s completely transparent for the caller.

There’s a breaking change to unload, though: instead of unloading the assets using “Resources.UnloadAsset()”, it’s necessary to use “DelayedAsset.Unload()” (should be used whether a normal asset or a “DelayedAssetProxy” is used).

1 Like

This is exactly the type of thing I just finished writing up myself.
The idea of specifying a DelayedAssetType is really nice. I also didn’t think of trying to introduce an editor only fields and just made a property drawer for displaying in inspector. I need to try those things out myself.
Thanks A lot for the post.

One of the problems I had and from a quick look at your code you might have as well is sub-assets like Sprites.
If you want to grab a specific Sprite from a multi-sprite texture asset (single sprite texture should still work fine for you), you need to use LoadAll with Sprite type and match one in the array by name. The way I detect if a sub-asset name is required is by checking if real asset object name matches the name of the asset I get from AssetDatabase, as default loading a sprite’s asset path will give you the texture asset and their names should be different.

One other thing I did and found it useful was only storing Unity’s asset Guid instead of the actual path. I have a ScriptableObject that acts as a database for the final asset paths. It is maintained in edit mode and that allows you to move assets around or rename them and not break the referencing, as the asset Guids should not change (can’t rename Sprites though).

You can have a look into my solution if you’re interested. My code is not just for Resource folder assets but for AssetBundle assets as well, so it’s definitely messier and not commented as well as yours.

https://github.com/JuliusNarvilas/Share/raw/master/AssetReferenceTest.unitypackage

Yes, I didn’t take into account subassets, that’s a current limitation. I also thought about expanding the tool to handle asset bundles, but couldn’t find the time :frowning:

Regarding the asset paths, take into account that they are not used to keep track of the assets in the editor, only to load them at runtime, and are updated when Unity performs serialization; thus, changing the assets’ paths or names is not an issue, they are always correctly tracked since I maintain the direct references in the editor.

Do you not find that these references in other scenes (scene you don’t open) don’t update?
Do you find that the object (that exists in the editor) does serialization during the build process or something?
My expectation would be that data in scenes you don’t open would stay with the old path strings.

Definitely getting ideas on how to improve my code from this, thanks.

The DelayedAsset instances are correctly serialized even if they are not in the current scene, because the class implements the interface ISerializationCallbackReceiver. So when Unity performs the build process, it serializes the scenes and everything inside, previously calling the method ISerializationCallbackReceiver.OnBeforeSerialize that DelayedAsset implements, which takes the instance of the asset and saves its path; so it’s always the path at the moment of building. Of course the key part is having the reference to the asset only available in the editor, so that it’s not loaded at runtime (what we want to avoid).