我想建立自己的2D迷你物理引擎,包括(现在)僵硬的身体和约束(关节,接触,弹簧......)。 当我开始一个时间步时,我试图弄清楚阶段的正确顺序是什么, 当一般阶段是:宽相,窄相(碰撞检测和接触生成), 分辨率(约束求解器)和积分 - 希望你能告诉我正确的顺序。
我对每个阶段都有一般性问题:
狭窄阶段 - 当我发现碰撞时,我是否需要在发现碰撞后将尸体分开,或者只是在分辨阶段对它们的速度施加冲动?
如果我对某些身体使用CCD(连续碰撞检测),那么如果我发现快速移动的物体A与其他物体B发生碰撞(最早是物体A),那么我会发现在物体碰撞物体A之前,物体B将与其他物体C碰撞,在我找到他的第一次碰撞后,我是否需要回到物体A并寻找其他碰撞?
如果我在我的引擎中使用Contact Solver(在约束求解器中,我将为每次碰撞生成A Contact约束,然后我将解决所有分辨率阶段,或者我将解决每个联系人我发现它们处于狭窄的阶段之后呢?
分辨率 - 在解决阶段使用constaints Solver可以吗? 如果通过解决一个约束我将解决其他约束或制定更多约束怎么办? (如果解决了其他约束或者做出更多限制,我是否需要在解决每个约束后进行检查?
Intefration - 这是我收集所有信息(冲动,力......)并将它们融入速度而不是用我选择的积分方法定位的部分?
对于我的物理引擎来说,辛厄勒整合就足够了吗?
我在许多物理引擎中看到过,比如box2D,他们使用迭代让我选择像这里的频率(注意我可以改变迭代次数(10)和频率(60.0 Hz)):{{0} }
这些变量意味着什么?迭代是我在每一帧中回忆起物理更新(上面的所有阶段)的次数,或者它只是回想起一个阶段,如狭窄阶段或类似的东西? 而频率变量让我选择一秒钟内会有多少帧? 如果我想请你纠正我。
上面的所有问题让我从学习堆栈中学习,因为它的基础知识,而且我没有找到一个可以在干净利落的前沿中解释这些东西的地方
非常感谢能够阅读所有内容的人,尤其是那些也会帮助我解决所有问题的人:)
答案 0 :(得分:-1)
阶段的顺序并不那么重要。是的,他们互相混乱,但任何失效都可以通过调整常数来纠正......
入门
在任何模拟代码之前,您应该从对象表示和可视化开始。您需要能够可视化您的东西,以便您可以看到是否一切正常
对象类
所以为你想要支持的所有对象类型创建类,并添加一些基本的接口函数(虚拟基类对于将来的实现是一个好主意),如:
load(file,ini,stream...),save(file,ini,stream...)
draw(screen or render context),update(dt),bool colide(object)
等等...... 及时你会看到你需要添加的内容......现在它们将是空的(而不是你需要编写的绘图......)。您还可以准备一些常见的物理变量(也称为虚拟变量),如位置,速度,温度......
基类
它应该是你的引擎的封装。它包含所有对象/东西的列表。主界面有 GUI / App。为了更深入地了解我的意思,现在看Drag&Drop editor in C++,你可以将它作为起点,只需添加物理迭代/更新即可。不要忘记将对象接口实现到这个主类中,如:
load(filename)
...加载所有对象draw(...)
...绘制所有对象...... <强>模拟强>
仅在上述内容已经有效的情况下才开始。您需要考虑您想要达到的准确性和响应时间。对象/进程越快,您需要的仿真循环频率就越高。对于我的模拟,我通常每次迭代使用1-100 ms
。对于非常快速的模拟,您可以在每次定时调用时进行N
次迭代。这就是计数和频率的含义。如果频率是定时器调用速度,则调用迭代循环并且count(N
)是时间步dt
除法,因此如果N=10
和f=60Hz
则表示您迭代{{}每10
秒{1}}次,因此它与1/60
相同,但至少在Windows计时器分辨率为N=1 f=600Hz
且不是非常精确的以太。因此,1 ms
以上的频率是不可靠的。如果您想要更加精确,可以通过100 Hz
或PerformanceCounter
或者您可以使用的任何足够精确的 API 来衡量时间。
您可以使用 D&#39; Alembert 原则(简单积分)来模拟其余运动的已知方程运动(不知道您要模拟的所有内容)。例如,请查看simple mass points gravity simulation in 3D (C++)
如果您的代码写得很好,您可以并行化迭代循环,但您必须记住,它可以创建一些麻烦,如双重碰撞反应等
<强>冲突强>
SO / SE 上有很多关于此的内容,所以只需搜索它。我想你应该看一下:simulation of particle collisions以了解多次碰撞处理。
这里有想法是我的模拟之一的样子
它使用我在这里写的所有东西,另外它使用一个特殊的类来表示弹簧或关节等键。这就是它的样子:
RDTSC
我可以用这背后的东西覆盖整本书,但是我太懒了,而且这个网站也不适合这个,所以匆忙重要的是每个债券都有它的开始和结束对象指针(struct _bond
{
physics_point *pnt0,*pnt1;
double l0,l1;
int _beg0,_end0;
int _beg1,_end1;
List<int> depend0,depend1;
int _computed;
_bond() {}
_bond(_bond& a) { *this=a; }
~_bond() {}
_bond* operator = (const _bond *a) { *this=*a; return this; }
//_bond* operator = (const _bond &a) { ...copy... return this; }
};
List<_bond> bnds;
)并在计算期间填充依赖项列表(pnt0,pnt1
)。
depend0,depend1
来自depend0
方pnt0
来自depend1
方的所有后续债券。然后在每次迭代期间递归更新所有债券以匹配债券/碰撞条件,只有当它们都这样做时,才会立即为所有债券更新头寸。
pnt1
只是宣布这种关系是正常的(符合条件)。其余的只是临时变量,以减轻递归堆/堆栈废弃的负担