1

I am following a tetris tutorial when it called for an array of prefabs as a part of the spawner object. The spawner would randomly create the shapes, such as the I shape, the T shape, etc. These shapes are prefabs.

public class Spawner : MonoBehaviour {
    public GameObject[] blocks;

    //...
}

This setup would make a custom field in the Inspector, where I can put in all the shapes that could spawn. However, this got me thinking, what if I have 1000 shapes? Surely there's a better way than filling in 1000 prefabs? Can I initialize the array without the Inspector?

Thank you for your time.

3 Answers3

4

Using the Inspector this is actually pretty easy:

Select your 1000 prefabs -> drag&drop them all onto the name of the list/array in the Inspector

=> they all get added as new elements to the list/array (afaik in the order they were selected) ... what could be easier than that? ;)

derHugo
  • 83,094
  • 9
  • 75
  • 115
1

Of course you can initialize the prefabs without using the inspector. You have to know the directory path of your prefabs to be able to load them into your array. For example you can load TShapedBlockPrefab by these lines. By the way any directory you would like to use should be under the Resources folder, Prefabs folder is under the Resources folder in Unity for this example.

var path = "Prefabs/Shapes/TShapedBlockPrefab"
blocks[0] = Resources.Load<GameObject>(path);
Dozturk
  • 21
  • 1
  • 7
0

A couple of things: Yes you can drag drop multiples. Next, you should create ScriptableObject custom classes named SpawnSettings or other names for things like this. ScriptableObjects exist in the project, and you modify their settings without worrying about which scene is open. Just have your components have a public SpawnSettings Settings; reference (and drag your custom Scriptable Object into the inspector for that field). It can be a lot more convenient to modify scriptable properties rather than have them on gameobjects in the scene. Finally, you can write a custom editor menu item to automate tasks like what you are describing - below is some example code. When you have a static method with the attribute [MenuItem] it will show up under the Unity menu that you specify as in the example below. It ends up on the Tools menu and then under Grasslands.

[MenuItem("Tools/Grasslands/Import Recipes (must be in GameUI scene with CraftManager)")]
private static void ImportRecipes()
{
    if (!CraftManager.Instance)
    {
        Debug.LogError("No CraftManager object found, you must switch to GameUI scene");
        return;
    }

    Undo.RecordObject(CraftManager.Instance, "Imported Crafting Items");

    var mainpath = "Assets/Grasslands";

    var guids = AssetDatabase.FindAssets("t:CraftRecipeDefinition");
    print($"Found CraftRecipeDefinitions: {guids.Length} total");
    CraftManager.Instance.Recipez = new CraftRecipeDefinition[guids.Length];
    List<CraftRecipeDefinition> items = new List<CraftRecipeDefinition>();

    int total = 0;
    foreach (var guid in guids)
    {
        var path = AssetDatabase.GUIDToAssetPath(guid);
        if (!path.Contains(mainpath))
            continue;

        try
        {
            var item = AssetDatabase.LoadAssetAtPath<CraftRecipeDefinition>(path);
            items.Add(item);
            total++;
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"Exception importing itemdefinition [{path}]: {ex.Message}");
        }
    }

    items.Sort();
    for(int i=0; i<items.Count; i++)
    {
        var item = items[i];
        //item.Id = i + 1;
        //EditorUtility.SetDirty(items[i]);

        if (items.Any(x => x.Id == item.Id))
        {
            Debug.LogError($"Duplicate ID for [{item.name}] - SetId has been called and it is fixed now.");
            item.SetId();
            EditorUtility.SetDirty(item);
        }
    }

    CraftManager.Instance.Recipez = items.ToArray();
    print($"------ Imported Crafting Recipes: {total} total --------");

    EditorUtility.SetDirty(CraftManager.Instance);
    EditorSceneManager.SaveOpenScenes();
    AssetDatabase.SaveAssets();
}

the CraftManager uses a common Unity Singleton pattern which just gives it a .Instance property so you can reference it from anywhere easily. I check to make sure we are in the right scene with a CraftManager gameobject. Then I grab all the CraftRecipeDefinition custom Scriptable Objects in the project and add them into a simple List<> which I then sort and finally assign to the CraftManager.Instance.Recipez property. In my case, Recipez is just a public List Recipez. So yes you can write custom editor menu item scripts that run and modify anything in your scene, including replacing List<>s with items that you grab from the Project Assets using the AssetDatabase.FindAssets() method provided by the UnityEditor namespace. It is pretty simple, the FindAssets() returns a list of UniqueId strings and we need to make a call to load the asset into memory (and we specify the type to cast it into) and then we can add it into the List<>. Note that a menu item script like that should be encloded in #if UNITY_EDITOR #endif so that it won't break your builds when you build an EXE. You should look up Unity Singleton Pattern, and Scriptable Objects.

Michael Urvan
  • 492
  • 3
  • 8