Search Unity

assets loaded with Resources.Load() are automatically unloaded

Discussion in 'Scripting' started by nachobeard, Dec 8, 2020.

  1. nachobeard

    nachobeard

    Joined:
    May 7, 2013
    Posts:
    47
    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.
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    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?
     
  3. adehm

    adehm

    Joined:
    May 3, 2017
    Posts:
    369
    If you loaded the asset then it should be there unless you lose reference.

    Code (CSharp):
    1. public static class HoldResource()
    2. {
    3.     public static Texture Texture;
    4.      public static void Load()
    5.     {
    6.          Texture = Resources.Load<Texture>(texturePath);
    7.     }
    8. }
    HoldResource.Texture will always exist and hold the texture.
     
    nachobeard likes this.
  4. nachobeard

    nachobeard

    Joined:
    May 7, 2013
    Posts:
    47

    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:

    Code (CSharp):
    1.  
    2.  
    3.     //needs to be struct for dictionary to use this correctly as a key
    4.     //(otherwise we won't find any of our cached animations)
    5.     struct ClipName
    6.     {
    7.         public string spriteAnimationName;
    8.         public string clipName;
    9.        
    10.         public ClipName(string spriteAnimationName, string clipName)
    11.         {
    12.             this.spriteAnimationName = spriteAnimationName;
    13.             this.clipName = clipName;
    14.         }
    15.     }
    16.    
    17.     //cached data
    18.     public class Clip
    19.     {
    20.         public tk2dSpriteAnimation spriteAnimation;
    21.         public tk2dSpriteAnimationClip clip;
    22.        
    23.         public Clip(tk2dSpriteAnimation spriteAnimation, tk2dSpriteAnimationClip clip)
    24.         {
    25.             this.spriteAnimation = spriteAnimation;
    26.             this.clip = clip;
    27.         }
    28.     }
    29.  
    30. //holds all cached data
    31. static Dictionary<ClipName, Clip> cachedClips = new Dictionary<ClipName, Clip>();
    32.  
    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.
     
    Last edited: Dec 8, 2020
  5. nachobeard

    nachobeard

    Joined:
    May 7, 2013
    Posts:
    47
    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.
     
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    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.
     
  7. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    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.
     
    nachobeard and PraetorBlue like this.
  8. nachobeard

    nachobeard

    Joined:
    May 7, 2013
    Posts:
    47
    forgot to add that I'm calling Resources.UnloadUnusedAssets a few hundred times in between levels, that's probably what's triggering it.
     
    PraetorBlue likes this.
  9. nachobeard

    nachobeard

    Joined:
    May 7, 2013
    Posts:
    47
    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.
     
  10. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    That's, uh, different from what I imagined when you wrote that Unity was doing this "behind your back"...
     
    PraetorBlue likes this.
  11. nachobeard

    nachobeard

    Joined:
    May 7, 2013
    Posts:
    47
    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.
     
  12. nachobeard

    nachobeard

    Joined:
    May 7, 2013
    Posts:
    47
    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

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. //https://forum.unity.com/threads/assets-loaded-with-resources-load-are-automatically-unloaded.1018705/#post-6602224
    6. public class HoldResources : MonoBehaviour
    7. {
    8.     static HoldResources instance;
    9.  
    10.     public List<Object> holdResources;
    11.  
    12.     static HoldResources Instance
    13.     {
    14.         get
    15.         {
    16.             if (instance != null)
    17.             {
    18.                 return instance;
    19.             }
    20.             else
    21.             {
    22.                 instance = ((GameObject)Instantiate(Resources.Load("holdResources"))).GetComponent<HoldResources>();
    23.                 DontDestroyOnLoad(instance.gameObject);
    24.                 return instance;
    25.             }
    26.         }
    27.     }
    28.  
    29.     // Start is called before the first frame update
    30.     void Awake()
    31.     {
    32.         if (instance != null)
    33.         {
    34.             Debug.LogError("cannot create multiple instances");
    35.         }
    36.     }
    37.  
    38.     public static void HoldResource(Object o)
    39.     {
    40.         if (!Instance.holdResources.Contains(o)) Instance.holdResources.Add(o);
    41.     }
    42. }

    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.