Note: This is an advanced tutorial, recommended for those already familiar with the Unity3D game engine.
With the Resources class, Unity has provided an easy way to load all assets from a folder. This is very powerful, but how do you incorporate this within your game architecture?
Goal:
Instantiate by name, any prefab created by your team. As an example, see the code below and the image to the right.
Instantiate(globalPrefabs.getPrefab("coin_01"));
Step-by-step:
- Create a class containing a static Dictionary that will store the references to all prefabs. The key-value pair will be such that the key is an integer (the hash code of the object name), and the value is a reference to the Unity game object.
using UnityEngine; using System.Collections; using System.Collections.Generic; //Required for Dictionary public class globalPrefabs : ScriptableObject { public static Dictionary<int, Object> objectList = new Dictionary<int, Object>(); //... }
Note that the class does not inherit from the MonoBehaviour class, but instead, ScriptableObject. As such, it does not need to be attached to a game object, just included in your scripts folder as part of the game assembly.
- Within the same class, create a static initialization function that uses Resources.LoadAll([path]) to populate the Dictionary with all prefabs located in that folder.
public static void LoadAll(string pPath) { Object[] ObjectArray = Resources.LoadAll(pPath); foreach (Object o in ObjectArray) objectList.Add (o.name.GetHashCode(), (Object)o); }
Note that for a key, we use the integer-valued hash code of the object’s name. This will be more efficient than storing the string-valued name, but may be slightly harder to debug.
Also, note that as a static function, this must be called as class-name reference from another game script, ideally in the Start() function.
- Finally add a public look-up function that returns the gameobject stored in the Dictionary.
public static Object getPrefab(string objName) { Object obj; if (objectList.TryGetValue(objName.GetHashCode(), out obj)) return obj; else { Debug.Log("Object not found"); return (emptyObj); } }
Using our globalPrefabs script:
As mentioned, because the script uses static functions and does not inherit from MonoBehavior, there is no need to attach the script to a Unity gameObject. Instead, the script should simply be added to the Assets folder ensuring it is included in the game assembly.
Now, assuming you have a prefab named “coin_01” in a folder named “Prefabs” which is a subfolder of “Assets/Resources”, the following script (sampleScript.cs) will work when attached to a game object.
Note that you should only call globalPrefabs.LoadAll() once per folder, and of course, be aware of resource implications caused by loading hundreds or thousands of prefabs, especially if they are not used in the game.
using UnityEngine;
using System.Collections;
public class sampleScript : MonoBehaviour {
void Start ()
{
globalPrefabs.LoadAll ("Prefabs");
}
void Update ()
{
if (Input.GetButtonDown ("Fire1"))
Instantiate(globalPrefabs.getPrefab("coin_01"));
}
}
Thanks to my colleagues, Joe Manley and Evan Schipellite, for helping me brainstorm this as a possible approach.
Full source is available on GitHub. In this version I’ve included both automatic prefab generation as well as the messageQueue functionality.
3 comments on “Unity | Access Any Prefab By Name”
I tried this and get the Unity error message:
ArgumentException: The Object you want to instantiate is null. UnityEngine.Object.CheckNullArgument ….