9

这个问题有一个大问题和一个小问题。我相信我在研究中的任何一个问题上都是对的,但不是两者兼而有之。

TotalForce对于我的物理循环,我做的第一件事是对刚体对象施加重力。TotalForce然后我使用 my和 my来检查碰撞Velocity。MyTotalForce会在每个物理循环之后重置(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上面的计算总是至少有一个 .y 为 -9.8(我的重力)。当朝向法线为 (0, 1, 0) 的平面移动时,将产生denom-9.8 的 ay。

我的问题是

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;
    }
}
4

6 回答 6

1
  1. 您应该使用if(fabs(dist) < 0.0001f) { /* collided */ }This is 来计算浮点精度。在大多数角度或接触中,您肯定不会得到精确的 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除非一切都有单位质量,否则你不能盲目地做。

编辑

  1. 要在 3D 空间中完全定义一个平面,您的平面结构应该存储一个平面法向量和平面上的一个点。http://en.wikipedia.org/wiki/Plane_(geometry ) 。

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);

A 在任何时候,球体上的力可以在任何方向上,与行进方向完全无关。所以denom本质上是计算平面方向上的力的大小,但没有告诉你球是否会撞到平面上。例如,重力是向下的,但球可以具有向上的速度并撞击上方的平面。有了这个,你需要Vec3Dot(plane.normal, velocity)改为。

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

egs = 0.5 * (u + v) * t;给出未来时间 t 之后的位移。将该位移与距平面的距离进行比较,您将得到球体是否会撞击平面。再说一次,我建议您先阅读http://en.wikipedia.org/wiki/Linear_motion和简单的东西,然后再阅读http://en.wikipedia.org/wiki/Kinematics

还有另一种方法,如果您期望或假设没有其他力作用在球体上,那么您进行射线/平面碰撞测试以找到它将撞击平面的时间 t,在这种情况下,请阅读http://en .wikipedia.org/wiki/Line-plane_intersection

于 2012-05-09T08:51:28.367 回答
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).

编辑:

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

dist = Vec3Dot(&spherePosition, &plane->GetNormal()) - sphereRadius;

这与您的方程式相同 if plane->d == sphereRadius。请注意,如果飞机不在原点,则使用:

D3DXVECTOR3 vecTemp(spherePosition - pointOnPlane);
dist = Vec3Dot(&vecTemp, &plane->GetNormal()) - sphereRadius;
于 2012-05-01T11:53:54.627 回答
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
于 2012-05-09T22:51:11.743 回答
0

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

1) 确保您的 sim 在没有重力的情况下工作。球必须穿过空间并与有角度的无摩擦表面发生非弹性(或部分弹性)碰撞。

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

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

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

5) 尝试在重力作用下沿水平表面滑动球。如果一切都做对了,它的角速度会逐渐增加,线速度会逐渐减小,直到变成一个滚动。尝试给球一些初始旋转(“draw”、“follow”或“english”)。

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

如果你走到这一步,你将拥有一个非常逼真的模拟游戏。不要试图跳过任何步骤,你只会让自己头疼。

于 2012-05-10T13:30:29.717 回答
0

我建议在那之后看看 erin cato 的文章(Box2D 的作者)和 Glenn fiedler 的文章。重力是一种强大的加速度,会产生强大的力。由于浮动不精确、可变时间步长和欧拉积分,很容易很快出现错误的模拟。球体在平面表面重新定位,以防球体开始隐藏自己通过平面是强制性的,我注意到只有当球体的速度与平面法线相反时才这样做更好(这可以比较在 3D 渲染中进行面剔除:不考虑背面平面)。

此外,大多数物理引擎停止对空闲物体的模拟,并且大多数游戏在移动时从不考虑重力,只有在下落时才会考虑。他们使用“导航网格”和自定义系统,只要他们确定模拟对象坚持其“地面”即可。

我不知道那里有完美的物理模拟器,总会有集成爆炸,错过碰撞(寻找“扫描碰撞”)......这需要大量的经验微调。

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

还可以查看“每个计算机科学家都应该了解的浮点知识”

祝你好运,你进入了一个雷区,随机无法理解,数字计算机科学咬手指的领域:)

于 2012-05-14T10:03:37.450 回答
-1

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

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

您提到球体是刚体;您是否还将飞机建模为刚性?如果是这样,那么在接触和完全弹性碰撞的那一刻,您将拥有无限的点力,而没有明显的动量耗散。

力和速度不能相加(不兼容的单位);如果您只是尝试对运动学建模,则可以忽略质量并仅使用加速度和速度。

假设球体只是通过完全非弹性碰撞(没有反弹)落到水平面上,你可以这样做 [注意,我不太了解 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)
于 2012-05-03T17:56:22.993 回答