[英]How do I animate my java Swing graphics2d component
我正在制作一個重力模擬器,我需要它實時動畫,以便用戶可以觀看它。 我已經能夠追蹤到 object 將采用的路徑。
但正如您所看到的,它只是跟蹤它,然后顯示 window。 我認為我的問題是因為所有這些都在構建 JPanel 的代碼部分中,但我不知道如何正確更改它。
這是我為 window 所做的:
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 的繪制 function 的代碼:
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);
}
所以基本上我要問的是如何讓它運行該算法以在 window 已經拉起之后將 MassiveObject 粘貼到其新位置,以便用戶可以看到它發生,而不是僅僅構建 window 已經在它上面?
您的 animation 的邏輯不應該在paintComponent()
方法中。 paintComponent()
方法應該只繪制 animation 的當前幀。 paintComponent()
中的代碼在一個專門用於處理所有 UI 繪制、響應點擊等的特殊線程中運行。因此,只要paintComponent()
正在運行,UI 中就不會發生任何其他事情,因此您的應用程序“磨成一個停”。
定期更新 state 然后訂購重繪的邏輯應該在單獨的線程(或主線程)中。 當它更新了 state 並需要繪制下一幀時,它會調用面板的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
不應go 在invokeLater()
或任何 UI 代碼(如paintComponent()
內。
非常感謝您的幫助,我能夠讓程序按照我想要的方式制作動畫,並且完全按照你們的建議。 我從 paintComponent() 中刪除了我的邏輯並將其放在 JPanel 窗格中,運行一個計時器以不斷更新 position,然后在計時器中每個循環結束時運行 repaint() function。
public class TestPane extends JPanel {
int paneWidth = 1200;
int paneHeight = 1200;
double[] velocity={4,4};
MassiveObject planet = new MassiveObject( 50, 50, velocity, paneWidth/2,paneHeight/2);
MassiveObject rock = new MassiveObject( 2, 25, velocity, 150, 200);
double[] traj = objectTrajetory(planet, rock, rock.getMass());
double xm=0.00;
double ym=0.00;
public TestPane() {
Timer timer = new Timer(500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
xm = traj[0];
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);
System.out.println("position changed: "+rock.getCoords());
repaint();
traj = objectTrajetory(planet, rock, 1);
rock.setX(rock.getX()+(xm));
rock.setY(rock.getY()+(ym));
repaint();
}
});
timer.start();
}
@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;
rock.draw(g);
planet.draw(g);
g2d.dispose();
}
該程序現在的動畫非常流暢,而不是僅僅吐出一個 plot 的路徑。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.