在没有检查器的情况下以编程方式加载带有预制件的数组

Load an array with prefabs programatically without inspector

我正在学习俄罗斯方块教程,因为它需要一组预制件作为生成器对象的一部分。刷怪笼会随机生成I型、T型等形状,这些形状都是预制件

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

    //...
}

此设置会在检查器中创建一个自定义字段,我可以在其中放入所有可能产生的形状。然而,这让我想到,如果我有 1000 个形状怎么办?肯定有比填充 1000 个预制件更好的方法吗?我可以在没有 Inspector 的情况下初始化数组吗?

感谢您的宝贵时间。

当然你可以不使用检查器来初始化预制件。您必须知道预制件的目录路径才能将它们加载到阵列中。例如,您可以通过这些行加载 TShapedBlockPrefab。顺便说一下,你想使用的任何目录都应该在 Resources 文件夹下,Prefabs 文件夹在这个例子中在 Unity 的 Resources 文件夹下。

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

使用 Inspector 这实际上非常简单:

Select 您的 1000 个预制件 -> 将它们全部拖放到 Inspector

中 list/array 的 name

=> 它们都作为新元素添加到 list/array(afaik 按照它们被选择的顺序)......还有什么比这更容易的呢? ;)

一些事情:是的,你可以拖放倍数。接下来,您应该创建名为 SpawnSettings 的 ScriptableObject 自定义 类 或其他类似的名称。 ScriptableObjects 存在于项目中,您修改它们的设置而不用担心打开了哪个场景。只需让您的组件有一个 public SpawnSettings 设置;引用(并将您的自定义脚本化对象拖到该字段的检查器中)。修改可编写脚本的属性比将它们放在场景中的游戏对象上要方便得多。最后,您可以编写一个自定义编辑器菜单项来自动执行您所描述的任务 - 下面是一些示例代码。当您有一个带有属性 [MenuItem] 的静态方法时,它将显示在您指定的 Unity 菜单下,如下例所示。它最终出现在“工具”菜单上,然后出现在“草原”下。

[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();
}

CraftManager 使用通用的 Unity 单例模式,它只给它一个 .Instance 属性,因此您可以从任何地方轻松引用它。我检查以确保我们在正确的场景中使用 CraftManager 游戏对象。然后,我获取项目中的所有 CraftRecipeDefinition 自定义脚本化对象,并将它们添加到一个简单的 List<> 中,然后对其进行排序,最后分配给 CraftManager.Instance.Recipez 属性。 就我而言,Recipez 只是一个 public List Recipez。 所以是的,您可以编写 运行 的自定义编辑器菜单项脚本并修改场景中的任何内容,包括将 List<>s 替换为您使用由UnityEditor 命名空间。这很简单,FindAssets() returns 一个 UniqueId 字符串列表,我们需要调用将资产加载到内存中(我们指定要将其转换为的类型),然后我们可以将其添加到列表<>。 请注意,像这样的菜单项脚本应该包含在#if UNITY_EDITOR #endif 中,这样在您构建 EXE 时它不会破坏您的构建。 您应该查找 Unity 单例模式和脚本化对象。