为什么 Destroy(GameObject) 不适用于游戏对象元组列表的第二项?

Why does Destroy(GameObject) not work on the second item of a List of Tuples of GameObjects?

在我的应用程序中,用户可以将对象放置到场景中。每个对象都有一个背景网格,该网格将由程序计算和添加。因此放置的对象存储为元组列表:

public List<Tuple<GameObject, GameObject>> PlacedObjects;

用户还会得到一个“清除”按钮来删除对象,如下所示:

foreach (var placedObject in PlacedObjects)
{
    Destroy(placedObject.Item1);
    Destroy(placedObject.Item2);
}
PlacedObjects.Clear();

出于某种原因,Item1 被删除,但 Item2 在场景中仍然可见。我猜想,一定是有什么泄漏或参考,阻止了数据被破坏?

总而言之,下面是数据的生成方式。首先,用户可以放置一个项目(从下拉选择中克隆),这将按如下方式完成:

public void OnPlaceHereButtonPressed()
{
    var clone = Instantiate(currentlySelectedObject);

    clone.GetComponentInChildren<MeshFilter>().mesh = Instantiate(currentlySelectedObject.GetComponentInChildren<MeshFilter>().mesh);
    // Clone the material for each item
    Material cloneMat = new Material(currentlySelectedObject.GetComponentInChildren<Renderer>().material);

    if (currentlySelectedObject.tag.Equals(paramSphere)) // Spheres get random colors after placing
        cloneMat.SetColor("_BaseColor", Random.ColorHSV());
    clone.GetComponentInChildren<Renderer>().material = cloneMat;

    PlacedObjects.Add(new Tuple<GameObject, GameObject>(clone, null));
    SendVirtualObjectsToServer(clone);

    SetCameraBackgroundShader(true);
}

Item2 有点复杂,因为它来自服务器的结果数据。其中大部分只是将传入的数据流转换为网格数据:

// this is done for each of the placed objects after getting the result
if (hasReconstrucedData)
{
    var reconstructed = new GameObject("Reconstructed Mesh " + i.ToString(), typeof(MeshFilter), typeof(MeshRenderer));
    reconstructed.transform.position = new Vector3(0, 0, 0);
    reconstructed.transform.localScale = new Vector3(1, -1, 1);
    reconstructed.isStatic = true;
    Mesh mesh = new Mesh();

    // vertices
    int vertexBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);
    mesh.vertices = nativeBuffer.GetSubArray(bufferOffset, vertexBufferSize).Reinterpret<Vector3>(vertexBufferSize / 12).ToArray();
    bufferOffset += vertexBufferSize;

    // normals
    int vertexNormalsBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);
    mesh.normals = nativeBuffer.GetSubArray(bufferOffset, vertexNormalsBufferSize).Reinterpret<Vector3>(vertexNormalsBufferSize / 12).ToArray();
    bufferOffset += vertexNormalsBufferSize;

    // colors
    int vertexColorsBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);
    mesh.colors = nativeBuffer.GetSubArray(bufferOffset, vertexColorsBufferSize).Reinterpret<Color>(vertexNormalsBufferSize / 16).ToArray();
    bufferOffset += vertexColorsBufferSize;

    // triangles
    int trianglesBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);
    mesh.triangles = nativeBuffer.GetSubArray(bufferOffset, trianglesBufferSize).Reinterpret<int>(trianglesBufferSize / 4).ToArray();
    bufferOffset += trianglesBufferSize;

    // sh probes
    int reconstructedProbeBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);

    // set material and probe texture for reconstructed mesh
    Material reconstructedMat = new Material(Shader.Find("Custom/Differential"));

    rows = (int)Math.Ceiling((float)reconstructedProbeBufferSize / (float)cols);
    recbuffer = new NativeArray<float>(cols * rows, Allocator.TempJob);
    // only needed to fill up the complete texture
    recbuffer.CopyFrom(nativeBuffer.GetSubArray(bufferOffset, reconstructedProbeBufferSize).Reinterpret<float>(reconstructedProbeBufferSize / 4));
    bufferOffset += reconstructedProbeBufferSize;
    Texture2D shEnvTexture = new Texture2D(cols, rows, TextureFormat.RFloat, false);
    shEnvTexture.LoadRawTextureData(recbuffer);
    shEnvTexture.filterMode = FilterMode.Point;
    shEnvTexture.wrapMode = TextureWrapMode.Clamp;
    shEnvTexture.Apply(updateMipmaps: false);
    reconstructedMat.SetTexture("_SHTexture", shEnvTexture);


    float[] objectPos = new float[3]
    {
        PlacedObjects[i].Item1.transform.position.x,
        PlacedObjects[i].Item1.transform.position.y,
        PlacedObjects[i].Item1.transform.position.z
    };
    reconstructedMat.SetFloatArray("_ObjectPosition", objectPos);
    reconstructedMat.SetFloat("_ObjectScale",
        (PlacedObjects[i].Item1.transform.localScale * differentialShaderScaleFactor).magnitude);

    reconstructed.GetComponentInChildren<Renderer>().material = reconstructedMat;
    reconstructed.GetComponent<MeshFilter>().mesh = mesh;
    PlacedObjects[i] = Tuple.Create(PlacedObjects[i].Item1, reconstructed);
}

这些都是在列表中添加或删除数据的地方。为什么 Destroy() 函数对第二项不起作用?

终于找到问题了,隐藏的还不错。问题不是 destroy 函数不起作用,而是 Item2 的创建会在服务器每次发送新结果而不清理现有对象时创建一个新对象。

将相应代码更改为以下内容后:

if (hasReconstrucedData)
{
    if(PlacedObjects[i].Item2 == null)
        PlacedObjects[i] = Tuple.Create(PlacedObjects[i].Item1, new GameObject("Reconstructed Mesh " + i.ToString(), typeof(MeshFilter), typeof(MeshRenderer)));

    PlacedObjects[i].Item2.transform.position = new Vector3(0, 0, 0);
    PlacedObjects[i].Item2.transform.localScale = new Vector3(1, -1, 1);
    PlacedObjects[i].Item2.isStatic = true;
    Mesh mesh = new Mesh();

    // vertices
    int vertexBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);
    mesh.vertices = nativeBuffer.GetSubArray(bufferOffset, vertexBufferSize).Reinterpret<Vector3>(vertexBufferSize / 12).ToArray();
    bufferOffset += vertexBufferSize;

    // normals
    int vertexNormalsBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);
    mesh.normals = nativeBuffer.GetSubArray(bufferOffset, vertexNormalsBufferSize).Reinterpret<Vector3>(vertexNormalsBufferSize / 12).ToArray();
    bufferOffset += vertexNormalsBufferSize;

    // colors
    int vertexColorsBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);
    mesh.colors = nativeBuffer.GetSubArray(bufferOffset, vertexColorsBufferSize).Reinterpret<Color>(vertexNormalsBufferSize / 16).ToArray();
    bufferOffset += vertexColorsBufferSize;

    // triangles
    int trianglesBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);
    mesh.triangles = nativeBuffer.GetSubArray(bufferOffset, trianglesBufferSize).Reinterpret<int>(trianglesBufferSize / 4).ToArray();
    bufferOffset += trianglesBufferSize;

    // sh probes
    int reconstructedProbeBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset += sizeof(int);

    // set material and probe texture for reconstructed mesh
    Material reconstructedMat = new Material(Shader.Find("Custom/Differential"));

    rows = (int)Math.Ceiling((float)reconstructedProbeBufferSize / (float)cols);
    recbuffer = new NativeArray<float>(cols * rows, Allocator.TempJob);
    // only needed to fill up the complete texture
    recbuffer.CopyFrom(nativeBuffer.GetSubArray(bufferOffset, reconstructedProbeBufferSize).Reinterpret<float>(reconstructedProbeBufferSize / 4));
    bufferOffset += reconstructedProbeBufferSize;
    Texture2D shEnvTexture = new Texture2D(cols, rows, TextureFormat.RFloat, false);
    shEnvTexture.LoadRawTextureData(recbuffer);
    shEnvTexture.filterMode = FilterMode.Point;
    shEnvTexture.wrapMode = TextureWrapMode.Clamp;
    shEnvTexture.Apply(updateMipmaps: false);
    reconstructedMat.SetTexture("_SHTexture", shEnvTexture);
    reconstructedMat.SetFloatArray("_SHEnvironmentProbe", lightProbeValues);

    float[] objectPos = new float[3]
    {
        PlacedObjects[i].Item1.transform.position.x,
        PlacedObjects[i].Item1.transform.position.y,
        PlacedObjects[i].Item1.transform.position.z
    };
    reconstructedMat.SetFloatArray("_ObjectPosition", objectPos);
    reconstructedMat.SetFloat("_ObjectScale",
        (PlacedObjects[i].Item1.transform.localScale * differentialShaderScaleFactor).magnitude);

    PlacedObjects[i].Item2.GetComponentInChildren<Renderer>().material = reconstructedMat;
    PlacedObjects[i].Item2.GetComponent<MeshFilter>().mesh = mesh;
}

我想,在正常情况下,人们很容易看到多个物体,因为我猜会有 z-fighting 发生。然而,这里这些对象用于 AR 系统的差分渲染(即将虚拟对象的阴影渲染到真实世界的场景),因此它们大部分是透明的,为了调试,我只是在着色器中将输出颜色更改为红色。