相序和其他一般信息
我想要构建我自己的2D迷你物理引擎,这将包括(现在)僵硬的身体和约束(关节,接触,弹簧......)。 我试图找出阶段的正确顺序,当我开始一个时间步,当一般阶段是:宽相,窄阶段(碰撞检测和联系生成),分辨率(约束求解)和整合 - 希望你能告诉我正确的顺序。
我也有关于每个阶段的一般问题:
窄阶段 - 当我发现碰撞时,我是否需要在发现碰撞后分开物体,或者在解析阶段对其速度施加冲击?
如果我对某些物体使用CCD(连续碰撞检测),如果我发现快速移动物体A与其他物体B发生碰撞(最早出现在物体A上),并且发现物体B会在物体与物体A碰撞之前与其他物体C发生碰撞,我是否需要返回物体A并在发现第一次碰撞后寻找其他碰撞?
如果我在我的引擎中使用了联系人求解器(在约束求解器中,我将为每次碰撞产生一个Contact约束条件,然后我将在解决方案阶段解决所有问题,否则我将在我他们发现他们在狭窄的阶段?
分辨率 - 在解析阶段使用constaints Solver可以吗? 以及如果通过解决一个约束我会解决其他约束或者制定更多约束呢? (如果解决其他限制或做更多的事情,我是否需要在每个约束求解后检查?
互动 - 这是我收集我收集的所有信息(冲动,力量)并将他们整合为速度的一部分,而不是用我选择的整合方法进行定位?
辛等欧勒集成对我的物理引擎来说足够了吗?
我也在许多物理引擎中看到过,比如box2D,他们使用迭代,让我选择这里的频率(注意我可以改变迭代计数(10)和频率(60.0 Hz)):
这些变量的含义是什么? 迭代是我记得每次帧中物理更新(上述所有阶段)的次数,或者它只是回忆一个阶段,就像狭窄的阶段或类似的东西? 而频率变量让我选择在一秒钟内有多少帧? 纠正我,如果我worng请。
上面所有的问题都让我从学习中学到了东西,因为它的基础是东西,而且我没有找到一个能够在一个干净的海峡中解释这个东西的地方
非常感谢任何会阅读所有这些内容的人,特别是对于那些也会帮助我解决所有问题的人:)
你的阶段顺序并不那么重要。 是的,他们确实互相混淆,但任何失功能都可以通过调整常数来纠正。
入门
您应该在任何模拟代码之前从对象表示和可视化开始。 你需要能够形象化你的东西,这样你才能看到是否全部好
对象类
所以为所有需要支持的对象类型创建类并添加一些基本的接口函数(虚拟基类对未来的实现来说是个好主意),如下所示:
load(file,ini,stream...),save(file,ini,stream...)
draw(screen or render context),update(dt),bool colide(object)
等... 您将及时看到您需要添加的内容......现在它们将是空的(而不是您需要编码的绘图...)。 你也可以准备几个常见的物理变量(也是虚拟的),比如位置,速度,温度......
基类
它应该是你的引擎的封装。 它拥有所有对象/东西的列表。 与GUI /应用程序的主界面。 想要更深入地了解我现在看的意思,请点击这里在C ++中使用拖放编辑器,您可以将它用作起始点,只需将物理迭代/更新添加到它即可。 不要忘记将对象接口实现为这个主类,例如:
load(filename)
...加载所有的对象 draw(...)
...绘制所有对象... 模拟
只有在以上情况已经发挥作用的情况下才开始。 您需要考虑您想要达到的准确性和响应时间。 更快的对象/过程是您需要的更高仿真回路频率。 对于我的模拟,我通常每次迭代使用1-100 ms
。 对于非常快速的模拟,您可以每个定时器调用进行N
次迭代。 这是计数和频率的含义。 频率是计时器的呼叫速度,你的迭代循环被调用,计数( N
)是时间步长dt
除法,所以如果N=10
和f=60Hz
这意味着你每1/60
秒迭代10
次,所以它与N=1 f=600Hz
但至少在Windows定时器分辨率为1 ms
而不是非常精确的以太网。 所以100 Hz
以上的100 Hz
是不可靠的。 如果你想更精确的话,你可以通过PerformanceCounter
或RDTSC
来测量时间,或者你可以RDTSC
任何足够精确的时间API 。
您可以使用D'Lambert原理(简单积分)来模拟运动,其余部分用已知方程计算(不知道您想要模拟的所有内容)。 例如,看这里简单的质量点在3D中的重力模拟(C ++)
如果你的代码编写得很好,你可以并行化迭代循环,但是你必须记住它可以产生一些麻烦,比如双重碰撞反应等等
碰撞
SO / SE上有很多这方面的内容,所以只需要搜索它。 我认为你应该看看这里:模拟粒子碰撞以获得多重碰撞处理的想法。
在这里得到这个想法就是我的一个模拟仿真的例子
它使用了我在这里写的所有东西,另外它还使用了一个特殊的类来用于弹簧或关节等债券。 这是它的样子:
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
方的所有相关债券。 然后,在每次迭代期间,递归更新所有债券以匹配债券/碰撞条件,直到他们全部完成后,立即更新所有债券/仓位。
_computed flag
只是宣布这个债券是好的(条件得到满足)。 其余部分只是用于缓解递归堆/堆栈垃圾堆栈负担的临时变量
链接地址: http://www.djcxy.com/p/66167.html