Referencing assets without loading them automatically?

Hi there

I’m trying to store references to ScriptableObject assets in a different asset without them being loaded into memory. An example might be a loot system. Each item in the loot drop would be a ScriptableObject asset (eg. Loot). You have another asset that has a list of loot items that might be dropped (called DropPool). At runtime, you want to skim 3 or 4 loot items from the pool and load them. The rest of the items in the pool, you do not want loaded. How can I achieve this in a neater way than storing individual asset paths?

The following is an example in code

The scriptable object class I want to reference:

public class LootItem : ScriptableObject
{
    // Some code here
}

The scriptable object that references it:

public class DropPool : ScriptableObject
{
   public LootItem[] items;
}

Now if I have a project full of “LootItem” assets that have been saved out, I can then fill a “DropPool” asset with an array of references to these loot items. If my understanding is correct then loading the “DropPool” will currently also load every “LootItem” asset that it references. Can I prevent this so that I can load them seperately as required later on?

The only solution I can think of is to change the “DropPool” to the following:

public class DropPool : ScriptableObject
{
    public Guid[] items;
}

I would then have to write a custom editor to show an object field for each entry and pull the Guid from that. That seems like a pain just to get the ability to drag and drop my assets onto it.

Any help would be greatly appreciated

1 Like

I should point out that I want to use GUIDs instead of just storing the path so that I can reorder the asset folders without breaking all my references.

In my game the classes that would replace “LootItem” above reference a number of textures, models and audio files. As only a limited number will ever be used at any one time I don’t want to pull all of them into the scene when the “DropList” equivalent is loaded. I just want to grab one or two items from the list in that asset and load them as and when required.

I’ve been digging a little closer, and knocked out the following test code to demonstrate what I’m after:

using UnityEngine;
using UnityEditor;
using System;

public class Referenced : ScriptableObject
{
    // Code here
}

public class Referencer : ScriptableObject
{
    public byte[] assetHandle;
}

[CustomEditor(typeof(Referencer))]
public class ReferencerEditor : Editor {
   
    // The guid pulled from the asset file byte[]
    private Guid guid;
   
    // The object (asset) the guid refers to
    UnityEngine.Object referenced = null;
   
    //void OnGUI () {
    public override void OnInspectorGUI()
    {
        // Begin group for manipulating the keyframe list
        GUILayout.BeginVertical("Referencing Test", GUILayout.ExpandHeight(true));   
       
        Referencer cast = (Referencer)target;
       
        // Get the Guid from the asset (if set)
        if(cast.assetHandle != null)
            guid = new Guid(cast.assetHandle);
        else
            guid = Guid.Empty;
       
        // Check the asset is referenced (checked as this can be called many times)
        if(guid != Guid.Empty && referenced == null)
        {
            // Convert the guid string to a format Unity recognises (irritating)
            string[] guidParts = guid.ToString().Split('-');
            string formattedGuid = string.Empty;
            foreach(string p in guidParts)
                formattedGuid += p;

            // Convert formatted guid string to asset path
            string path = AssetDatabase.GUIDToAssetPath(formattedGuid);
           
            // Load the asset into reference
            referenced = AssetDatabase.LoadAssetAtPath(path, typeof(Referenced));
        }
       
        // Show the loaded asset in an object field and get a new one out
        UnityEngine.Object objAfter = EditorGUILayout.ObjectField("Referenced", referenced, typeof(Referenced), false);
       
        // If the user changed the referenced asset then get the new id and save
        if(objAfter != null && objAfter != referenced)
        {
            guid = new Guid(AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(objAfter)));
            cast.assetHandle = guid.ToByteArray();
            referenced = objAfter;
        }

        GUILayout.EndVertical();
       
        // Refresh editor
        if(GUI.changed) {
            EditorUtility.SetDirty(target);
        }
    }
}

The above gives you an editor inspector when you are editing a “Referencer” asset that lets you drag and drop a “Referenced” asset onto an object field. That will then save the Guid of the “Referenced” asset to the assetHandle field in “Referencer” so that you can load it later from code.

This way I can load “Referencer” without worrying that “Referenced” will be pulled into memory too.

The above code is a massive faff though and has a few nasty bits. AssetDatabase only seems to like Guid strings presented to it in a specific format. In fact if you convert a Guid obect to string then that will not be accepted by AssetDatabase. You need to strip the dashes out for it to work. You would also need to do all the conversions when you want to load the “Referenced” asset later on.

If AssetDatabase could work with System.Guid objects directly and not just strings, and if Unity could serialize System.Guid fields then I would be ok with using the above code. I’m looking for a neater alterative though. Any thoughts? Anyone?

1 Like

Ok, so more digging and it sounds like to achieve what I want, I have to use Resources.Load() which negates the use of drag and drop referencing of my assets anyway and means I have to use paths. This is a shame, but I’ll keep looking before I commit and post if I find anything that changes this.