如何计算球体和平面之间的碰撞响应?


9

我正在尝试创建一个简单的3D游戏,并且需要将玩家限制在游戏世界的范围内。当玩家撞到世界的两边时,我希望玩家的飞船稍微弹起。

实际上,我试图将玩家困在一个盒子里,阻止他们从侧面逃脱……

我设法将游戏世界的界限定义为一组平面,并具有法线和距原点的距离。播放器有一个球形的边界球,通过跟踪此网站http://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php,我设法检测到了碰撞。

现在,我无法完全确定检测到碰撞时该怎么办。我能管理的最好的办法是让玩家卡在飞机上,直行通过飞机,或者以非常快的速度反复跳下飞机。

常识告诉我,我需要使用其法线来计算平面上的反射角并将其应用于玩家的速度,但是我想我首先需要查看玩家是否已通过平面,这是我无法做到的锻炼。

Answers:


4

您将不得不对您的对象施加脉冲,这是其速度的立即变化。在现实世界中,很强的力将在很短的时间内施加到对象上,从而反转其加速度并导致其速度发生变化。但是,由于我们在一个离散的世界中工作,因此我们不得不作弊以模拟这种突然的方向变化。对于球体和飞机,这非常简单。最基本的碰撞响应是反映球体绕平面法线的速度,然后得到球体的新速度。伪代码如下所示:

reflected = 2 * plane.normal * (plane.normal * sphere.velocity)
sphere.velocity -= reflected

从那里,您可以添加一些阻尼(乘以某个系数,例如0.9)以解决因热量或摩擦而损失的能量。如果要涉及角速度(也许您的球正在旋转),则方程式将变得更加复杂。

有关更多信息,请参考Chris Hecker的“ 刚体动力学”文章。如果您以前从未听说过Chris Hecker,那么他就以游戏物理学以及他在《孢子》中角色角色生成和动画方面的工作而闻名。


4
从本质上讲,这是正确的方法,但是计算影响时间(TOI)可使帧率波动或下降时更加准确。根据当前速度知道撞击发生的时间,可以帮助您计算撞击时间,并据此可以在撞击时刻将球体移回其位置并从那里调整速度。从撞击点调整位置和速度后,在撞击时,您可以沿新速度移动减去的总时间到达TOI。
Nic Foster 2012年

好的,这似乎可以正常工作,但是有点...奇怪。我认为我可能在代码错误的地方执行了此操作。我应该遍历所有对象并在它们移动之前测试它们是否会发生碰撞(基于它们下一帧的位置),或者先将它们移动然后再进行碰撞测试?
Piku 2012年

@Piku,不,请不要检测它们是否会发生碰撞。如果发生碰撞,请记住,这两个对象现在很有可能重叠在一起,远远超出实际发生碰撞的位置。本质上,您需要弄清楚发生碰撞的位置,就好像帧速率是无限的(不是),然后将对象移回最初发生碰撞的位置。如果不这样分离对象,您将不断对同一碰撞做出反应,并且该对象将被卡住。
乔纳森·迪金森

@Piku,然后我们确定过去发生碰撞的时间(称为TOI /碰撞时间)。一旦有了,我们就可以使用物体的速度将其向后移动(distance = speed * time通常为避免误差而留出很小的距离),然后将其速度更新为碰撞结果。
乔纳森·迪金森

@Piku我们也无法弄清楚下一帧的位置(我从未亲身经历过),但是,通常,我们会进行碰撞检测和响应:在计算出该帧的新位置之后,但在此之前我们将新位置应用于此框架。
乔纳森·狄金森

1

F = ma或a = F / m。计算球体和平面之间的碰撞点。通常是球体中心-法线*半径。如果需要更高的精度,请计算球体穿透平面的距离,然后调整计算。当然,这在很大程度上是可选的,除非您想要真正精确的物理。现在计算沿法线的相对速度。对于静态平面,这是:Vball DotN。然后将VballDotN乘以-1,然后乘以质量。在现阶段的物理学中,您还可以将其乘以恢复系数(反弹系数)。将此标量乘以N,即可得到所需的力。

调整Vball时,再次将力除以质量,便得到最终的加速度,因此只需将其加到速度上,便得到最终的碰撞后速度。

vec3 Vrel = Ball.getVelocity();
float vDotN = Vrel.Dot(CollisionNormal);
vec3 F = -(1.0f+Ball.getRestitution())*vDotN;
F*=Ball.getMass();
Ball.accelerate(F/Ball.getMass());

该方法对于碰撞响应的公式是准确的。如果您想要更高的准确性,则需要考虑摩擦,这将导致球旋转,但是我不知道您是否希望在比赛中这么做。如果这样做,这是计算切向力的方式:

vec3 Ft = -(Ball.getvelocity()+(vDotN*CollisionNormal));
Ft*=Ball.getKineticFriction()+Wall.getKineticFriction(); //you could fudge these numbers
Ft*=Ball.getMass();
vec3 vec2Centre = Ball.getPosition()-ContactPoint;
vec3 Torque = cross(vec2Centre,Ft);
Ball.AngularAccelerate(Torque/Ball.getMomentofInertia(glm::normalize(Torque)));

确保在应用任何线性效应之前计算Ft,否则摩擦将不准确。


第3行不应该是:vec3 F = -CollisionNormal * (1.0f+Ball.getRestitution())*vDotN;吗?
Shital Shah

确实是的,我错过了那部分。感谢您指出。
伊恩·杨

0

我建议先计算您到飞机的距离;然后当距离<=半径时进行碰撞反应。

然后,您可以更改此值以计算距离,如果距离小于半径(表示对象重叠),则移动球的位置,然后进行碰撞反应。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.