在〈牛頓運動定律〉中使用 p5.Vector
建立加速度向量,然後乘上物體的質量來取得力的向量,然而力有各種類型,為了區別,這邊先基於 F = ma
,針對力定義基礎類別:
class Force {
constructor(mass, acceleration) {
this.mass = mass;
this.acceleration = acceleration;
}
}
Body
的 applyForce
現在接受 Force
實例,取得加速度後來修改速度:
class Body {
constructor(coordinate, velocity, mass = 1) {
this.coordinate = coordinate;
this.velocity = velocity;
this.mass = mass;
}
applyForce(force) {
this.velocity.add(force.acceleration);
}
update() {
this.coordinate.add(this.velocity);
}
}
現在〈牛頓運動定律〉中的球反彈範例,可以修改如下:
在上面的實作中,將邊界的反彈抽取為 checkEdges
,邊界的反彈其實屬於物體碰撞的問題,碰撞涉及物體的形狀,這是個複雜的議題,目前暫且忽略,單純使用 checkEdges
搪塞一下,實作中在球超出邊界時,套用了反作用力,並馬上調整球的位置,這樣球就不會畫出邊界外。
談到力,最基本的就是重力了吧!以地球的重力而言,G = mg
,m
為質量,若不在意地球是橢圓的事實,一般都假設 g
為 9.8,可以定義如下:
class Gravity extends Force {
constructor(mass, g = createVector(0, 2)) {
super(mass, g);
}
}
咦?g
不是 9.8 嗎?那是在地球上,在模擬的世界中,g
要怎麼定義,要看你的世界怎麼定義,就這邊的文件來說,只要運動起來的效果像是在有重力的環境就可以了。
來為方才的範例套上重力:
咦?為什麼球不會停下來?現實世界中的球下落後,不是會慢慢停下來嗎?當然,這是因為目前模擬的世界中只有重力,現實世界中還會有空氣阻力、摩擦力等,它們的施力方向與物體的速度方向相反,因此球才會慢慢停下來。
來為模擬的世界加個空氣阻力,空氣是流體的一種,根據維基百科的阻力方程,流體的阻力大小是:
其中 v 是速度,A 是參考面積,ρ 為流體密度、CD 為阻力係數,如果你只模擬一種流體,例如空氣,這兩個的值基本上就是取個合理的常數就可以了,也就是在簡單的模擬時,可以簡化為 c * v2 * A。
那麼要怎麼用來建立 Force
實例呢?A 是參考面積,ρ 為流體密度,兩者的乘積隱含著質量,這邊就簡單地以 A 來表示質量,而因為 F = ma
,在計算出阻力大小時,除以 A 就可以當作加速度,方向會與指定的速度相反,因此可以定義出 Drag
:
class Drag extends Force {
constructor(area, velocity, c = 2) {
const acceleration =
velocity.copy()
.mult(-1)
.normalize()
.mult(pow(velocity.mag() * c, 2) / area);
super(area, acceleration);
}
}
到這邊可以看到,對於力的模擬,視你的世界如何定義,多半都會有一定程度的簡化,當然,如果你有多種流體,在公式的部份就可以更講究。
來試著為方才的範例加上空氣阻力:
因為目前沒有地面摩擦力,範例中只有在球離地面有一定高度時套用重力、空氣阻力等動作,以免球在穩定之前震盪過久而看來不自然。