【本文参与征文活动】
浮力是生活中常见的力,使用得当往往让人眼前一亮得效果。
接下来我将会教大家如何实现它,但开始前,为了能够更好了解过程,我们需要复习一下物理知识。
浮力F=排出水的面积area*水得密度density;
通过以上公式可知我们主要是算出排出水的面积。也就是下方红色框区域的面积。
由于我没从cocos引擎中找出计算两个碰撞体重合面积的公式,所以我用了PolyonClipper库。为了找出对应的js库我也是找了很久,在这里我直接分享给大家。
clipper.zip (43.6 KB)
具体用法如下:
splution用来接收最后重合的地址;
self和other是两个碰撞体描点的世界坐标,也就是碰撞体points的世界坐标;
polytype我并没有详细了解,大致意思是一个主要一个次要的意思,也就是告诉程序计算谁覆盖谁。
cliptype代表不同的裁切效果,这个我们使用ctIntersection,取覆盖的部分。
值得注意的是,它所返回来的结果并不是vec2,所以这里我们需要转化一下。
得出形状后,接下面就是计算面积了,在这方面,我就从引擎中找出相应的公式。
首先先实例一个模具,new b2.PolygonShape()
然后用SetAsArray函数计算出形状,参数有两个,重合面积的世界坐标点和坐标数量SetAsArray(this._clipResuit,this._clipResuit.length)。这里值得注意的是,因为cocos所使用的计算单位是像素,而面积计算用的是米,所以每个坐标点都需要除以32。(cocos像素与米的比例就是32:1);
接下来就是算质量,质量需要一个质量信息来接收,然后通过水的密度算出质量。
最后重力的反向力乘以质量就是所得的浮力,但知道浮力还不够,还需要知道这个浮力的施力点,而这个施力点在重合形状的重心位置。
我们可以通过b2.PolygonShape.ComputeCentroid获得,有三个参数,重合面积的世界坐标点,坐标数量和最后接受结果的向量。
然后在undate里让物体的刚体一直施加力applyForce(buoyan,centroid);
以上就是浮力部分,觉得怎么样,是不是很简单,但还没结束哦,因为上面只讲了浮力,还缺了个阻力,没了阻力物体会一直上下波动不停,而这个阻力专业术语叫流体阻力。
以下就是这个阻力的公式:
是不是很熟悉,初中的物理知识,相信大家都会,那么接下来就是实现它。
p为流体密度,也就是水的密度,这个不用多说。
v是速度差,一般我们的水是不动的,所以基本上算的是物体的速度。
c为阻力系数,通常小于1,我通常直接用1计算。
a是阻力的受力面积,因为我们用的是2d,所以应该叫受力边长,而这个边长是作用边的投影。
好了,大致都了解,接下来就是敲代码实现它。
首先遍历每个重合面积的世界坐标点,让它和下一个坐标点计算它们的中间点,也就是每个边的中心点,还有相减算出每个边的向量。
接下来就是算碰撞时的相对速度,这个cocos有提供api,body.getLinearVelocityFromWorldPoint()
所需要的参数坐标点就是中间坐标
水和物体都要算,然后算出它的速度差,但一般水是不动的,所以结果跟物体计算相对速度是一样的。
因为我们计算用的是像素,所以得出速度差后需要除以32,然后v就是速度差的长度。let v=velDiff.mag(),之后拿速度差的方向,用于之后的计算。velDiff.normalize();
因为阻力的作用只有跟物体的速度方向跟速度差方向一致才有作用,所以要算出是否是同方向,所以通过右侧法向量计算,算出受力边的法向量。
可能有些初学者不懂右侧法向量,我把公式发出来:
两个参数,num我们填-1,vec就是边的向量,也就是edge。
得出法向量后用点乘,乘以速度差的方向。当结果大于0时,代表方向相同,才可以进行接下来的计算。
计算受力面积,也就是受力边,用边向量叉乘速度差的单位向量得出边向量的投影,也就是受力边。
let a=Math.abs(edge.cross(diff))/32;
除以32是为了像素转成单位米。
那么这个阻力也就是let fd=0.5vvadensity
方向为速度差单位向量的反方向。
所以最后结果为 let force=diff.mul(fd)
也是利用applyForce一直施加力,世界坐标点为边的中间坐标。
终于写完了,因为原稿我用的是b2box的向量,所以有些不方便展示,但为了让大家更好的理解,我会把原稿展示下来,只展示阻力那一部分,浮力太简单就不展示了。原稿只是为了实现,所以没有整理,请大家谅解一下。