【问题标题】:OverlapBox returns null almost every timeOverlapBox 几乎每次都返回 null
【发布时间】:2022-09-27 16:31:57
【问题描述】:

我试图制作一个脚本来在随机位置生成对象而不会相互碰撞。它不能正常工作,因为 OverlapBox 几乎每次都返回 null,即使它接触到正方形也是如此。

这是脚本:

var quadBoundaries = quad.GetComponent<MeshCollider>().bounds;
var squareRadius = new Vector2(1, 1);

        foreach (var square in squaresToSpawn)
        {
            _isOverlapping = true;
            while (_isOverlapping)
            {
                _spawnPoint = new Vector2(Random.Range(quadBoundaries.min.x + 1.5f, quadBoundaries.max.x - 1.5f), 
                    Random.Range(quadBoundaries.min.y + 1.5f, quadBoundaries.max.y - 1.5f));

                _collisionWithSquare = Physics2D.OverlapBox(_spawnPoint, squareRadius, 
                    0, LayerMask.GetMask(\"Square Layer\"));

                if (_collisionWithSquare is null)
                {
                    square.transform.position = _spawnPoint;
                    _isOverlapping = false;
                }
            }
        }   

四边形是我放置的四边形的边界,因此正方形将在有限的空间中随机生成。
我的理解是,我在四边形边界中生成一个随机点,然后我检查在该点上是否适合比例尺 (1,1) 的正方形,而无需触及任何其他具有对撞机且位于正方形图层上的东西。如果它接触,那么我会生成一个新点,直到碰撞为空,这样我就可以将正方形放置在指定位置。
但是一堆我不明白的事情正在发生。
第一的,正方形相互接触。第二,只有几个特定的​​方块记录了碰撞,但即使是那些也被其他方块触摸了。第三,当我放大正方形半径(例如 10,10)时,正方形之间会出现很大的裂痕(如下图所示)。

我必须补充一点,所有正方形都有一个对撞机,都在正方形层上,而四边形在不同的层上。
任何人都可以向我解释我没有到达这里吗?非常感谢!

  • 你能从场景视图中显示一张选中所有四边形和正方形的图片吗?
  • 具有与上图相同的半径?
  • 如果图片中的正方形大小是 10x10,你知道 1x1 的面积只有 1%,所以测试可能返回 null。
  • 白色方块的大小都是 (1,1),但 OverlapBox 的半径是 (10,10)。当我将 OverlapBox 的半径设置为 (1,1) 时,我会得到一堆随机生成的正方形
  • 在你点击 Play 之前,你的方块在哪里? IE。在您的循环中,它们是否已经与未定位的正方形发生碰撞?另请注意,如果没有剩余可能的位置(一旦它被修复),您的代码可能会出现无限循环,您可以通过给它一个 max-tries 计数器中断来避免这种情况。您也可以考虑将 squareRadius 更改为使用实际的正方形大小,从而消除另一个可能的错误来源。祝你好运。

标签: c# unity3d


【解决方案1】:

在回答之前我想说,这样的生成算法是非常危险的,因为你可以进入一个无限循环,此时没有新的正方形的地方。为使此代码更安全,您可以做的最低限度是添加重试计数以查找生成位置。但我会把它留给你的良心。

为了使这个算法有效,你应该明白 Unity 中的所有物理都是在固定更新中更新的。因此,您使用PhisycsPhisics2D 执行的所有操作都在使用在上次Pyhsics 更新中执行的对象状态。当您更改对象的位置时,物理核心不会立即捕获此更改。作为一种解决方法,您可以分别在固定更新中生成每个对象。像这样:

public class Spawner : MonoBehaviour
{

    [SerializeField] private GameObject[] _squaresToSpawn;
    [SerializeField] private GameObject _quad;
    
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(Spawn());
    }

    private IEnumerator Spawn()
    {
        var quadBoundaries = _quad.GetComponent<MeshCollider>().bounds;
        var squareRadius = new Vector2(1, 1);
        Vector2 spawnPoint;
        
        foreach (var square in _squaresToSpawn)
        {
            yield return new WaitForFixedUpdate();
            var isOverlapping = true;
            var retriesCount = 10;
            while (isOverlapping && retriesCount > 0)
            {
                
                spawnPoint = new Vector2(Random.Range(quadBoundaries.min.x + 1.5f, quadBoundaries.max.x - 1.5f), 
                    Random.Range(quadBoundaries.min.y + 1.5f, quadBoundaries.max.y - 1.5f));

                var hit = Physics2D.OverlapBox(spawnPoint, squareRadius,
                    0, LayerMask.GetMask("Square"));
                
                if (hit is null)
                {
                    square.transform.position = spawnPoint;
                    isOverlapping = false;
                }
                else
                {
                    retriesCount--;
                    if (retriesCount == 0)
                    {
                        Debug.LogError("Can't find place to spawn the object!!");
                    }
                }
            }
        }  
    }
}

但是这样的代码会产生连续产生的效果:

使对象在一帧内正确生成。您应该手动将所有生成的对象边界框存储在代码中,并手动检查您的新生成对象边界框是否与先前生成的对象发生冲突。

【讨论】:

    猜你喜欢
    • 2017-10-07
    • 1970-01-01
    • 1970-01-01
    • 2016-05-22
    • 1970-01-01
    • 2013-08-19
    • 2012-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多