为什么有时在生成新网格时会出现缺少渲染器组件的异常?

Why sometimes when generating a new grid i'm getting exception of missing renderer component?

这是异常消息:

MissingComponentException: There is no 'Renderer' attached to the "Bottom Wall" game object, but a script is trying to access it. You probably need to add a Renderer to the game object "Bottom Wall". Or your script needs to check if the component is attached before using it.

我有 3 个脚本附加到层次结构中的同一个游戏对象。

第一个脚本生成网格:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GridGenerator : MonoBehaviour
{
    public GameObject gridBlock;
    public int gridWidth = 10;
    public int gridHeight = 10;
    public List<Vector3> positions = new List<Vector3>();
    public List<GameObject> blocks = new List<GameObject>();

    private GameObject[] wallsParents = new GameObject[4];

    void Start()
    {
        wallsParents[0] = GameObject.Find("Top Wall");
        wallsParents[1] = GameObject.Find("Left Wall");
        wallsParents[2] = GameObject.Find("Right Wall");
        wallsParents[3] = GameObject.Find("Bottom Wall");

        GenerateGrid();

        var testing = GetComponent<tester>();
        tester.Test();
    }

    public void AutoGenerateGrid()
    {
        for (int i = 0; i < blocks.Count; i++)
        {
            DestroyImmediate(blocks[i]);
        }
        GenerateGrid();
        var testing = GetComponent<tester>();
        tester.Test();
    }

    public void GenerateGrid()
    {
        for (int x = 0; x < gridWidth; x++)
        {
            for (int z = 0; z < gridHeight; z++)
            {
                GameObject block = Instantiate(gridBlock, Vector3.zero, gridBlock.transform.rotation) as GameObject;
                block.transform.parent = transform;
                block.transform.tag = "Block";
                block.transform.localScale = new Vector3(1, 0.1f, 1);
                block.transform.localPosition = new Vector3(x * 1.5f, 0, z * 1.5f);
                block.GetComponent<Renderer>().material.color = new Color(241, 255, 0, 255);

                if (x == 0)//TOP
                {
                    positions.Add(block.transform.localPosition);
                    block.transform.parent = wallsParents[0].transform;
                    block.transform.name = "TopWall";
                    blocks.Add(block);
                }
                else if (z == 0)//LEFT
                {
                    positions.Add(block.transform.localPosition);
                    block.transform.parent = wallsParents[1].transform;
                    block.transform.name = "LeftWall";
                    blocks.Add(block);
                }
                else if (z == gridHeight - 1)//RIGHT
                {
                    positions.Add(block.transform.localPosition);
                    block.transform.parent = wallsParents[2].transform;
                    block.transform.name = "RightWall";
                    blocks.Add(block);
                }

                else if (x == gridWidth - 1)//BOTTOM
                {
                    positions.Add(block.transform.localPosition);
                    block.transform.parent = wallsParents[3].transform;
                    block.transform.name = "BottomWall";
                    blocks.Add(block);
                }
            }
        }
    }
}

第二个脚本从任意两面墙上随机选取两个对象:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class tester : MonoBehaviour
{
    private GameObject[] wallsParents = new GameObject[4];

    public void Test()
    {
        try
        {
            wallsParents[0] = GameObject.Find("Top Wall");
            wallsParents[1] = GameObject.Find("Left Wall");
            wallsParents[2] = GameObject.Find("Right Wall");
            wallsParents[3] = GameObject.Find("Bottom Wall");

            var wallsList = wallsParents.ToList();

            // remove random 2 times
            for (int i = 0; i < 2; i++)
            {
                wallsList.Remove(wallsList[Random.Range(0, wallsList.Count)]);
            }

            var childsWall0 = wallsList[0].GetComponentsInChildren<Transform>();
            var childsWall1 = wallsList[1].GetComponentsInChildren<Transform>();
            var randomBlock = childsWall0[Random.Range(0, childsWall0.Length)];
            var randomBlock1 = childsWall1[Random.Range(0, childsWall1.Length)];

            randomBlock.GetComponent<Renderer>().material.color = Color.red;
            randomBlock1.GetComponent<Renderer>().material.color = Color.red;
        }
        catch(MissingComponentException missing)
        {
            var mis = missing;
        }
    }
}

第三个脚本向检查器添加了一个按钮,每次都会生成一个新网格,其中从两个随机墙中挑选了两个随机块:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(GridGenerator))]
public class GenerateGridButton : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        GridGenerator myScript = (GridGenerator)target;
        if (GUILayout.Button("Generate New Grid"))
        {
            myScript.AutoGenerateGrid();
        }
    }
}

问题是当我点击按钮时它工作正常但是在点击之后每次都会在另一面墙上抛出异常。有时在底部墙上有时在左右或顶部。有时点击按钮两次后有时点击按钮 10 次后。

在我添加按钮和 AutoGenerateGrid 方法之前,它工作正常,我是 运行 游戏,它每次都创建一个新网格,其中包含两个随机选择的墙和两个从两个墙中选择的块。

毕竟,当我单击按钮并销毁所有现有的多维数据集,然后创建新的多维数据集时,新的多维数据集应该具有渲染器组件。

我不知道是什么导致了这个异常。我添加了 try and catch 但它并没有给我更多帮助。

我知道问题有点长,但每个脚本都是相互关联的,所以很难缩小问题范围。

您的代码实际上正在执行您提到的内容。

var childsWall0 = wallsList[0].GetComponentsInChildren<Transform>();
var childsWall1 = wallsList[1].GetComponentsInChildren<Transform>();
var randomBlock = childsWall0[Random.Range(0, childsWall0.Length)];
var randomBlock1 = childsWall1[Random.Range(0, childsWall1.Length)];

randomBlock.GetComponent<Renderer>().material.color = Color.red;
randomBlock1.GetComponent<Renderer>().material.color = Color.red;

如果您仔细阅读此 thread 发生的事情是,您将在 parent 游戏object 中获得 Transform 组件以及 child 游戏 objects。当然,如果您的 parent 游戏 object 没有任何 渲染器 ,如果您将它用作 object 组的容器。因此,为此您必须获得游戏 object 的所有 child 而不是 parent 游戏 object 本身。 因为你正在对所有 objects 进行随机转换,所以如果在范围内选择了 parent,它有时会给出错误。我建议的是移除 childWall0 和 childWall1 中 parents 的引用,或者另一种方法是

 foreach (Transform child in transform) {
        //Add this child to your childsWall0 & childsWall1
 }

但这仅适用于中间 objects 而不是嵌套。如果您有嵌套元素,最好使用您的方法,但只需删除 parent 即可解决您的问题。

这里还有查找参考线程 child transfrom