用代码描述自然系统-构造恒星系统
前面的文章中模拟了万有引力,通过万有引力进一步模拟了自由落体,但自由落体并不是万有引力最好的表现,本篇博文通过万有引力模拟一个恒星系统和一个行星系统,这个系统将完全遵守万有引力法则。
构建恒星系统
由于显示范围有限,需要对几个常数进行修改,修改如下:
// 万有引力常数
const G = 6.67259E-9;
// 地球质量
const M = 5.965E5;
// 太阳质量
const M_SUN = 2E10;
然后定义一个 Planet 类,用来实例化行星。
class Planet {
constructor(name = '', position, mass = 1, diameter = 20) {
this.name = name;
this.position = position;
this.velocity = createVector(0, 0);
this.mass = mass;
this.diameter = diameter;
this.filledColor = color(random(10, 100));
this.beforeDisplay = null;
}
addForce(force) {
const a = force.div(this.mass);
this.velocity.add(a);
}
setColor(color) {
this.filledColor = color;
}
setVelocity(vel) {
this.velocity = vel;
}
reverseVelocity(attenuation = 1) {
this.velocity.mult(-1 * attenuation);
}
update() {
this.position.add(this.velocity);
}
setBeforeDisplay(fun) {
this.beforeDisplay = fun;
}
display() {
this.beforeDisplay && this.beforeDisplay(this);
fill(this.filledColor);
noStroke();
ellipse(this.position.x, this.position.y, this.diameter);
text(this.name, this.position.x + this.diameter / 2 + 6, this.position.y + 6);
}
}
接下来为系统中加入恒星,作为太阳,太阳的质量在这个有限系统中最大,将其绘制在系统的中心。
function setup() {
// ...
// sun
const sun = new Planet('Sun', createVector(PWidth / 2, PHeight / 2), M_SUN, 30);
sun.setColor(color('#ffc107'));
sun.setBeforeDisplay((sun) => {
fill(color(250, 250, 0, 100));
ellipse(sun.position.x, sun.position.y, sun.diameter + 10);
});
// ...
}
定义一个简单的行星模拟水星,让水星围绕太阳运转,并给予水星初始速度。
function setup() {
// ...
// mercury
const mercury = new Planet('Mercury', createVector(sun.position.x + 35, PHeight / 2), 1E2, 6);
mercury.setColor(color('#abb8c3'));
mercury.setVelocity(createVector(0, 2));
// ...
}
再分别加入金星和地球:
function setup() {
// ...
// Venus
const venus = new Planet('Venus', createVector(sun.position.x + 80, sun.position.y), 100, 7);
venus.setColor(color('#f3f4f7'));
venus.setVelocity(createVector(0, -1.2));
// earth
const earth = new Planet('Earth', createVector(sun.position.x + 150, PHeight / 2), M, 10);
earth.setColor(color('#4395ff'));
earth.setVelocity(createVector(0, 1));
// ...
}
将三个行星加入到 planets 数组中统一管理:
function setup() {
// ...
planets.push(sun);
planets.push(mercury);
planets.push(earth);
planets.push(venus);
// ...
}
最后,为整个系统增加万有引力,并计算每个星体的受力,将受力作用在星体上,实现 draw 函数如下:
function draw() {
background(10);
planets.forEach(mover => {
planets.forEach(op => {
if (op === mover) return;
const forceDirection = p5.Vector.sub(op.position, mover.position).normalize();
const dist = p5.Vector.dist(mover.position, op.position);
const f = op.mass * mover.mass * G / (dist * dist);
mover.addForce(forceDirection.copy().mult(f));
});
mover.update();
mover.display();
});
}
最终的显示效果:
加入小行星
为系统随机增加一些小行星,定义 createMiniPlanets 方法:
function createMiniPlanets(count = 5) {
const list = [];
for (let i = 0; i < count; i++) {
const position = createVector(PWidth / 2 + 200 + random(-100, 100), PHeight / 2 + random(-200, 200));
const asteroid = new Planet(`Asteroid_${i + 1}`, position, 1, 4);
asteroid.setColor(color(random(50, 255), random(50, 255), random(50, 255)));
asteroid.setVelocity(createVector(0, 0.8));
list.push(asteroid);
}
return list;
}
将小行星加载到系统中:
function setup() {
createCanvas(PWidth, PHeight);
planets = createMiniPlanets(5);
// ...
}
其他代码无需修改,这是加入小行星后的演示效果:
后续文章简单介绍下粒子系统。