java - 如何为我的 java Swing graphics2d 组件制作动画
问题描述
我正在制作一个重力模拟器,我需要它实时动画,以便用户可以观看它。我已经能够让它追踪对象将要走的路径。
但正如您所看到的,它只是跟踪它然后显示窗口。我认为我的问题是因为所有这些都在构建 JPanel 的代码部分中,但我不知道如何正确更改它。
这是我正在为我的窗口做的事情:
import java.awt.*;
import javax.swing.*;
import java.lang.Math;
public class Universe {
public static void main(String[] args) throws InterruptedException {
new Universe();
}
public Universe() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Gravity Simulator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
int paneWidth = 500;
int paneHeight = 500;
@Override
public Dimension getPreferredSize() {
return new Dimension(paneWidth, paneHeight);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int size = Math.min(getWidth()-4, getHeight()-4) / 10;
int width = getWidth() - (size * 2);
int height = getHeight() - (size * 2);
int x0=paneWidth/2; int y0=paneHeight/2; int radius0=20;
int y = (getHeight() - (size * 10)) / 2;
for (int horz = 0; horz < 2; horz++) {
int x = (getWidth() - (size * 10)) / 2;
for (int vert = 0; vert < 10; vert++) {
g.drawRect(x, y, size, size);
drawCircle(g, x+25, y+25, 5);//A massive object would go here this just proof of concept
x += size;
}
y += size;
}
double[] velocity={5,-2};
MassiveObject planet = new MassiveObject(g, 20, 50, velocity, 250, 150);
planet.draw(g);
MassiveObject rock = new MassiveObject(g, 2, 25, velocity, 275, 300);
rock.draw(g);
double sGravity = fGrav(planet, rock);
//double dis = massDis(planet, rock);
System.out.println("Distance: "+massDis(planet, rock));
System.out.println("Gravity: "+sGravity+" Newtons of force(gravity is multiplied by "+1000000+")");
double[] traj = objectTrajetory(planet, rock, rock.getMass());
int t = 0;
try {
while(true) {
//double k = sGravity/dis;
//x and y components of motion
double xm = traj[0];
double ym = traj[1];
double[] nVelocity= {xm,ym};
//////////////////////////////
//set new position of object
rock.setX(rock.getX()+(xm));
rock.setY(rock.getY()+(ym));
rock.setVelocity(nVelocity);
rock.draw(g);
t++;
System.out.println("position changed: "+rock.getCoords());
traj = objectTrajetory(planet, rock, 1);
Thread.sleep(100);
if (t> 15){break;}
}
}
catch(Exception e) {
}
//System.out.println("Distance: "+massDis(planet, rock));
//System.out.println("Gravity: "+fGrav(planet, rock)+" Newtons of force(gravity is multiplied by "+1000000+")");
g2d.dispose();
}
这是我的 MassiveObject 的绘制函数的代码:
public void draw(Graphics g){
Graphics2D g2d = (Graphics2D) g;
Ellipse2D.Double circle = new Ellipse2D.Double(this.x0-(this.radius/2), this.y0-(this.radius/2), this.radius, this.radius);
g2d.setColor(Color.GRAY);
g2d.fill(circle);
}
所以基本上我要问的是如何让它运行该算法以在窗口已经拉起后将 MassiveObject 粘贴到它的新位置,这样用户就可以看到它的发生,而不是仅仅用它来构建窗口就可以了?
解决方案
动画的逻辑不应该在paintComponent()
方法中。该paintComponent()
方法应该只绘制动画的当前帧。里面的代码在paintComponent()
一个专门处理所有 UI 绘制、响应点击等的特殊线程中运行。因此,只要paintComponent()
正在运行,UI 中就不会发生任何其他事情,因此您的应用程序“停止运行”。
定期更新状态然后订购重绘的逻辑应该在单独的线程(或主线程)中。当它更新了状态并需要绘制下一帧时,它会调用面板的repaint()
方法。因为您是在另一个线程中执行此操作,所以您会将其包围在SwingUtilities.invokeLater()
. 这命令 Swing 回调到paintComponent()
:
while (true) {
// Update state used by the paintComponent() method
updateObjectPositions();
// Now draw the new animation frame
SwingUtilities.invokeLater(() -> {
universePanel.repaint(0, 0, universeWidth, universeHeight);
});
Thread.sleep(...);
}
因为绘制和更新是在不同的线程中进行的,所以您需要确保数据以线程安全的方式在线程之间共享。如果您刚刚开始并且计算非常快,那么您可以将updateObjectPositions()
方法放入其中,invokeLater()
以便在 UI 线程中更新数据和重绘。但请记住,invokeLater()
只要它运行,里面的代码就会阻塞 UI,所以它应该尽可能简短并且只处理一个帧。至关重要的是,您的while
循环sleep
不应进入任何UI 代码内部invokeLater()
或内部,例如paintComponent()
.
推荐阅读
- json - Laravel 8 在数据库中手动保存 JSON
- vbscript - 需要帮助将多个函数添加到一个 VBScript
- node.js - 使用 node.js 和 Bullmq 部署 API 的流程布局
- reactjs - 反应查询突变打字稿
- arrays - vb.net - “在数组中”函数?
- node.js - 为什么我使用 express-validator 得到未处理的 Promise 错误
- laravel - 带有 Laravel 的 VueJS 3 内联模板
- python - Pip freeze requirements.txt 不起作用
- java - 错误:不支持请求方法“GET”
- python - 通过shell脚本在ubuntu服务器中设置python3.8,对应的pip和virtualenv