簡體   English   中英

如何為我的 java Swing graphics2d 組件設置動畫

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM