Best practices with Unity have always been an hidden gem. Most precisely concerning the Resources folder. Hopefully things are changing with Unity’s best practices guide! And things are simple concerning the Resources folder: just don’t use it!
Managing 2D Sprites in Unity is simple if you have a static scene: just put every sprite needed on your screen and you’re almost done. Obviously you should package them in Spritesheets. The easy way to do it is via Unity’ Sprite Packer tool. But what to do if you need to load Sprites at runtime? How to access a Sprite if it’s not linked directly on a GameObject or a Prefab and without using the Resources folder?
Create a ScriptableObject! They are the best way to store informations within Unity. Here is an Editor Script for generating ScriptableObjects with a list of Sprites for each Packing Tag mentioned:
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
[Serializable]
public class SpritesData:ScriptableObject {
[SerializeField]
public Sprite[] sprites;
static public string assetDirPath = "RuntimeSprites/SpritesData/";
static public string assetPath = "Assets/RuntimeSprites/SpritesData/{0}.asset";
#if UNITY_EDITOR
[MenuItem("Data/Create Sprites Data")]
static void CreateSpritesData() {
string dirPath = Application.dataPath + "/" + assetDirPath;
if (!System.IO.Directory.Exists(dirPath))
System.IO.Directory.CreateDirectory(@dirPath);
SpritesData data = ScriptableObject.CreateInstance<SpritesData>();
List<Sprite> spritesList = new List<Sprite>();
string[] anims = AssetDatabase.FindAssets("t:Sprite", new string[] {"Assets/Sprites"});
string currentTag = "";
foreach (string anim in anims) {
string path = AssetDatabase.GUIDToAssetPath(anim);
TextureImporter ti = TextureImporter.GetAtPath(path) as TextureImporter;
if (ti.spritePackingTag != currentTag) {
if (data && currentTag != "") {
data.sprites = spritesList.ToArray();
AssetDatabase.CreateAsset(data, string.Format(assetPath, currentTag));
}
data = ScriptableObject.CreateInstance<SpritesData>();
spritesList = new List<Sprite>();
currentTag = ti.spritePackingTag;
}
Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path);
spritesList.Add(sprite);
}
data.sprites = spritesList.ToArray();
AssetDatabase.CreateAsset(data, string.Format(assetPath, currentTag));
}
#endif
}
Now that you have all your SpritesData you just have to create a MonoBehaviour with a public SpritesData[] spritesDatas; property, then you will have access to all your sprites! The good things is while a sprite isn’t displayed on Screen it’s not in memory.
Cheers!