盒子与飞机相撞 - 奇怪的旋转

问题描述 投票:0回答:1

我正在关注游戏物理引擎开发书。我理解分割碰撞:首先生成接触,然后根据其方向、位置、穿透等来解决它。我已经有

sphere - plane
sphere - sphere
碰撞正常工作。

但是,我在

box - plane
方面遇到了重大问题。这是当前外观的视频:https://imgur.com/a/YqEjCoK

请注意,立方体有一些非常可疑的行为。它永远不会落在一侧,并且永远卡在单个顶点或单侧上。

我想了解地面和盒子之间第一次发生碰撞的情况。请注意,虽然底面的顶点位于同一平面上,以相同的速度行进并同时均匀地撞击地面,但最终会向每个顶点施加不同的冲量,从而错误地产生旋转。

我关注的书找到具有最大穿透力的接触,对其应用旋转/线性变化,然后根据具有最大穿透力的接触的角度/线性分量调整所有其他接触的穿透力:

  var velocityChange = [float3.zero, float3.zero]
  var rotationChange = [float3.zero, float3.zero]

  velocityIterationsUsed = 0

  while (velocityIterationsUsed < velocityIterations) {
    var max = velocityEpsilon
    var index = contacts.count

    // find contact with greatest "penetration"
    for i in 0 ..< contacts.count {
      if (contacts[i].desiredDeltaVelocity > max) {
        max = contacts[i].desiredDeltaVelocity
        index = i
      }
    }

    if (index == contacts.count) {
      break
    }

    // Match the awake state at the contact
    contacts[index].matchAwakeState()

    // Apply velocity change to the greatest penetration contact
    contacts[index].applyVelocityChange(
      velocityChange: &velocityChange,
      rotationChange: &rotationChange
    )

    // Loop all generated contacts
    for i in 0 ..< contacts.count {
      // Check each body in the contact
      for b in 0 ..< 2 {
        if (contacts[i].bodies[b] != nil) {
          // Check for a match with each body in the newly resolved contact
          for d in 0 ..< 2 {
            if (contacts[i].bodies[b] == contacts[index].bodies[d]) {
              let deltaVel = velocityChange[d] + cross(rotationChange[d], contacts[i].relativeContactPosition[b])

              print("""
                    =======================
                    contact idx \(i):
                    velocityChange: \(velocityChange[d])
                    rotationChange: \(rotationChange[d])
                    relativeContactPosition: (\(contacts[i].relativeContactPosition[b].x) \(contacts[i].relativeContactPosition[b].y) \(contacts[i].relativeContactPosition[b].z))
                    deltaVel: (\(deltaVel.x) \(deltaVel.y) \(deltaVel.z))
              """)

              // deltaVel does not make sense on first impact

            }
          }
        }
      }
    }

  }

  velocityIterationsUsed += 1

}

注意

deltaVel
变量,它根据线性和角度分量重建每个接触速度偏移。在第一次盒子撞击时,由于底面的 4 个顶点同时以相同的速度均匀撞击地面,
deltaVel
报告了非常奇怪的值:

=======================
contact idx 0:
velocityChange: SIMD3<Float>(0.0, 3.1689641, 0.0)
rotationChange: SIMD3<Float>(0.26407933, 0.0, 0.2640793)
relativeContactPosition: (0.5 -0.5010185 -0.50000006)
deltaVel: (0.13230862 3.4330435 -0.13230863)
=======================
contact idx 1:
velocityChange: SIMD3<Float>(0.0, 3.1689641, 0.0)
rotationChange: SIMD3<Float>(0.26407933, 0.0, 0.2640793)
relativeContactPosition: (-0.5 -0.5010185 -0.50000006)
deltaVel: (0.13230862 3.1689641 -0.13230863)
=======================
contact idx 2:
velocityChange: SIMD3<Float>(0.0, 3.1689641, 0.0)
rotationChange: SIMD3<Float>(0.26407933, 0.0, 0.2640793)
relativeContactPosition: (0.5 -0.5010185 0.4999999)
deltaVel: (0.13230862 3.1689641 -0.13230863)
=======================
contact idx 3:
velocityChange: SIMD3<Float>(0.0, 3.1689641, 0.0)
rotationChange: SIMD3<Float>(0.26407933, 0.0, 0.2640793)
relativeContactPosition: (-0.5 -0.5010185 0.4999999)
deltaVel: (0.13230862 2.9048848 -0.13230863)

注意这条线

let deltaVel = velocityChange[d] + cross(rotationChange[d], contacts[i].relativeContactPosition[b])

它如何为每个顶点产生几乎相同的

deltaVel
,除了 Y 分量随着每个下一个顶点逐渐减小?但为什么会减少呢?我在 matlab 中对每个顶点做了这个方程,它确实是正确的。据我了解,它应该沿着所有顶点相等(因为它们位于同一平面上)?这样它们都会以相同的 Y 速度反弹,并且不会发生旋转。

如您所见,迭代之间唯一不同的值是

contact[i].relativeContactPosition
。由于盒子的宽度、高度和深度均为 1,因此每个轴上的这些值约为 +/- 0.5。

// vertex #1
let linearChange0 = float3(0, 3.1854, 0.0)
let angularChange0 = float3(0.265, 0, 0.265)
let relativeContactPos0 = float3(-0.5, -0.5265012, -0.50000006)
linearChange0 + cross(angularChange0, relativeContactPos0) = (0.139522806, 3.18540001, -0.139522806)

// vertex #2
let linearChange1 = float3(0, 3.1854, 0.0)
let angularChange1 = float3(0.265, 0, 0.265)
let relativeContactPos1 = float3(-0.5, -0.5265012, 0.4999999)
linearChange1 + cross(angularChange1, relativeContactPos1) = (0.139522806, 2.92040014, -0.139522806)

鉴于两个方程之间的唯一区别是

relativeContactPos
值的 Z 分量,为什么生成的 Y 值不同?如何让它们统一?

TLDR:上面所附的 IMGUR 视频中的每个顶点都会获得不同的 Y 速度偏移,即使它们都位于同一平面上,以相同的速度行进。该怎么办?

game-physics physics physics-engine
1个回答
0
投票

考虑到两个方程之间的唯一区别是 Z

relativeContactPos
值的分量,为什么得到的Y 价值不同?如何让它们统一?

因为你正在计算叉积。只有离轴项对所得轴项有贡献:

您采用的叉积是正确的——毕竟,扭矩是叉积——但显然采用的是错误的叉积。

计算出的速度变化值 (

velocityChange
) 看起来是正确的,位置矢量 (
relativeContactPos
) 也是如此。

我们需要的是每个顶点上的扭矩产生的冲量,我们知道它应该采用以下形式

由于扭矩由下式给出

由于冲量等于力乘以时间,我们可以为扭矩引起的冲量构建以下方程:

但是由于加速度等于速度随时间的变化,这变成了

因此,叉积应该是

cross(contacts[i].relativeContactPosition[b], velocityChange[d])

这将导致完全偏移的向量如下所示(自上而下视图)

但请注意,这不处理脉冲方面,仅处理扭矩方面。扭矩的大小将根据质量和时间增量来缩放。在这种正面碰撞的特定情况下,这并不重要,因为产生的扭矩相互抵消,但在其他情况下,它们的脉冲计算会被错误地缩放。

由于您没有包含完成冲量计算所需的必要信息,因此无法确定您需要做什么,但从广义上讲,扭矩对速度变化的完整贡献将计算为:

cross(contacts[i].relativeContactPosition[b], velocityChange[d]) * deltaT * mass
© www.soinside.com 2019 - 2024. All rights reserved.