You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于牛顿引力的物理模拟器:正质量互斥、负质量互吸问题求助

牛顿引力模拟器异常:正质量排斥、负质量吸引问题排查

我基于牛顿引力方程开发了一款物理模拟器,用于模拟物体间的引力作用。目前遇到异常:仅当物体设置负质量(如mass: -10000000)时才会互相吸引,设置正质量时反而互相排斥。已排除引力常数G过小的可能,以下是物理引擎physicsStepper类、物理对象particle类的代码,Vector2类取自three.js,请求排查问题原因。

physicsStepper类代码

class physicsStepper {
    /**
     * Create a physicsStepper class
     * @param {Object} configs - Configs for the physics stepper. (Optional)
     * @param {number} configs.G - The Gravitational Constant.
     * @param {boolean} configs.collisions - Whether or not to detect collisions.
     * @param {number} configs.simulationBounds - The size of the simulation used to create a Quadtree.
     * @param {number} configs.subdivisions - number of nodes for the Quadtree.
     */
    constructor(configs = {}) {
        //default configs
        Object.assign(this, {
            G: 6.6743 * Math.pow(10, -11),
            collisions: false,
            dimensions: 2,
            useGrid: false,
            simSize: {
                height: 1600,
                width: 1600,
            },
        });
        //apply changes
        Object.assign(this, configs);
        //setup Grid
        if (this.useGrid) this.grid = new grid();
    }

    /**
     * Circle vs circle collider
     * @param {Object} a - A particle object with radius
     * @param {Object} b - A particle object with radius
     * @param {number} r - The distance between the two objects
     * @returns {boolean} - Whether a collision happened or not
     */
    collider(a, b, r) {
        if (r < a.radius + b.radius) return true;
        else return false;
    }

    /**
     * Apply force to an entity
     * @param {Object} entity - a physics object
     * @param {Object} force - a force vector2
     */
    applyForce(entity, force) {
        force.divideScalar(entity.mass);
        entity.acceleration.add(force);
    }

    /**
     * Calculate the attraction between two objects
     * @param {Object} a - A particle object with radius
     * @param {Object} b - A particle object with radius
     * @returns {Object} data.r - the distance between the two entities
     * @returns {Object} data.force - the force of attraction between the two entities
     */
    calcAttraction(a, b) {
        const { G } = this;
        const force = new Vector2();
        force.subVectors(a.position, b.position);
        const r = $.distance(a, b);
        force.normalize();
        const strength = ((G * a.mass * b.mass) / r) * r;
        force.multiplyScalar(strength);
        return { r, force };
    }

    /**
     * Translate the entity to its new position by using it's velocity vector2
     * @param {Object} entity - A physics object
     */
    translatePositions(entity) {
        entity.velocity.add(entity.acceleration);
        entity.position.add(entity.velocity);
        entity.acceleration.multiplyScalar(0);
    }

    /**
     * Calculates and updates all physics objects
     * @param {Object[]} scene - The scene containing the physics objects (required)
     * @param {Number} delta - The delta time (required)
     */
    step(scene, dt) {
        for (let [i, a] of scene.children.entries()) {
            if (!a.physics) continue;
            for (let b of scene.children) {
                if (!b.physics) continue;
                if (a.sn === b.sn) continue;
                const { r, force } = this.calcAttraction(a, b);
                if (this.collisions && this.collider(a, b, r)) {
                    a.velocity.multiplyScalar(0.999);
                    b.velocity.multiplyScalar(0.999);
                }
                force.multiplyScalar(dt);
                this.applyForce(a, force);
            }
            this.translatePositions(a);
        }
    }
}

particle类代码

/**
 * Create a particle object with rendering and physics properties
 * @param {Object} configs - configs for the particle
 *
*/
export class particle {
    constructor(configs = {}) {
        //default configs
        Object.assign(this, {
            type: "particle",
            // physics
            physics: true,
            position: new Vector2(),
            velocity: new Vector2(),
            acceleration: new Vector2(),
            force: new Vector2(),
            mass: 10,
            //visual
            radius: 10,
            color: "#ffffff",
        });
        //apply changes
        Object.assign(this, configs);
        //used by the grid iterator
        this.size = new Vector2(this.radius, this.radius);
    }

    dispose() {
        this.isWaste = true;
    }
}

问题根源及修复方案

1. 力的方向完全错误

calcAttraction方法中,force.subVectors(a.position, b.position)计算的是从b指向a的向量,这会让a受到远离b的力(排斥)。而引力的正确方向应该是从a指向b(让a向b移动)。

2. 引力强度公式错误

牛顿引力公式为 F = G*(m1*m2)/r²,但代码中写成了 ((G * a.mass * b.mass) / r) * r,这等价于 G*m1*m2,完全丢失了距离平方的衰减特性,导致力的大小异常。

修复后的关键代码修改

修正calcAttraction方法

calcAttraction(a, b) {
    const { G } = this;
    const force = new Vector2();
    // 修正方向:计算从a指向b的向量,实现引力吸引
    force.subVectors(b.position, a.position);
    const r = $.distance(a, b);
    // 避免物体重合时除以0
    if (r === 0) return { r: 0, force: new Vector2(0, 0) };
    force.normalize();
    // 修正引力强度:使用距离的平方
    const strength = (G * a.mass * b.mass) / (r * r);
    force.multiplyScalar(strength);
    return { r, force };
}

补充牛顿第三定律的双向力(可选但重要)

当前代码只对a施加力,不符合物理规律,需给b施加大小相等、方向相反的力:

// 在step方法的力应用部分修改
const { r, force } = this.calcAttraction(a, b);
if (this.collisions && this.collider(a, b, r)) {
    a.velocity.multiplyScalar(0.999);
    b.velocity.multiplyScalar(0.999);
}
const forceForB = force.clone().negate(); // 生成反向力
force.multiplyScalar(dt);
forceForB.multiplyScalar(dt);
this.applyForce(a, force);
this.applyForce(b, forceForB); // 给b施加反向力

额外说明

  • 负质量时表现正常的原因:质量符号反转会抵消方向错误,把排斥力变成吸引力。
  • 修复平方反比定律后,力的大小会随距离正常衰减,避免物体因受力过大瞬间飞出。

内容的提问来源于stack exchange,提问作者Kiron Dey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 01:10:27