确定使用外力时球体和平面之间的静止接触

时间:2012-05-01 05:12:37

标签: c++ algorithm physics game-physics

这个问题有一个主要问题,还有一个小问题。我相信我的研究中的任何一个问题都是正确的,但不是两者都适用。

对于我的物理循环,我做的第一件事就是对我的TotalForce施加一个引力,用于刚体对象。然后,我使用TotalForceVelocity检查是否存在冲突。我的TotalForce在每次物理循环后重置为(0, 0, 0),但我会保留velocity

我熟悉在仅使用速度时在移动球体和静态平面之间进行碰撞检查。但是,如果我除了velocity之外还有其他力量,比如引力,该怎么办?我将其他力量放入TotalForces(此时我只有引力)。为了弥补这一点,当我确定球体当前没有与平面重叠时,我做

    Vector3 forces = (sphereTotalForces + sphereVelocity);
    Vector3 forcesDT = forces * fElapsedTime;
    float denom = Vec3Dot(&plane->GetNormal(), &forces);

然而,这可能是我认为假设是静止接触的问题。我认为休息联系是由

计算的
denom * dist == 0.0f

dist

的位置
float dist = Vec3Dot(&plane->GetNormal(), &spherePosition) - plane->d;

(作为参考,显而易见的denom * dist > 0.0f意味着球体正在离开飞机)

然而,这绝不是真的。即使看起来有“休息接触”。这是因为上面的forces计算总是至少有-9.8(我的重力).y。当朝向法线为(0,1,0)的平面移动时,将产生denom的-9.8的y。

我的问题是

1)我是否正确计算了我的前两个代码片段中提到的休息联系方式?

如果是的话,

2)我的“其他力量”如引力如何使用?我对TotalForces的使用是否不正确?

作为参考,我的时间步长是

  mAcceleration = mTotalForces / mMass;
  mVelocity += mAcceleration * fElapsedTime;
  Vector3 translation = (mVelocity * fElapsedTime);

修改

由于某些建议的更改似乎会改变我的碰撞代码,因此以下是我检测碰撞状态的方法

if(fabs(dist) <= sphereRadius)
{ // There already is a collision }
else
{
    Vector3 forces = (sphereTotalForces + sphereVelocity);
    float denom = Vec3Dot(&plane->GetNormal(), &forces);

    // Resting contact
    if(dist == 0) { }
    // Sphere is moving away from plane
    else if(denom * dist > 0.0f) { }
    // There will eventually be a collision
    else
    {
        float fIntersectionTime = (sphereRadius - dist) / denom;
        float r;
        if(dist > 0.0f)
            r = sphereRadius;
        else
            r = -sphereRadius;

        Vector3 collisionPosition = spherePosition + fIntersectionTime * sphereVelocity - r * planeNormal;
    }
}

6 个答案:

答案 0 :(得分:1)

  1. 你应该使用if(fabs(dist) < 0.0001f) { /* collided */ }这是为了获得浮点精度。你绝对不会在大多数角度或接触时得到精确的0.0f。

  2. dist的值,如果为负,实际上是您将身体移回平面表面所需的实际量,以防它穿过平面。 sphere.position = sphere.position - plane.Normal * fabs(dist);

  3. 一旦将其移回表面,您可以选择使其在平面法线的相反方向上反弹;或者只是留在飞机上。

    parallel_vec = Vec3.dot(plane.normal, -sphere.velocity);

    perpendicular_vec = sphere.velocity - parallel_vec;

    bounce_velocity = parallel - perpendicular_vec;

  4. 除非一切都有单位质量,否则你不能盲目地totalforce = external_force + velocity

  5. 修改

    1. 要在3D空间中完全定义平面,平面结构应存储平面法线向量和平面上的点。 http://en.wikipedia.org/wiki/Plane_(geometry)。
    2. Vector3 planeToSphere = sphere.point - plane.point;

      float dist = Vector3.dot(plane.normal, planeToSphere) - plane.radius;

      if(dist < 0) { // collided. }

      如果这是你不知道的部分,我建议你先学习更多的数学。

      注意:抱歉,格式化乱了......我无法将其标记为代码块。

      编辑2 : 基于我对你的代码的理解,要么你正在严格命名你的变量,要么就像我之前提到的那样,你需要修改你的数学和物理理论。这一行没有任何用处。

      float denom = Vec3Dot(&plane->GetNormal(), &forces);

      在任何时候,球体上的力可以在任何方向上与行进方向无关。所以denom基本上计算了平面方向的力量,但没有告诉你球是否会撞到飞机。例如重力是向下的,但是球可以有向上的速度并击中上面的一个平面。有了这个,你需要Vec3Dot(plane.normal, velocity)代替。

      另外,Mark Phariss和Gerhard Powell已经为您提供了线性运动学的物理方程,您可以使用它们直接计算未来的位置,速度和撞击时间。

      e.g。 s = 0.5 * (u + v) * t;在未来时间t之后给出位移。将该位移与距离平面的距离进行比较,可以得出球体是否会撞击平面。所以,我建议您先阅读http://en.wikipedia.org/wiki/Linear_motion以及简单的内容然后http://en.wikipedia.org/wiki/Kinematics

      另一种方法,如果你期望或假设没有其他力量作用于球体,那么你进行射线/平面碰撞测试以找到它将击中飞机的时间t,在这种情况下,读取{{ 3}}。

答案 1 :(得分:0)

总是有-9.8y的重力作用于球体。在悬浮球体的情况下,这将导致向下加速(净力不为零)。在球体搁置在平面上的情况下,这将导致平面在球体上施加法向力。如果平面完全水平且球体处于静止状态,则该法向力将精确地为+ 9.8y,这将完全抵消重力。对于静止在非水平面上的球体,法向力为9.8y * cos(angle)(角度在-90到+90度之间)。

当移动的球体撞击平面时,事情变得更加复杂,因为法向力将取决于速度和平面/球体材料属性。根据您的应用要求,您可以忽略这一点,或者尝试使用正常力量的一些事情,看看它是如何工作的。

针对您的具体问题:

  1. 我认为联系更具体地说是dist == 0.0f,即球体和飞机正在接触。我假设您的碰撞考虑到球体可能在任何物理时间步长中移动经过飞机。
  2. 现在,当他们接触时,你似乎没有任何法向力从飞机上放到球体上。我会通过检查联系人(dist == 0.0f)来做到这一点,如果是,则将法向力添加到球体中。在球体落到近水平面(角度在-90到+90度)的简单情况下,它只是sphereTotalForces += Vector3D(0, 9.8 * cos(angle), 0)
  3. 修改

    根据问题和代码的详细信息(未给出),从here计算从球体边缘到平面的距离可能不正确。假设你的飞机经过原点,正确的等式是:

    dist

    这与dist = Vec3Dot(&spherePosition, &plane->GetNormal()) - sphereRadius; 的公式相同。请注意,如果飞机不在原点,请使用:

    plane->d == sphereRadius

答案 2 :(得分:0)

解决您的物理问题:

f = mg + other_f; // m = mass, g = gravity (9.8)
a = f / m; // a = acceleration
v = u + at; // v = new speed, u = old speed, t = delta time
s = 0.5 * (u + v) *t;

碰撞时,如果要反弹,则将两个速度都更改为0(或v和u = - (u * 0.7))。

因为速度= 0,球静止不动。

如果是2D或3D,那么您只需将曲面法线方向的速度更改为0,并保持平行速度相同。这将导致球在表面滚动。

如果切割表面,则必须将球移动到表面。您可以将碰撞距离设置为少量(例如0.001),以确保它保持静止。

http://www.physicsforidiots.com/dynamics.html#vuat

编辑:

NeHe是游戏引擎设计的一个惊人来源: 这是一个关于碰撞检测的页面,有很好的描述: http://nehe.gamedev.net/tutorial/collision_detection/17005/

编辑2 :(来自NeHe)

double DotProduct=direction.dot(plane._Normal); // Dot Product Between Plane Normal And Ray Direction
Dsc=(plane._Normal.dot(plane._Position-position))/DotProduct; // Find Distance To Collision Point
Tc= Dsc*T / Dst
Collision point= Start + Velocity*Tc

答案 3 :(得分:0)

这个问题的确切解决方案涉及一些非常严肃的数学。如果你想要一个近似的解决方案,我强烈建议分阶段开发它。

1)确保你的SIM卡没有重力。球必须穿过空间并与有角度的无摩擦表面发生非弹性(或部分弹性)碰撞。

2)引入引力。这将改变从直线到抛物线的弹道轨迹,并引入滑动,但它不会对碰撞产生太大影响。

3)引入静摩擦和动摩擦(独立)。这些将改变滑动的动态。暂时不要担心碰撞中的摩擦。

4)给出球的角速度和惯性矩。这是一大步。确保您可以对其施加扭矩并获得逼真的角加速度。请注意,旋转质量的实际行为可能违反直觉。

5)尝试在重力作用下沿着水平面滑动球。如果你已经做好了一切,它的角速度将逐渐增加,其线速度逐渐减小,直到它变成一个滚动。尝试给球一些初始旋转(“画”,“跟随”或“英语”)。

6)尝试相同,但在倾斜的表面上。这是一个相对较小的步骤。

如果你走得这么远,你将拥有一个非常逼真的SIM卡。不要试图跳过任何步骤,你只会让自己头疼。

答案 4 :(得分:0)

我建议在那之后看一下erin cato文章(Box2D的作者)和Glenn fiedler文章。 重力是一种强烈的加速度,并产生强大的力量。由于浮动不精确,可变时间步和非线性积分,很容易出现错误的模拟。 球体在平面上重新定位,以防它开始自行穿过平面是必须的,我注意到自己最好只在球体的速度与平面法线相反时这样做(这可以比较)在3D渲染中面对剔除:不要考虑背面的飞机。)

此外,大多数物理引擎停止对闲置物体进行模拟,大多数游戏在移动时都不会考虑重力,只有在坠落时才会考虑。他们使用“导航网格”和自定义系统,只要他们确信模拟对象坚持其“基础”。

我不知道那里有一个完美的物理模拟器,总会有一个整合爆炸,一个错过的碰撞(寻找“甜蜜的碰撞”)....它需要大量的经验性微调。 / p>

另外,我建议您寻找“冲动”,这是一种避免在遇到碰撞时手动调整速度的方法。

另请参阅“每个计算机科学家应该了解浮点数”

祝你好运,你进入一个雷场,随机不可理解,咬指数字计算机科学领域:)

答案 5 :(得分:-1)

为了获得更高的保真度(不会解决您的主要问题),我会将您的时间步长更改为

mAcceleration = mTotalForces / mMass;
Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2);
mVelocity += mAcceleration * fElapsedTime;

你提到球体是刚体;你是否也将飞机建模为刚性?如果是这样的话,你会在接触时获得无限的力量。完全弹性碰撞,没有明显的动量消散。

力量&amp;速度无法求和(不兼容的单位);如果你只是试图模拟运动学,你可以忽略质量并使用加速度&amp;只有速度。

假设球体被简单地放在水平面上并且完全没有弹性碰撞(没有反弹),你可以做[N.B.,我真的不知道C语法,所以这将是Pythonic]

mAcceleration = if isContacting then (0, 0, 0) else (0, -9.8, 0)

如果你为碰撞添加一些弹性(比如保留一半动量),那就更像是

mAcceleration = (0, -9.8, 0) + if isContacting then (0, 4.9, 0)