- Home /
Goods and bads about using Resources.load
Hi guys,
I've had a disagreement with a co-worker for quite a while now about using Resources.load(). Now, I understand how it is used, but I'm trying to get some points on why/when/where to use it. Currently, my co-worker prefers sticking just about everything under the Resources directory. Then loading it during run-time using Resources.load (of course). From my perspective, this is a misuse of the tool.
Can anybody help me understand the implications of using Resources.load better?
This is what I understand:
Used for loading assets that are rarely used so that they are not loaded as part of any of the scenes. " For example, there may be a character or other object that can appear in any scene of the game but which will only be used infrequently (this might be a "secret" feature, an error message or a highscore alert, say). ", says Unity.
If you want WebPlayers to load a game quickly then you can make a lightweight scene that loads only the minimum required assets, then use Resources.load as needed to load the remaining assets.
Answer by whydoidoit · Sep 17, 2012 at 10:30 PM
I agree with your points - well put. If you put it all in Resource directories then it will be included in the build - no matter if you stop using it - normally only assets that are actually being used in a built scene will be included in the actual executable. So putting everything in Resources is a bit of a nightmare because you have to work out whether you are still using things or your executable starts to fill with wasted stuff - big problem on phones.
I use resources for things like:
Music - because the player can select a track and I don't want them hanging around when they aren't being used
Character animations because I want to load the set that are relevant for the currently selected character.
Images that may be displayed at some point during any scene (like control tips etc).
Your first point - things that may be loaded but aren't always - like characters that may only be used sometimes.
Prefabs that are always needed in a scene but have no obvious "home" get stuck in a game object that loads with the scene (set using the inspector into public variables- then it is a compile error if I spell it wrong). Generically useful prefabs get loaded in a singleton UsefulPrefabs script where the prefabs are set using the inspector so that I don't have to hunt around in code for them by name (this script is DontDestroyOnLoad and is always included by loading additively from a master scene if it isn't found).
I would much prefer to be able to identify where my objects are so that there is no hunting in code looking for the names being passed to Resources.Load - and as mentioned above - strings are fragile compared to variables.
> So putting everything in Resources is a bit of a nightmare because you have to work out whether you are still using things or your executable starts to fill with wasted stuff - big problem on phones.
I'm not sure I understand why this is a problem and why it matters for phones any more than any other platform. It seems to be it would be easier to keep your Resources folder clean than to maintain object lists for every level that may keep changing. If you stop using something permanently, take it out. Or if you're not sure whether you'll be using it or not, move it to a "Unused" subfolder in resources so you can delete it before your final build.
Now for web, you have asset bundles to deal with, so it may be different.
Because if you put it in Resources it will be included. If you don't it will only be included if it is used - Unity figures that out for you.
> Because if you put it in Resources it will be included
Included in the build, yes, but not loaded into memory. Why would you have stuff in resources you don't want in the build? Editor tools shouldn't be in there. And for in-progress/undecided stuff, you could have a Work folder inside Resources, but sort and organize when you finalize it. Then before build, you just have to worry about cleaning up Work.
I guess it just depends how you work which way is easier.
So per my original point - if you put everything in Resources then it is your job to manage it - this gets big and ugly and misses a key feature of Unity that automatically reduces the size of your build. Which is, as I said, a problem on phones - because phones have a) limited memory b) limited over the air download sizes c) a potential end user cost associated with bandwidth usage.
I don't know about you - but I am continually refining what is included in scenes and as my game development progresses I move from using one set of prefabs to another, one set of textures and models to another etc etc etc.
Given the uses I suggested for Resources I am already having to manage that directory - managing it for things used in my scenes would be a massive pain. I'd rather have a nice little object, that I can set a couple of prefabs into and know that anything it previously had set will be excluded from the build. Plus of course, if I move something, it doesn't break.
Answer by guavaman · Sep 17, 2012 at 10:56 PM
I generally agree with your coworker, but it depends on your game's needs. (EDIT: Actually, I don't agree that EVERYTHING should go in Resources -- I kind of misread the question. I put all prefabs, audio files, and certain large GUI textures, NOT dependencies like models, animations, textures, etc.) My game is not web-based, so bundling all assets into the build is not a problem. I put most prefabs in Resources because many times objects I want to load are not instantiated in the scene such as bullets, randomized enemies, random objects, etc. I had been instantiating all my objects from a master prefabs object that contained links to all the prefabs in my game, but as you can imagine it started taking up a silly amount of memory as the game grew. I ended up switching to a string-based solution (created with an editor script from my prefabs object each time I change something) which allows me to load the assets as needed from disk with Resources.Load. I use id's to look up prefabs from the lists, but you could also use enums, so there's no dealing with strings in the code. I personally think its far more trouble to manage a prefab list of exactly what objects you want loaded on a per-scene basis because you have to be constantly cleaning up this list for every scene every time you want to add or remove something.
You must be my co-worker, hah. I'm joking. I do agree with you on the fact that linking everything to the scene for the sake of having access to it can cause the scene to get pretty memory intensive. You really only want things that are always available to the player at any given time to be part of the scene. Let's take the bullets for example. You can link the default bullets that the players starts the level with to some game object in the scene. If the player finds a special power-up in the level then you can use Resources.load to get the special bullets assets. What do you think?
... I do agree with you on the fact that linking everything to the scene for the sake of having access to it can cause the scene to get pretty memory intensive. :) Well, I was referring to having a gigantic list that holds links to all the prefabs in the entire game, which is what I did at first. That takes a whole lot more memory than a list for one level. $$anonymous$$y gigantic list of linked prefabs stays on disk (never in a scene) and gets turned into a gigantic list of strings (a persistent object in-world) pointing to their location in Resources so I have access to every prefab in the game on all levels, though I certainly won't ever use them all at once. (When adding new objects to my lists, I add them to the giant prefab list on disk and then run an editor script which updates the string list.)> You must be my co-worker, hah. I'm joking.
> You really only want things that are always available to the player at any given time to be part of the scene.
Sort of. Unless you plan on dynamically unloading objects too (like in a sandbox game - Resources.UnloadUnusedAssets), you definitely want to have enough memory available to hold all possible objects that might be instanced in your scene. Linking all the objects in the scene is an easy way to ensure you do, but I find it cumbersome.
If you're not unloading, using Resources.Load is more just for convenience because it prevents you from having to maintain an object list per-level. It doesn't really help with memory in that case and will probably actually complicate things because you have to worry about expanding memory. (I forget if Unity will unload things by itself if needed or not...)
> You can link the default bullets that the players starts the level with to some game object in the scene. If the player finds a special power-up in the level then you can use Resources.load to get the special bullets assets.
Yes, I think that's a good strategy and I had planned doing that in the end when I get to optimization. I currently access everything that needs to be instantiated with Resources.Load, but I think pre-caching some common objects at the beginning of the game/level might help prevent hiccups (which I haven't seen in a desktop build, but just to be safe). I would do it either by linking them to some object in the scene like you said or by having a script instantiate and destroy them on level start.
Obviously phone and web may be different by taking longer to load something on-the-fly, etc. I have no experience with those platforms.
I just clarified in the above post something I may not have made clear. I don't advocate putting EVERYTHING into the resources folder. I don't put models, most textures, animations, other dependencies in there. I mainly have prefabs, audio, and other heavy things like large images used infrequently as part of the GUI. Sorry if I didn't make that clear.
I gotcha. Thanks for all the input man. This is exactly the kind of input I was looking for.
Just a note - I see the release notes for 3.5.6 mention that they've fixed a memory leak in Resources.Load - that may also explain further some of the issues I have with my files that do sit in that folder.
Answer by JustinRyder · Oct 05, 2012 at 08:03 PM
(When adding new objects to my lists, I add them to the giant prefab list on disk and then run an editor script which updates the string list.)
Could you give some more info on this system you use? I was thinking of keeping a class with static strings of resource paths. The editor script you described sounds much more convenient.
class MyResources
{
static string
player = "Prefabs/Player",
bullet = "Prefabs/Bullet",
themeSong = "Audio/Main Theme"
;
}
//anywhere i need a resource
Instantiate(Resources.Load(MyResources.player));
Hi, this Unity answers system doesn't really support huge messages but I'll do my best to post it here. By the way, you should comment, not make a new answer when asking a question. Please excuse any sloppy code. I didn't say it was well written, but it does the job. ;)
// THE PREFAB CLASS - This class is put on a gameobject and saved to a prefab. // This prefab always stays on disk and should never be instanced in a scene. // Drag and drop references to your prefabs in these arrays first. public class Prefabs : $$anonymous$$onoBehaviour { // Prefab arrays public GameObject[] weapons; public GameObject[] bullets; // ... (all your various lists)
public GameObject GetPrefab(AssetList listName, int index) { // return a prefab
if(index < 0) return null;
GameObject[] list = GetGameObjectArray(listName); // get our array by enum
if(index >= list.Length) return null; // make sure we don't request something out of range
return list[index]; // return the game object
}
private GameObject[] GetGameObjectArray(AssetList listName) { // return the right array based on inco$$anonymous$$g enum
GameObject[] list;
if(listName == AssetList.weapons) list = weapons;
else if(listName == AssetList.bullets) list = bullets;
// ... (all your various lists)
else list = null;
return list;
}
// ... various other helper functions for other things
}
// The Assets class - This class goes on a gameobject. $$anonymous$$ake it into a prefab. // This prefab should be instantiated in the scene and be persistent and not die on level load, etc. // NOTE: I also included a caching system to reduce calls to Resource.Load, but omitted that from this example to make it shorter. public class Assets : $$anonymous$$onoBehaviour {
// These will be populated from Prefabs by an editor script whenever you add/remove items
public string[] weapons;
public string[] bullets;
// ... (These arrays should mirror those in Prefabs, but are of type string[] ins$$anonymous$$d of GameObject[])
public GameObject GetPrefab(AssetList listName, int index) {
if(index < 0) return null;
string[] list = GetStringArray(listName); // get our array
// Try to load object from resources folder
string fileName = list[index];
GameObject obj = (GameObject)Resources.Load(fileName, typeof(GameObject)); // load the object
if(obj == null) {
Debug.Log("Resources.Load: Prefab not found at path " + fileName + "!");
return null;
}
return obj; // return the game object
}
private string[] GetStringArray(AssetList listName) {
string[] list;
if(listName == AssetList.weapons) list = weapons;
else if(listName == AssetList.bullets) list = bullets;
// ...
else list = null;
return list;
}
}
// ENU$$anonymous$$S // $$anonymous$$A$$anonymous$$E SURE enum names match array names EXACTLY public enum AssetList { weapons = 0, bullets = 1 // ... };
// EDITOR SCRIPT class PrefabListEditor$$anonymous$$enu { [$$anonymous$$enuItem ("CustomTools/Update Data/Update prefab string names from prefab lists")] static void Custom$$anonymous$$enu_UpdateStringNamesFromPrefabLists() { string resourcesPath = "Assets/Resources/"; string dataObjectPath = resourcesPath + "Prefabs/DataObjects/"; // This is the path to your prefab with the Prefabs script on it
// PrefabLists object
string fileName = "PrefabLists.prefab";
Prefabs prefabs = (Prefabs)AssetDatabase.LoadAssetAtPath(dataObjectPath + fileName, typeof(Prefabs));
if(prefabs == null) { Debug.Log("Prefab prefab not found at " + dataObjectPath + fileName); return; }
// PrefabFiles object
fileName = "Assets.prefab";
Assets assets = (Assets)AssetDatabase.LoadAssetAtPath(dataObjectPath + fileName, typeof(Assets));
if(assets == null) { Debug.Log("Assets prefab not found at " + dataObjectPath + fileName); return; }
Custom$$anonymous$$enu_Helper_UpdateFileNamesFromPrefabList(prefabs, assets); // populate string names by taking them from the prefab list object
}
static void Custom$$anonymous$$enu_Helper_UpdateFileNamesFromPrefabList(Prefabs prefabs, Assets assets) { string resourcesPath = "Assets/Resources/"; int updateCount = 0; string[] listNames = System.Enum.GetNames(typeof(AssetList)); int listCount = listNames.Length;
SerializedObject soSource = new SerializedObject(prefabs);
SerializedProperty spSource;
SerializedObject soTarget = new SerializedObject(assets);
SerializedProperty spTarget;
for(int i = 0; i < listCount; i++) {
spSource = soSource.FindProperty(listNames[i]);
spTarget = soTarget.FindProperty(listNames[i]);
soSource.Update();
soTarget.Update();
spSource.Next(true); // Array
spTarget.Next(true); // Array
spSource.Next(true); // size
spTarget.Next(true); // size
// Get new array length
int arrayLength = spSource.intValue;
if(arrayLength != spTarget.intValue) { // arrays are different sizes
spTarget.intValue = arrayLength; // copy new array length
}
// Go through each element in the array and copy
for(int j = 0; j < arrayLength; j++) {
spSource.Next(true); // element
spTarget.Next(true); // element
if(spSource.propertyType != SerializedPropertyType.ObjectReference) {
Debug.Log("Error! Property type " + spSource.propertyType + " not supported! Skipping list!");
break;
}
UnityEngine.Object obj = spSource.objectReferenceValue;
System.Type dataType = obj.GetType();
if(dataType == typeof(GameObject)) {
spSource.Next(true); // m_FileID
spSource.Next(true); // m_PathID
} else if(dataType == typeof(AudioClip)) {
spSource.Next(true); // m_FileID
spSource.Next(true); // m_PathID
} else {
// Null field or unsupported data type
spSource.Next(true); // m_FileID
spSource.Next(true); // m_PathID
}
// CONTINUED NEXT POST
// Save the string // Get a path for each object. These should be in Resources folder, but will find anywhere. string fileLocation = AssetDatabase.GetAssetPath(obj); string filePathAndName = ""; string fileName; string filePath;
// Format the path to our needs
if(fileLocation != null && fileLocation != "") { // GetAssetPath returns "" if not found, not null
filePath = Path.GetDirectoryName(fileLocation) + "/"; // get directory path only
fileName = Path.GetFileNameWithoutExtension(fileLocation); // strip out file extension
filePathAndName = filePath + fileName; // combine
if(filePathAndName.StartsWith(resourcesPath))
filePathAndName = filePathAndName.Remove(0, resourcesPath.Length); // strip out path of resources folder
else
Debug.Log("Error! Object not in resources folder! " + filePathAndName);
}
spTarget.stringValue = filePathAndName; // save the path string
// Deal with the char array below the string
spTarget.Next(true); // Array
spTarget.Next(true); // size
if(filePathAndName != null) { // we had a file path, now skip over the char array
spTarget.intValue = filePathAndName.Length; // set size of char array
for(int k = 0; k < filePathAndName.Length; k++) { // just skip over the chars, they'll fill in automatically
spTarget.Next(true); // char
}
}
updateCount++;
}
soTarget.Apply$$anonymous$$odifiedProperties();
}
Debug.Log("Updated " + updateCount + " prefab names in " + listCount + " lists!");
} }
// About the editor script: // Each time you add/remove items from the Prefabs list on disk, run this editor script // to update the string arrays in Assets. This updates the Assets prefab on disk so any scene with it updates automatically.
// USAGE: // For easier access, get a static link to the Assets object in some static class // so you can call it like this: // Game$$anonymous$$anager.assets.GetPrefab(AssetList.weapons, 5); // Load the weapon at index 5 // // Using arrays like I do makes it a little annoying because they are fixed and you // can't rearrange objects. Also you have to call objects by their ID number. // // Note: $$anonymous$$y version uses strings ins$$anonymous$$d of enums, but I thought using enums would be a little safer, so I modified the code above. I haven't tested the enum version, but it should work.
I suppose it would be easy enough to modify this to create a text file from the Prefabs list ins$$anonymous$$d of storing the path strings in the Assets class as serialized data. Then you could just load in the text file, populate the arrays in Assets from it, and even make Assets a static class (no need for monobehaviour) so you could access it directly. (Why didn't I think of that before... Too much to do. :P )
Answer by URGr8 · Jul 29, 2018 at 05:25 AM
I know this is an old thread. But Unity will automatically put the stuff in your project that have dependency links into your project folder, like textures or objects that are in a scene.
So really put as little as you need into the Resources folder (to keep the build size down). Only put stuff you will need to load during the game.Like GUIs, sounds, GameObjects that you need to instantiate or use.
If you already have a link to it, like using a GameManager Object with some scripts, then it will get put into the build, so it doesn't matter if it's in the Resources folder or not. But it does make it easier to clean up and change stuff if it's not in the Resources Folder.
You can also look at using Asset Bundles and loading from there if your project is large.
Your answer
Follow this Question
Related Questions
prefab loading via resources forced to inactive 2 Answers
Rendering Image Sprites Programatically 0 Answers
How to check the path on Android 0 Answers