我们选取一个开源的2D力学引擎,进行力学实验的开发,在引擎的选取和初期开发过程中遇到以下问题。4. 质量、密度、体积之间的关系与真实世界不一致。目前,普遍认为最好的2D力学引擎是Box2D,但是,有一个力学引擎nape(https://github.com/deltaluca/nape),性能要高于Box2d,nape在flash界很出名,其它领域的开发者可能不知道。nape的性能平均比Box2D快3到6倍,内存节省一半[1]。(注:nape作者对比的是flash版的,而且是2012年的数据,Box2D一直在维护,nape已经没有更新了)Box2D适用于0.1m-10m之间的物体[2]。在实验中,我们的最小物体在cm量级,比如钩码是3cm,砝码更小,最小的可能小于1cm。实测,nape确实比Box2D性能要好,功能要强大(从API文档就能看出来)。其中Box2D我们使用的是box2dweb和box2d-ts。box2dweb:https://roombox.xdf.cn/blog/problems-in-the-development-of-mechanics-experiment-2d/git@github.com:hecht-software/box2dweb.gitbox2dts:https://github.com/flyover/box2d.tsnape的API对js开发者友好。Box2D是c++开发的,API和js的习惯差别很大,nape是面向As3.0开发者的,AS3.0和js都是基于ECMAScript标准的,API几乎完全一样。测试场景1:重力加速度9.8m/s²,1m对应5000px,物体尺寸1cmBox2D默认情况下,刚体稳定之后,刚体和地面之间的间隙太大。通过调整settings中的参数可以解决。调整参数之后,大尺寸物体和小尺寸物体的碰撞,很容易导致不收敛。nape默认支持,收敛性也比Box2D要好。弹簧测力计至少需要两个约束(弹簧+杆),一个实现弹簧的效果,一个把弹簧约束到一条线上(弹簧只能沿弹簧测力计方向运动)的一段范围内(弹簧不能无限制的拉伸和压缩)。nape中我们可以使用LineJoint和DistanceJoint来实现,Box2D中只有DistanceJoint,没有LineJoint。box2dweb中的DistanceJoint只有一个距离相关的参数,不能限制距离范围,新版的可以。Box2D中的滑轮,只能绑定到两个刚体,也就是说两根绳子的一端绑定刚体,另一端只能绑定到世界坐标,相当于固定死的;nape中的滑轮,可以绑定到4个刚体,两根绳子的任何一端都可以绑定一个刚体。Box2D中的滑轮不能满足需求,比如,如何实现一个动滑轮。nape中的滑轮能够满足需求。box2dweb中有浮力,作为controller实现的,官网最新版的代码中是没有浮力的,浮力的实现,需要先计算几何图形的相交面积,然后计算浮力,给刚体一个冲量。要是用最新版的Box2D,需要自己把浮力相关的代码移植过去。物理教学中,老师需要讲解动摩擦、静摩擦、滚动摩擦,Box2D中只有一个摩擦因数,nape中动摩擦、静摩擦、滚动摩擦都有。使用Box2D需要自己去实现最大静摩擦的效果,比如根据速度动态改变摩擦因数。力学引擎都是使用的凸多边形,nape中提供了凹多边形分解的相关API,以及其它一些几何算法,虽然可以自己实现,但是引擎提供了,还是方便一些。我们通过约束把两个钩子连接到一起,钩子可以钩在一起,可以解开。在Box2D中,我们要频繁的创建约束,删除约束。在nape中,我们只需要设置约束的active参数即可。频繁创建销毁,会增加开发难度,增加GC。按照需求,我们要设置钩子和轻绳的质量为0,但是刚体的质量会影响约束的计算,在约束计算时,我们又希望钩子或者轻绳的质量大一些,两者是矛盾的。nape中可以设置gravMass为0,mass为实际质量。我们选择了性能最好的力学引擎。但是计算精度依然不够。很多人会有疑问:基于力学引擎做的游戏,效果很好,实验中的东西也不复杂,怎么就不够用了呢?游戏中,要么重力加速度比较小,要么物体尺寸比较大。比如,愤怒的小鸟。假设小鸟身高是10cm,可以测出1.5秒,自由落体,下落1.1米,可以算出g = 2 * s / (t * t) ≈ 0.98m/s²。重力加速度是真实情况的1/10。其它小游戏中也都类似。游戏中的物体尺寸一般都比较大,比如,人的身高将近2米,箱子的尺寸大约1米,汽车在3-5米。等等。一个3米的汽车,重力加速度9.8m/s²,和一个0.3米的汽车,重力加速度0.98m/s²是一样的。游戏物理引擎中往往用了大量的精力来优化碰撞检测,实验中不会同时存在大量刚体,对于大量物体碰撞检测的性能要求不高;实验中,对于约束的要求比较高,比如一个弹簧测力计挂一个钩码,再用鼠标拖动弹簧测力计;比如两个弹簧测力计连接一个弹簧,弹簧固定在墙上;刚体没几个,但是约束一大堆。多个约束同时作用的情况下,很容易出现不收敛的情况。比如用量程是1N的弹簧测力计测物体的重力,显示数值要到0.01N,如果不显示数值,可能已经稳定了,人眼完全看不出来振荡,但是数据确实还在振荡,一旦显示读数,问题就暴露出来了。还有弹簧的震动,位移曲线、速度曲线、加速度曲线,应该是正余弦,人眼是完全看不出来的,一旦显示图像,数据有一点振荡,就全暴露出来了。等等。多个约束同时存在时,可能和预期的效果不一致。比如:
二维力学引擎中,质量=密度面积。而真实世界中,质量=密度体积。虽然二维世界的物理规律也是完备的,但是学生学习过程中还是以3维世界为准的。我们也很难通过扩展,让二维力学引擎按照3维的规律来计算质量、密度、体积之间的关系。
中学书上通常只说一个物体的摩擦因数是多少,但是实际上摩擦是两个物体之间的相互作用,取决于两个物体。力学引擎中,一般取两个相互接触的物体的摩擦因数的乘积的平方根作为两个物体间的摩擦因数。因此,把一个物体的摩擦因数从0.2改为0.4,摩擦力并不会变成两倍。
最大静摩擦大于动摩擦,这就导致了如下图所示的情况,拉力大于最大静摩擦,木块加速,变成动摩擦,摩擦力变小,木块先加速再减速,循环,就产生了振荡的效果。浮力的大小等于物体排开液体的重力。物体放入液体中,为了显示液面升高,我们就需要增大液体对应的Shape,然而Shape的增大就导致了液体质量的增加,质量凭空增加了,显然是不对的,要保持质量不变,液面还要升高,实现起来也很困难。
1. 对于精度不够的问题(或者振荡、不收敛的问题),增加引擎的帧率可以解决,但是目前增加帧率计算机性能不够,我们不能改重力加速度,我们只能改变时间,可以把时间调慢(时间步长调小)。可以看出,我们是在保证重力加速度不变的前提下,让力学引擎一帧对应的加速度尽量小。2. 对于约束效果的问题,可能自己去扩展约束能够解决,实现起来比较困难。3. 质量、密度、体积的问题没有想到办法,使用3D力学引擎就没有这个问题。
4. 摩擦、滑动的问题,nape的Config中有一个staticFrictionThreshold参数,设置成0,应该可以解决。5. 浮力、排开液体的体积的问题,使用复杂形状实现起来太复杂,不现实,nape中可以使用gravMass来解决,也可以通过配重(或者添加冲量)来抵消掉增加的质量。
http://hanyeah.com/mirror/nape/help/manual.htmlhttps://box2d.org/documentation/