如何从Physics.OverlapBox()获取碰撞数据?

bla*_*ole 8 c# collision-detection unity-game-engine

我正在尝试寻找其他注册冲突的方法(除了OnCollisionEnter()和之外OnCollisionExit()).我目前正在使用Physics.OverlapBox(),但我需要有关碰撞的更多信息; 即,正常,点.

我可以使用Physics.BoxCast(),但问题是它移动一个给定距离的盒子,使用maxDistance = 0f将无法工作.

我需要一种检查碰撞类似的方法,Physics.OverlapBox()除了它还会返回有关转换中所有碰撞的信息.

任何帮助表示赞赏.谢谢.

Ruz*_*ihm 5

您可以使用OverlapBox和 使用ColliderClosestPoint选择单个重叠点,并使用它来进行碰撞计算。

Collider[] cols = Physics.OverlapBox(...);
Vector3 myPosition = transform.position; // for example

foreach (Collider col in cols) {
    Vector3 closestPoint = col.ClosestPoint(myPosition);
    Vector3 positionDifference = (closestPoint-myPosition);
    Vector3 overlapDirection = positionDifference.normalized; 
}
Run Code Online (Sandbox Code Playgroud)

overlapDirection将指向远离您使用的位置ClosestPoint到每个碰撞体中心的方向。如果你想要基于物体表面的东西,你可以做的是使用重叠方向来放置瞄准你的物体的光线投射,以这种方式找到法线:

// ...

foreach (Collider col in cols) {
    Vector3 closestPoint = col.ClosestPoint(myPosition);
    Vector3 positionDifference = (closestPoint-myPosition);
    Vector3 overlapDirection = positionDifference.normalized; 

    RaycastHit hit;
    int layerMask = 1;  // Set to something that will only hit your object
    float raycastDistance = 10.0; // something greater than your object's largest radius, 
                                  // so that the ray doesn't start inside of your object
    Vector3 rayStart = myPosition + overlapDirection * raycastDistance;
    Vector3 rayDirection = -overlapDirection ;

    if (Physics.Raycast(rayStart, rayDirection, out hit, Mathf.Infinity, layerMask)) {
        Debug.Log(hit.normal);
        Debug.Log(hit.position);
    } else {
        // The ray missed your object, somehow. 
        // Most likely it started inside your object 
        // or there is a mistake in the layerMask
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。我理解你的方法,但有一个缺陷 - 想象一下,如果物体被对撞机表面上的一个点击中,但同一对撞机上的另一个点比实际碰撞点更接近“myPosition”。我们会得到有关碰撞法线、位置等的错误数据。解决此问题的唯一方法是我们是否可以“以某种方式”获得两个碰撞器之间的最近距离和方向。 (2认同)

isp*_*zax 4

您在第一个答案的评论中表达的担忧是有效的,但坏消息是没有简单的技巧可以解决它。您应该寻找的是所谓的连续碰撞检测,其简化版本在我关于类似问题的回答中描述:

基本上,对于场景中的每个移动物体,您必须在帧的一部分内计算下一次碰撞的时刻0<t0<1,然后将位置前进到帧内的时刻 t0,更新由于碰撞而产生的速度,并进一步进行下一次碰撞 t0<t1<1,直到您到达tn=1(帧结束)的时间,确保您不会由于计算的舍入或“角落”对象而卡在帧的中间。对于球形碰撞器,通常通过使用胶囊与胶囊(对于对象对)相交和胶囊与盒子作为边界来完成。

与我所指的答案中的简单引擎相反,Unity 具有连续的碰撞检测功能。因此,您可以启用连续碰撞和连续动态,这在计算上是非常昂贵的。

您还可以尝试使用RigidBody.SweepTest,它会返回最接近的碰撞信息。请注意,即使还有RigidBody.SweepTestAll,它也没有多大帮助。不仅因为它仅返回前 128 个碰撞,还因为它不处理它们,因为没有反射。对于物理上正确的行为,您必须执行上述操作 - 提前时间直到第一次碰撞并更新速度。无论是使用物理引擎还是您自己。这是非常昂贵的,并且没有多少游戏这样做,甚至使用简化的对象进行作弊(球体是最便宜的,因为两个扫过的球体是两个胶囊,它们的交集是一个相对便宜的计算),但是步骤量很大,特别是在“角落”中“当物体无处可去并因此不断碰撞的情况可能会非常大,而且这种情况发生的次数超出了人们的预期。

对于复杂的对象,您不太可能比 SweepTest 做得更好,除非您基于更简单的原语(例如Physics.BoxCast或 )触发它Physics.SphereCast。同样,即使有Physics.BoxCastAllPhysics.SphereCastAll它们也不是特别有用,因为只能保证发生第一次碰撞。这些 xxxCastAll 是您编写的您正在寻找的函数,因此请尝试一下,它们可能足以满足您的用例。