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; } }
问题原因
- 速度重复累加:
vector方法已经计算出了新速度(原速度+加速度*时间步长),但在Timer中又把这个新速度加到了原速度上,导致速度只会持续增大,无法减小或反向。 - 方向向量丢失符号:计算
dX和dY时用了Math.abs,丢失了相对位置的方向信息,导致加速度方向错误,无法正确改变速度的正负。 - 位置更新未乘时间步长:直接用速度值更新位置,忽略了
deltaTime,导致移动速度与时间步长脱节,物理逻辑错误。 - 椭圆绘制尺寸错误:
fillOval的宽高参数用了半径而非直径,导致图形显示过小。 - 过度重绘:
paintComponent中调用repaint()会导致无限重绘循环,浪费资源。
修复方案
修改Sim.java
- 修正速度更新逻辑,直接替换而非累加:
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 + "]"); } });
- 修正椭圆绘制尺寸并移除多余
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
相关产品推荐
相关产品推荐

