首先,您会认为,当有那么多游戏引擎时,为什么要实现自己的游戏引擎。 可能是您不喜欢其中的任何一个,或者只是考虑开始游戏并实现自己的引擎可能是个好主意,那么请不要这样做,并且本文可能不适合您。 但是,如果您(像我一样)想要学习物理引擎的精髓,这可能是一个很好的练习。
这是最终结果的演示,您可以在我的GitHub上找到代码。
- Nomad游戏引擎:第4.4部分— SoA实现
- 约克郡河谷:大型环境建筑
- 我如何在22天内准备并通过Unity认证的开发人员考试(第一部分)
- 茶歇:弗雷德·杨(Fred Yang),另一半
- 全球游戏果酱,ISU游戏开发俱乐部2019

在本文中,我将不提供任何代码段,因为您可以在上面提供的GitHub链接中找到该代码段,但更多地说明了如何将它们组合在一起。
首先,我们将从一些基本假设开始:
1.所有物体都是刚性物体,因此它们在撞击时不会变形。
2.刚体的碰撞形状为凸形,非凸形应以凸形为边界进行碰撞检测。
2.物理模拟以恒定的帧速率(通常为60 fps)运行。
3.无论我要描述的是什么,都已针对2D实现,但大多数总体思路也适用于3D。 另外,我所描述的过程是我已经实现的过程,不同的游戏引擎倾向于使用不同的模型或优化。
总览
为了使物理引擎正常运行,它应该执行以下三个基本操作:
1.它应该能够预测在任何给定时间点的平移特性,例如位置,速度,加速度和旋转特性,例如方向,角速度,扭矩。
2.它应该能够检测到正在碰撞的物体。
3.它应该能够正确解决由于这些碰撞而产生的脉冲。
计算属性
为了计算属性,我们基本上使用牛顿运动定律。

和,

哪里,
F =施加的力
v =速度
m =身体质量
x =位置
dt =自上次执行以来经过的时间(如果为60 fps,则为1 \ 60)
为了解决游戏的这些微分方程,我们广泛地遇到了以下三个积分器:
- 显式欧拉法

这是求解一阶微分方程的最基本方法。 它只是以较小的间隔添加曲线值。

2.内隐(象征性)欧拉
区别在于我们使用右Reimann和规则而不是左和规则来解决问题。 对于不打算阅读Wikipedia的人,雷曼和用于通过将区域分解成小形状来计算区域。 在Euler方法中,区域是矩形。
例如,要获取时间t处的位置,我们将需要时间t + 1处的速度,因此,不是从零速度开始,而是先计算速度,然后计算位置。

3. Runge-Kutta(RK)四阶方法
我不会对此做太多解释,但是它比欧拉方法具有更好的准确性,但是计算量大。 实际上,欧拉方法只是RK的特例。
隐式Euler比显式Euler给我们更准确的结果,并且更接近RK4。 这是对这三种方法的更详细的比较,并解释了为什么隐式Euler对于游戏引擎来说还可以。
因此,使用隐式欧拉,我们将得到:
v + =(1 / m * F)* dt
x + = v * dt
如果我们考虑轮换,
ω+ =(1 / I *τ)* dt
θ+ =ω* dt
哪里,
I =惯性矩
θ=方向
ω=角速度
τ=扭矩
计算碰撞的刚体对
在每个循环中,发动机都必须知道哪对刚体发生碰撞,这样才能解决碰撞,并且车身不会相互通过。
天真地如果我们对所有N个实体进行迭代,我们将需要检查` N *(N-1)/ 2`可能的组合很多。 假设N = 100,我们将需要检查4950个身体对,它们可能占用的时间超过帧持续时间(即,仅1 / 60s或16ms)。 在大多数游戏中,存在的对象比在任何给定时间点的对象都要多。
因此,首先我们使用称为BroadPhase计算的过程来缩小要检查的冲突数。 其目的是生成可能会碰撞的一对刚体列表。
*它是通过空间划分算法实现的。
*基本思想是将整个区域划分为网格(以3D的体积表示),并将每个刚体分配给网格中的某个位置。
*现在,将主体添加到它重叠的每个单元格中,因此最后,我们遍历网格并制作一对重叠的主体。 对象存储在诸如kd树之类的数据结构中 或BSP树以提高效率。 通常,它将迭代时间从O(N²)减少到对数时间。
这是一个不错的博客,它更详细地解释了广泛阶段的实现,例如网格大小的选择等。我的演示仅针对较小范围,因此我没有实现这一部分。
然后,我们进行NarrowPhase计算,即实际上确定哪些物体正在碰撞。 Narrowphase的目的是为每对碰撞体计算三件事:
1. 碰撞法线 :它们碰撞的物体的方向
2. 穿透深度 :多少个物体相互重叠
3. 接触点 :重叠的接触点
可以使用分离轴定理来实现。
基本原理是: 如果两个物体不碰撞,则它们的投影将在某个轴上不重叠。
对于一般的凸多边形,可能要检查很多轴,但为简化起见,我将对撞机的形状定为圆形和与轴对齐的边界框(AABB),即仅是一个矩形。 现在进行碰撞检查,我们将实现四种组合(不考虑轮换):
* 圈圈 :
为此,测试很容易-圆之间的距离应小于半径的总和
//重叠的距离必须小于
isColliding = distance(circle1,circle2)<= circle1.radius + circle2.radius
* Circle-AABB
我们将首先在AABB和圆之间找到AABB上的最近接触点。
positionDiff = AABB.pos — circle.pos
//在x轴上取接近点的边界或差异
最近点x =钳位(-AABB.width / 2,AABB.width / 2,positionDiff.x)
// y轴相同
最近点=钳位(-AABB.height / 2,AABB.height / 2,positionDiff.y)
//矩形上最近的点到圆心的距离
//小于圆的半径
isColliding = distance(circle1,最近点)<= circle.radius
* AABB-圆
与上述相同
* AABB-AABB
positionDiff = AABB2.pos — AABB1.pos
//查找重叠值
xOverlap = AABB1.width / 2 + AABB2.width / 2 — abs(positionDiff.x)// x轴重叠
yOverlap = AABB1.height / 2 + AABB2.height / 2 — abs(positionDiff.y)// y轴重叠
//对于要碰撞的物体,两个轴必须重叠
isColliding = xOverlap> 0 && yOverlap> 0
对于凸多边形,其实现将更加复杂,GDC Vault中有一段很好的视频介绍了该过程。 另外,您也可以同样通过Box2D代码。 这是我的实施清单中的下一个清单。
冲动分辨率
冲动分辨率意味着当物体碰撞时,我们需要阻止它们在彼此内部移动。 首先想到的是,我们可以向它们施加力,但是力不会立即改变速度。 为此,我们直接改变物体的速度,或者说我们施加了一个脉冲(即您可以将其视为几乎在零时间内的巨大力)。
除了动态特性之外,每个刚体还具有一些物理特性,我们将其归类为Material 。
– 质量(m) :体重多少
– 恢复系数(e) :表面有多粗糙
– 静摩擦系数(μₛ) :静止物体的摩擦系数
– 动摩擦系数(μd) :运动物体的摩擦系数
对于一对物体,我们计算等效系数。
*等效恢复系数为` e = min(e1,e2) `。
*等效摩擦系数为:

在接触期间,我们将考虑作用在它们上的两个脉冲:
1.法向力(垂直于接触平面)
2.摩擦力(与接触平面相切)
冲量(j)使用动量守恒和牛顿复原定律计算 。

正常冲动
让我们定义一些变量,
vA,uA =物体A的最终速度和初始速度
vB,uB = B体的最终速度和初始速度
jA = A的动量变化
jB = B的动量变化
n =碰撞法线方向上的法线向量(nn = 1)
根据牛顿的赔偿法,
e =相对最终速度/相对初始速度
由于动量是守恒的,因此,主体A的动量变化将与主体B的动量变化相反,即jA。 n = -jB。 n 。 因为我们只对法线方向感兴趣,所以将点积与n一起使用。

因此,我们的目的是根据初始速度来计算j ,因为我们不知道碰撞后物体的最终速度。 我们将重新排列以下术语:

哇,这是很多方程式,但是请放心,只是在重新排列术语以获取我们所需要的东西时,您就可以自己解决问题。
如果要考虑旋转运动,这很容易,就像线性动量一样,角动量也得以保留。 让我们定义更多的术语,以便进一步阐明方程式。
L =角动量
p =线性动量
I =关于质心(COM)的惯性矩
r =垂直于COM的半径矢量
一些基本定义

考虑线性动量的变化时,还应添加角动量,从而得出最终方程:

摩擦冲动
摩擦脉冲垂直于碰撞法线应用。

摩擦冲量可以通过用相对速度的切线大小和相反方向的正切值替代法向矢量来计算。
将n替换为t:

现在我们只需要计算t即可 。 为此,我们将获得相对速度的法向分量,并从总相对速度中减去以获得切向分量(矢量数学)。 使用负号是因为摩擦始终与运动方向相反。
t =-[Vᴬᴮ−(Vᴬᴮ⋅n)∗ n]
但是请耐心等待,摩擦对静态和动态对象的作用不同,这就是为什么两个摩擦系数不同的原因。 可以使用库仑定律确定如何使用它们。
jf <=μ* jn
让我们理解这个定义,如果我们求解的jf (代表摩擦力)小于静态系数(μl)乘以法向力( jn ),那么我们可以将jf大小用作摩擦力。 如果不是,则必须使用法向力乘以动态系数(μd)。
如果(abs(jf)<μₛjn){
// 不用找了
}其他{
jf =μd* jn
}
希望您现在可以开始理解编写游戏引擎的基础,并且再次在这里可以找到代码。 我试图包括实现工作演示绝对必需的大多数主题。 现在,您可以阅读有关特定主题的文章,以进一步了解它们以及它对您的实施有何好处。
图片和其他积分
*维基百科
* ResearchGate
* GameDevTuts