- Home /
How to create an array of prefabs with a specific script attached
I am trying to randomly generate levels for my game. I want an array or list of all of the tiles (game objects) with a specific script attached, TileIdentifier. I would like a way to do this through code without passing in the tile prefab as a public variable, as that will just take too much time for all of the tiles.
public List<TileIdentifier> tiles = new List<TileIdentifier>();
// Find all game objects with TileIdentifier script attached
I know you can use FindObjectsOfType to get objects in a scene with a specific script attached, but prefabs are not in the scene. And I know you can instantiate prefabs from passing them through as a public variable. But how could I create an array of prefabs with a specific script attached just from code?
Thanks for your time.
Answer by GameDevSA · Aug 02, 2017 at 09:30 AM
So based on @ SohailBukhari suggestion, I came up with a really good solution. Yes locking the inspector and dragging a bunch of objects into the list was a great simple solution, but I locked onto the idea of using the Resources folder. After some research, I found some useful things. Firstly, this code achieves it:
PrefabUID[] enemies = Resources.LoadAll<PrefabUID>("");
In this case, PrefabUID is a script I have attached to a GameObject stored in the Resources folder. That one line of code will get all of the GameObjects with that script attached in my Resources folder and sub folders. That's the quick answer.
For anyone else interested in looking at this in the future, the Resources folder is where you put game assets that you always want included in the final project. Otherwise, prefabs and assets that are not in a scene anywherearen't included in a build project (or probably testing at runtime in the editor either). So in other words, if you have prefabs you want to be able to access in code at any time, regardless of if they are in a scene or not, you should put them in a special folder called Resources.
If you put some GameObjects in the Resources folder, you can use the function I just did:
PrefabUID[] enemies = Resources.LoadAll<SomeType>("");
Resources.LoadAll() will load everything in the resources folder of the specified type, in this case, SomeType. You could set it to GameObject, Material to get all materials, a script or whatever you want.
There are other useful functions in Resources which you can read the documentation for here. Don't be daunted, it's actually really simple when you try it. The two others I've used are:
Resources.Load("LoadThis");
Which will load the asset with that name, or:
Resources.LoadAll("FolderName/");
Which, if you have sub folders, will load all of the assets in the specified sub folder. I assume if you leave it blank it might load everything in the resources folder - or give you an error.
I have found Resources.FindObjectsOfTypeAll to be unreliable, so I wouldn't recommend it. LoadAll does the same thing for what we want to achieve here.
Answer by SohailBukhari · Jun 06, 2017 at 08:00 AM
You can load from Resources
folder and then assign it to array. Other thing you talk about passing publicly takes too much time, no its not true lock your inspector and select all prefabs and assign in the array automatically all prefabs added in the array inspector within second.
Your comment was super helpful, thank you very much! It led me down the right track. I've built on it and put up the script I ended up using.
Answer by NoBrainer-David · Jun 06, 2017 at 09:04 AM
I was interested in this, so I threw this script together. Replace "MyDataHolder" with your class and you should be good to go!
The non-obvious part is how you go about searching for and accessing all prefab assets. This is done in 3 steps.
Find all assets with the type of GameObject, using AssetDatabase.FindAssets("t: GameObject"). This gives you an array of GUIDs.
Convert the GUIDs to Asset paths using, AssetDatabase.GUIDtoAssetPath.
Loading the Prefab into a GameObject variable with AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject))
Let me know if you need help using this script.
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class PrefabFinder
{
[MenuItem("Test/Find Data Holder Prefabs")]
public static void FindAllPrefabsWithDataHolders()
{
MyDataHolder[] dataHolders = FindPrefabsWithComponent<MyDataHolder>();
foreach(MyDataHolder holder in dataHolders)
{
Debug.Log(holder.name);
}
}
public static T[] FindPrefabsWithComponent<T>()
{
GameObject[] prefabs = FindAllPrefabsInProject();
List<T> componentList = new List<T>();
foreach(GameObject prefab in prefabs)
{
T tOnPrefab = prefab.GetComponentInChildren<T>();
if(tOnPrefab != null)
{
componentList.Add(tOnPrefab);
}
}
return componentList.ToArray();
}
public static GameObject[] FindAllPrefabsInProject()
{
string[] prefabGUIDs = AssetDatabase.FindAssets("t: GameObject");
GameObject[] prefabs = new GameObject[prefabGUIDs.Length];
for(int i = 0; i < prefabGUIDs.Length; i ++)
{
string assetPath = AssetDatabase.GUIDToAssetPath(prefabGUIDs[i]);
prefabs[i] = (GameObject) AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject));
}
return prefabs;
}
}