assets loaded with Resources.Load() are automatically unloaded

Hello

I keep some very frequently used animation assets in the Resources folder. These are loaded at the beginning of the game using Resouces.LoadAll() and references are cached inside a non-monobehavor class until the application terminates.

That is, I have a straight c# class used to cache references to a few resources that are used very frequently throughout the whole game.

The problem is that unity sometimes decides to unload the assets behind my back (I guess because it can’t find any references to them) and so the cached references become invalid.

Any ideas as to how I could tell unity to never unload these specific assets?

regards
nacho.

I’m not sure what kind of caching Unity does for this stuff, but if it’s based on you holding references to the data, could you just have an object that keeps references to all of these resources in a collection?

If you loaded the asset then it should be there unless you lose reference.

public static class HoldResource()
{
    public static Texture Texture;
     public static void Load()
    {
         Texture = Resources.Load<Texture>(texturePath);
    }
}

HoldResource.Texture will always exist and hold the texture.

1 Like

ahhh I see what you mean, looking at https://docs.unity3d.com/ScriptReference/Resources.UnloadUnusedAssets.html

“An asset is deemed to be unused if it isn’t reached after walking the whole game object hierarchy, including script components. Static variables are also examined.”

incidentally I’m already keeping all my cached animation clips in a dictionary defined as a static variable:

    //needs to be struct for dictionary to use this correctly as a key
    //(otherwise we won't find any of our cached animations)
    struct ClipName
    {
        public string spriteAnimationName;
        public string clipName;
       
        public ClipName(string spriteAnimationName, string clipName)
        {
            this.spriteAnimationName = spriteAnimationName;
            this.clipName = clipName;
        }
    }
   
    //cached data
    public class Clip
    {
        public tk2dSpriteAnimation spriteAnimation;
        public tk2dSpriteAnimationClip clip;
       
        public Clip(tk2dSpriteAnimation spriteAnimation, tk2dSpriteAnimationClip clip)
        {
            this.spriteAnimation = spriteAnimation;
            this.clip = clip;
        }
    }

//holds all cached data
static Dictionary<ClipName, Clip> cachedClips = new Dictionary<ClipName, Clip>();

this ensures I can just find a cached Clip based on its ClipName, you can see the dictionary is static.

isn’t this considered a static variable? weird, because the tk2dSpriteAnimation assets are being unloaded at random intervals.

yeah I was thinking of something along those lines but was hoping people would be able to point out other ways of achieving this, thank you.

That is indeed a static variable, but it is a collection. It is possible that Unity only considers direct individual static variables pointing to those references. This would truly be unfortunate. I wonder if any Unity employee could chime in on this. It’s possible this is a bug or just a poorly documented way that it is supposed to work.

That description from the documentation is less clear than I would hope. I suspect that by “game object hierarchy” they really mean the GameObject hierarchy, meaning that your variable needs to be somehow attached to a Unity GameObject, not just floating in a static class off in the ether. But I’m only speculating.

On a side note, if Unity is automatically trying to unload unused assets without you asking it to, that I suspect that may indicate that your game is getting a low memory warning or something similar. I wouldn’t expect Unity to do that entirely proactively. But again, I’m speculating.

2 Likes

forgot to add that I’m calling Resources.UnloadUnusedAssets a few hundred times in between levels, that’s probably what’s triggering it.

1 Like

yep you’d hope it was clearly laid out what constitutes a “static” variable in this sense.

adhem shared a good example earlier but I wish it was clearer in the documentation.

That’s, uh, different from what I imagined when you wrote that Unity was doing this “behind your back”…

1 Like

you have a point, though as far as the class holding all the cached references is concerned yes, they’re being invalidated for no reason.

wrote a bit of code to ensure there’s an gameobject with references to all the assets I’ve got cached, so they never expire

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// https://discussions.unity.com/t/819762/8
public class HoldResources : MonoBehaviour
{
    static HoldResources instance;

    public List<Object> holdResources;

    static HoldResources Instance
    {
        get
        {
            if (instance != null)
            {
                return instance;
            }
            else
            {
                instance = ((GameObject)Instantiate(Resources.Load("holdResources"))).GetComponent<HoldResources>();
                DontDestroyOnLoad(instance.gameObject);
                return instance;
            }
        }
    }

    // Start is called before the first frame update
    void Awake()
    {
        if (instance != null)
        {
            Debug.LogError("cannot create multiple instances");
        }
    }

    public static void HoldResource(Object o)
    {
        if (!Instance.holdResources.Contains(o)) Instance.holdResources.Add(o);
    }
}

just needs a prefab called “holdResources” with this component stored in the Resources folder.

call HoldResource to pass an object that will never get destroyed by unity.

not the most elegant but seemed like the safest option to me.