Unity销毁对象及其对象列表

时间:2019-02-25 22:48:01

标签: algorithm unity3d recursion stack-overflow

我正在尝试销毁许多对象,这些对象也需要销毁。

public class Ball : MonoBehaviour
{
    List<Transform> collidesColor = new List<Transform>();

   //is Colding?
    void OnCollisionEnter(Collision c)
    {
        if (c.transform.GetComponent<Renderer>() && c.transform.GetComponent<Renderer>().material.color == GetComponent<Renderer>().material.color)
        {
            collidesColor.Add(c.transform);
        }
    }

    void OnCollisionExit(Collision c)
    {
        collidesColor.Remove(c.transform);
    }

    //gave stack overflow errors
    //is mostly deleted
    /*void DestroySameColor()
    {
        foreach (Transform t in collides)
        {
            if (t != null && t.GetComponent<Renderer>() && t.GetComponent<Renderer>().material.color == GetComponent<Renderer>().material.color)
            {
                t.SendMessage("DestroySameColor");
            }
        }
        Destroy(gameObject);
    }*/

    //froze unity
    /*IEnumerator*/ void OnMouseDown()
    {
        //Instantiate(ball, new Vector3Int(Random.Range(-6, 6), 75, Random.Range(1, 13)), Quaternion.identity);
        for (var j = 0; j < collidesColor.Count; j++)
        {
            for (var i = 0; i < collidesColor[j].GetComponent<Ball>().collidesColor.Count; i++)
            {
                if (collidesColor[j].GetComponent<Ball>().collidesColor[i]==null || collidesColor.Contains(collidesColor[j].GetComponent<Ball>().collidesColor[i]))
                {
                    collidesColor[j].GetComponent<Ball>().collidesColor.RemoveAt(i);
                    i--;
                }
                //yield return null;
            }
            collidesColor.AddRange(collidesColor[j].GetComponent<Ball>().collidesColor);
            Destroy(collidesColor[j].gameObject);
            j--;
            //yield return null;
        }
        Destroy(gameObject);
    }
}

我已经尝试过递归算法,但是出现堆栈溢出错误。 DestroySameColors()是残缺的部分。我还尝试获取每个对象列表并将其添加到一个巨大的列表中,但是这冻结了团结并使其成为无法生存的选择

我不确定其算法或编程执行是否错误。他们都可能有缺陷

我也很确定此算法有一个魔术关键字或名称,它将在Google上解锁许多答案,但我不知道这些答案是什么

我将如何删除列表中包含需要销毁更多对象的许多对象?

1 个答案:

答案 0 :(得分:2)

问题是您的Ball对象彼此碰撞

=>所以Ball1Ball2的列表中,反之亦然

=>因此消息DestroySameColor在它们之间来回ping通,因为它们运行的​​是foreach,其中包括最初产生整个调用的组件……直到到达StackOverflow。


首先,我实际上会向enum组件本身添加一个适当的Ball颜色字段,并且从一开始就只收集那些具有相同颜色的碰撞,例如

//all the colours you have
public enum BallColor
{
    Blue,
    Red,
    // ...
}

public class Ball: MonoBehaviour
{
    // set this in the inspector or while instantiating etc
    public BallColor color;

    //...
}

那么您现在就可以做

void OnCollisionEnter(Collision c)
{
    var ball = c.gameObject.GetComponent<Ball>();

    // if no Ball component or different color ignore
    if (!ball || ball.color != color) return;

    // skip if already contains this ball
    if(collidingBalls.Contains(ball)) return;

    // ad to list
    collidingBalls.Add(ball);
}

void OnCollisionExit(Collision c)
{
    var ball = c.gameObject.GetComponent<Ball>();

    // if no Ball component ignore
    if (!ball) return;

    // if not contains ignore
    if(!collidingBalls.Contains(ball)) return;

    // remove from the list
    collidingBalls.Remove(ball);
}

然后到破坏部分

  1. 使整个销毁过程仅由一个对象处理。因此,请确保以某种方式仅DestroySameColorColliding被一个球调用!
  2. 在销毁任何物品之前,请将所有要销毁的物品收集在一个列表中,以确保没有重复或意外-特别要确保一个主要物品不在列表中
  3. 销毁列表中的所有对象
  4. 将主要对象销毁为最后一个

总共是这样的

public class Ball: MonoBehaviour
{
    //all the colors you have
    public enum BallColor
    {
        Blue,
        Red,
        // ...
    }

    private readonly List<Ball> _collidingBalls = new List<Ball>();

    // set this in the inspector or while instantiating etc
    public BallColor Color;

    private IEnumerable<Ball> GatherSubcollidingBalls(List<Ball> ignoreThose)
    {
        var output = new List<Ball>();

        // add yourself to the ignored list
        var newIgnoreThose = ignoreThose;
        newIgnoreThose.Add(this);

        // filter out only references that are not in ignoreThose
        var withoutIgnored = _collidingBalls.Except(ignoreThose);

        // add the filtered references to the output
        output.AddRange(withoutIgnored);

        // iterate your collidingBalls but ignore the once from ignoreThose
        foreach (var ball in _collidingBalls.Except(ignoreThose))
        {
            // get this balls collisions ignoring the newIgnoreThose
            var coll = ball.GatherSubcollidingBalls(newIgnoreThose);

            // filter out only Ball references that are not in output already
            var filtered = coll.Except(output).ToList();

            // especially remove this reference which is the main object of the call
            if (filtered.Contains(this)) filtered.Remove(this);

            // add the filtered objects
            output.AddRange(filtered);
        }

        return output;
    }

    private void OnCollisionEnter(Collision c)
    {
        var ball = c.gameObject.GetComponent<Ball>();

        // if no Ball component or different color ignore
        if (!ball || ball.Color != Color) return;

        // skip if already contains this ball
        if (_collidingBalls.Contains(ball)) return;

        // ad to list
        _collidingBalls.Add(ball);
    }

    private void OnCollisionExit(Collision c)
    {
        var ball = c.gameObject.GetComponent<Ball>();

        // if no Ball component ignore
        if (!ball) return;

        // if not contains ignore
        if (!_collidingBalls.Contains(ball)) return;

        // remove from the list
        _collidingBalls.Remove(ball);
    }

    public void DestroySameColorColliding()
    {
        // get all collisions ignoring yourself
        var toDestroy = GatherSubcollidingBalls(new List<Ball> { this });

        // destroy all other objects of the same color and in same collision chain
        foreach (var ball in toDestroy)
        {
            Destroy(ball.gameObject);
        }

        // finally destroy yourself
        Destroy(gameObject);
    }
}