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

2D重力模拟中velocity未衰减/反向的问题排查求助

重力模拟程序问题排查与修复

我正在开发一个程序,让object1的运动受object2的重力作用产生偏移,预期它会被甩动,速度(velocity)应在两个方向上发生变化,但实际速度从未减小或变为负值。公式推导自F = G(m1*m2)/r^2。

原代码

Sim.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Sim extends JPanel {

    private static final long serialVersionUID = -2669101810074157675L;
    public static final int PREF_W = 800, PREF_H = 600;

    private Mass object1, object2;

    private Sim() {
        this.setFocusable(true);
        this.setBackground(Color.WHITE);

        double[] vect1 = {1, 0}, vect2 = {0, 0};
        object1 = new Mass(new Point(PREF_W / 2 - 100, PREF_H / 2 - 100), vect1, 10);
        object2 = new Mass(new Point(PREF_W / 2 + 100, PREF_H / 2 + 100), vect2, 30);
        object2.lock();

        gameTimer.start();

    }

    private Timer gameTimer = new Timer(1000 / 30, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {

            double[] v = Mass.vector(object1, object2);
            object1.dx += v[0];
            object1.dy += v[1];

            Point p = new Point(
                    (int) (object1.center.x + object1.dx),
                    (int) (object1.center.y + object1.dy)
            );
            object1.center = p;

            System.out.println("[ " + object1.center.x + "," + object1.dy + "]");
        }
    });

    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;

        g2.fillOval(
                (int) object1.center.x - (int) object1.radius,
                (int) object1.center.y - (int) object1.radius,
                (int) object1.radius,
                (int) object1.radius
        );

        g2.fillOval(
                (int) object2.center.x - (int) object2.radius,
                (int) object2.center.y - (int) object2.radius,
                (int) object2.radius,
                (int) object2.radius
        );

        g2.drawLine(object1.center.x, object1.center.y, object2.center.x, object2.center.y);


        repaint();

    }

    /* METHODS FOR CREATING JFRAME AND JPANEL */

    public Dimension getPreferredSize() {
        return new Dimension(PREF_W, PREF_H);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Gravity Simulation");
                JPanel gamePanel = new Sim();

                frame.getContentPane().add(gamePanel);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }

}

Mass.java

import java.awt.Point;

public class Mass {

    public static double G = 1f;
    public static double deltaTime = 1;

    public Point center;
    public double mass;
    public double radius;

    public double dx = 0;
    public double dy = 0;

    public boolean locked = false;

    public Mass(Point center, double[] vect, double mass) {
        this.center = center;
        this.dx = vect[0];
        this.dy = vect[1];
        this.mass = mass;
        this.radius = mass;
    }

    public void lock() {
        this.locked = true;
    }

    public void unlock() {
        this.locked = false;
    }

    public static double distance(Mass obj1, Mass obj2) {

        double dX = obj1.center.x - obj2.center.x;
        double dY = obj1.center.y - obj2.center.y;

        double ans = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
        return (double) ans;

    }

    public static double force(Mass obj1, Mass obj2) {

        double ans = ((obj1.mass * obj2.mass) / Math.pow(distance(obj1, obj2), 2)) * G;
        return (double) ans;

    }

    public static double[] vector(Mass obj1, Mass obj2) {
        double force = force(obj1, obj2);

        double dX = Math.abs(obj1.center.x - obj2.center.x);
        double dY = Math.abs(obj1.center.y - obj2.center.y);

        double udX = dX / distance(obj1, obj2);
        double udY = dY / distance(obj1, obj2);

        double fx = -(udX * force);
        double fy = -(udY * force);

        double x = obj1.dx + fx / obj1.mass * deltaTime;
        double y = obj1.dy + fy / obj1.mass * deltaTime;

        double[] v = {x, y};
        return v;

    }

}

问题原因

  1. 速度重复累加:vector方法已经计算出了新速度(原速度+加速度*时间步长),但在Timer中又把这个新速度加到了原速度上,导致速度只会持续增大,无法减小或反向。
  2. 方向向量丢失符号:计算dX和dY时用了Math.abs,丢失了相对位置的方向信息,导致加速度方向错误,无法正确改变速度的正负。
  3. 位置更新未乘时间步长:直接用速度值更新位置,忽略了deltaTime,导致移动速度与时间步长脱节,物理逻辑错误。
  4. 椭圆绘制尺寸错误:fillOval的宽高参数用了半径而非直径,导致图形显示过小。
  5. 过度重绘:paintComponent中调用repaint()会导致无限重绘循环,浪费资源。

修复方案

修改Sim.java

  1. 修正速度更新逻辑,直接替换而非累加:
private Timer gameTimer = new Timer(1000 / 30, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        double[] v = Mass.vector(object1, object2);
        // 直接替换速度,避免重复累加
        object1.dx = v[0];
        object1.dy = v[1];

        Point p = new Point(
                // 位置更新乘以deltaTime,符合物理逻辑
                (int) (object1.center.x + object1.dx * Mass.deltaTime),
                (int) (object1.center.y + object1.dy * Mass.deltaTime)
        );
        object1.center = p;

        System.out.println("[ " + object1.center.x + "," + object1.dy + "]");
    }
});
  1. 修正椭圆绘制尺寸并移除多余repaint():
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;

    g2.fillOval(
            (int) object1.center.x - (int) object1.radius,
            (int) object1.center.y - (int) object1.radius,
            (int) object1.radius * 2, // 改为直径
            (int) object1.radius * 2
    );

    g2.fillOval(
            (int) object2.center.x - (int) object2.radius,
            (int) object2.center.y - (int) object2.radius,
            (int) object2.radius * 2, // 改为直径
            (int) object2.radius * 2
    );

    g2.drawLine(object1.center.x, object1.center.y, object2.center.x, object2.center.y);

    // 移除多余的repaint()
}

修改Mass.java的vector方法

保留方向向量符号,修正加速度方向:

public static double[] vector(Mass obj1, Mass obj2) {
    double force = force(obj1, obj2);

    // 保留相对位置的方向符号,不要取绝对值
    double dX = obj2.center.x - obj1.center.x;
    double dY = obj2.center.y - obj1.center.y;

    double distance = distance(obj1, obj2);
    double udX = dX / distance;
    double udY = dY / distance;

    // 力的方向指向object2,加速度与单位向量方向一致
    double fx = udX * force;
    double fy = udY * force;

    // 速度更新公式:v = v0 + (F/m)*deltaTime
    double x = obj1.dx + fx / obj1.mass * deltaTime;
    double y = obj1.dy + fy / obj1.mass * deltaTime;

    double[] v = {x, y};
    return v;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 05:40:54