【问题标题】:GameObject counted as destroyed but not really destroyedGameObject 算作已销毁但并未真正销毁
【发布时间】:2019-06-11 00:11:52
【问题描述】:

我目前正在制作一个 Breakout 风格的游戏,并且在玩超过 5 或 6 个球时会出现破坏砖块的问题(没有能够重现此错误的可靠数字,它只会在有很多球时发生在玩)。有双倍和三倍球的力量,所以球的数量可以在短时间内迅速增加。

在我的 GameManager 脚本中,我会在场景开始时记录积木的初始数量,并在每次球与积木接触时减 1。积木数量为零后,玩家获胜,游戏结束。该游戏适用于少量球,但是当涉及太多球时,该过程似乎会中断。

GameManager.cs 的相关代码:

void Start()
{
    livesText.text = "Lives: " + Lives;
    numOfBalls = GameObject.FindGameObjectsWithTag("Ball").Length;
    numOfBricks = GameObject.FindGameObjectsWithTag("Brick").Length;
    //Debug.Log(numOfBalls);
}

public void UpdateBrickNumber()
{
    numOfBricks--;
    if(numOfBricks <= 0)
    {
        numOfBricks = 0;
        GameOver();
    }
}

void GameOver()
{
    gameOver = true;
    if(numOfBricks == 0)
    {
        WinPanel.SetActive(true);
    }
    else
    {
        GameOverPanel.SetActive(true);
    }
}
...

处理碰撞的球码:

void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.transform.CompareTag("Brick"))
    {
        Debug.Log("Brick Hit");
        //Chance of powerup to spawn
        int rand = Random.Range(1, 101);
        //Chance of each power up
        int rand2 = Random.Range(1, 101);



        if (rand < 7)
        {
            if (rand2 >=1 && rand2<=20) {
                Instantiate(SpeedBall, collision.transform.position, collision.transform.rotation);
                //Debug.Log(" Speed Power Up Created ");
            }
            ...
            Rest of  power ups
            ...
        }
        Transform newExplosion = Instantiate(explosion, collision.transform.position, collision.transform.rotation);
        Destroy(newExplosion.gameObject, 2f);
        gm.UpdateBrickNumber();
        Destroy(collision.gameObject);
    }
}

调试日志确实显示了正确数量的砖块被击中,但我认为有些球在有机会被摧毁之前就击中了同一个砖块。如何确保在另一个球击中砖块上的对撞机之前砖块已被摧毁?

动作中的错误:

【问题讨论】:

  • 你没有说真正的问题是什么。

标签: c# unity3d game-physics


【解决方案1】:

假设您在一帧中遇到多次碰撞,导致积木数量少于剩余积木数量,这应该可以解决它:

void OnCollisionEnter2D(Collision2D collision)
{   //Check if the brick has already been processed
    if (collision.transform.CompareTag("Brick") && collision.collider.enabled) 
    {
        Debug.Log("Brick Hit");
        //Chance of powerup to spawn
        int rand = Random.Range(1, 101);
        //Chance of each power up
        int rand2 = Random.Range(1, 101);



        if (rand < 7)
        {
            if (rand2 >=1 && rand2<=20) {
                Instantiate(SpeedBall, collision.transform.position, collision.transform.rotation);
                //Debug.Log(" Speed Power Up Created ");
            }
            ...
            Rest of  power ups
            ...
        }
        Transform newExplosion = Instantiate(explosion, collision.transform.position, collision.transform.rotation);
        Destroy(newExplosion.gameObject, 2f);
        gm.UpdateBrickNumber();
        //Mark the brick as hit already so other balls can't hit it!
        collision.collider.enabled = false; 
        Destroy(collision.gameObject);
    }
}

说明

来自documentation

实际的对象销毁总是延迟到当前更新循环之后,但总是在渲染之前完成。

Destroy 不是即时的。它将在更新循环之后发生,这意味着如果两个球在同一帧中击中,您的计数将减少 2,但只会破坏一块砖。

【讨论】:

  • 错误:“属性碰撞。启用不能分配给--它是只读的”将其位置更改为屏幕外会产生相同的效果,还是有其他方法可以使用启用的功能
  • @LarrBear 抱歉,已修复。你想禁用对撞机,但我不小心写了碰撞
  • @LarrBear 为了回答你的问题,不,我不相信改变位置会解决它。如果我没记错的话,Unity 引擎物理阶段会计算所有碰撞,将 em 放入列表中,然后在碰撞阶段处理它们。这意味着即使你改变了位置,碰撞已经被检测到,所以OnCollision函数仍然会被调用。 (~70% 确定)
  • @LarrBear 这可能会有所帮助。注意内部物理阶段和碰撞之间的差距!此外,oncollision 和 ondestroy 之间的巨大差距! docs.unity3d.com/Manual/ExecutionOrder.html
  • 谢谢!最初它并没有太大的区别,但我将它作为实例化和其他函数之前 if 循环的第一行,现在它运行良好!
【解决方案2】:

快速简短回答

在 C# 和 Java 中,对象不会立即销毁,它们会放入要删除的进程中,有时会立即发生,有时不会。

有时,其他对象仍然引用它们。解决方案是删除之前的那些引用。

void OnCollisionEnter2D(Collision2D collision)
{
        ...

        Transform newExplosion = Instantiate(explosion, collision.transform.position,     collision.transform.rotation);


        // notify other objects that reference "newExplosion.gameObject"
        Destroy(newExplosion.gameObject, 2f);

        newExplosion.gameObject = null;


        gm.UpdateBrickNumber();

        // notify other objects that reference "collision.gameObject"
        Destroy(collision.gameObject);

        collision.gameObject = null;

        ...
}  // void OnCollisionEnter2D(...)

干杯。

【讨论】:

  • 错误:“Property collision.gameObject 不能分配给 - 它是只读的”与 newExplosion.gameObject 相同
  • 这和C#或者Java无关,Destroy是Unity特有的函数,实际销毁是有时间的。查看我的答案 + 文档
猜你喜欢
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-22
  • 2022-01-03
相关资源
最近更新 更多