匀变速直线运动(UVRM)动画开发:小车停止控制与窗口适配问题求助
问题核心原因梳理
Timer参数使用错误:Swing的Timer构造方法第一个入参是两次回调触发的间隔毫秒数,不是动画总运行时长,你之前把总时长传入会导致整个动画刷新逻辑完全不符合预期。- 位移计算逻辑错误:匀变速直线运动的位移不能每次固定加加速度值,需要结合运行时间按物理公式计算,否则运动速度不符合匀变速规则。
- 停止判断逻辑缺失:原有逻辑只判断了距离阈值,没有加入时间耗尽、超出窗口边界的判断。
具体修改方案
1. 调整Timer初始化与动画逻辑
动画刷新间隔统一设为16ms(对应约60帧的流畅效果),新增变量记录动画开始时间、总运行时长,每次回调时计算已运行时间,再按匀变速公式计算位移。
2. 补充多条件停止判断
同时满足以下任意一个条件就停止动画:
- 动画已运行时长 >= 设定的总时长
- 小车右边缘 >= 目标距离阈值
- 小车右边缘 >= 面板当前宽度(避免超出窗口)
3. 新增位置适配逻辑
每次计算位移后先判断是否超出面板边界,若超出则把小车位置固定在窗口右边缘内再停止。
修改后代码参考
Car类修改(新增重置位置方法)
public class Car { private final ImageIcon imagen; private double posx; private int width; public Car() { imagen = new ImageIcon(getClass().getResource("/images/carred.png")); posx = 10; width = 400; } // 新增重置位置方法,避免多次触发动画时位置累积 public void resetPos() { posx = 10; } public double getPosx() { return posx; } public int getWidth() { return width; } public void setPosx(double posx) { this.posx = posx; } public void draw(Graphics g){ g.drawImage(imagen.getImage(), (int) posx , 50, width, 300, null); } public void move(double x){ posx+=x; } }
JPanel相关代码修改
private void btnCalcActionPerformed(java.awt.event.ActionEvent evt) { String text_vf = txfvf.getText(); String text_v0 = txfv0.getText(); String text_a = txfa.getText(); String text_d = txfd.getText(); String text_t = txft.getText(); String res = "message.. res"; // 原有计算逻辑保留 // 注意:这里传入的t是秒数,后面转成毫秒作为总动画时长 moveCarImage(t * 1000, Integer.parseInt(text_v0), a, Integer.parseInt(text_d)); } private void moveCarImage(double totalDurationMs, double v0, double acceleration, int fDistance) { // 先重置小车初始位置 car.resetPos(); // 记录动画开始时间 long startTime = System.currentTimeMillis(); // 动画刷新间隔设为16ms,约60帧 int frameInterval = 16; // 如果之前有运行的timer先停止,避免多次点击生成多个timer if (timer != null && timer.isRunning()) { timer.stop(); } timer = new Timer(frameInterval, (e) -> { // 计算已运行时长,单位转成秒用于物理计算 double elapsedTimeS = (System.currentTimeMillis() - startTime) / 1000d; // 匀变速位移公式:s = v0*t + 0.5*a*t² double currentDisplacement = v0 * elapsedTimeS + 0.5 * acceleration * elapsedTimeS * elapsedTimeS; // 更新小车位置 car.setPosx(10 + currentDisplacement); // 停止条件判断 boolean isTimeOut = (System.currentTimeMillis() - startTime) >= totalDurationMs; boolean isReachDistance = (car.getPosx() + car.getWidth()) >= fDistance; boolean isOverFrame = (car.getPosx() + car.getWidth()) >= this.getWidth(); if (isTimeOut || isReachDistance || isOverFrame) { timer.stop(); // 超出窗口时把小车位置固定在右边缘内 if (isOverFrame) { car.setPosx(this.getWidth() - car.getWidth()); } System.out.println("Car Stopped........."); } repaint(); }); timer.start(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); car.draw(g); }
内容的提问来源于stack exchange,提问作者DarkVaderM
相关产品推荐
相关产品推荐

