Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java swing animation looks choppy. How to make it look pro?

Tags:

java

swing

UPDATE: semicomplex animation + swing timer = trainwreck. The ultimate source of the problems was the java timer, either the swing or utility version. They are unreliable, especially when performance is compared across operating systems. By implementing a run-of-the-mill thread, the program runs very smoothly on all systems. http://zetcode.com/tutorials/javagamestutorial/animation/. Also, adding Toolkit.getDefaultToolkit().sync() into the paintComponent() method noticeably helps.

I wrote some code that animated smoothly in an awt.Applet (but flickered), then I refactored it to java swing. Now it doesn't flicker but it looks choppy. I've messed with the timer but that doesn't work. Any tips or suggestions for smoothly animating swing components would be greatly appreciated.


import java.util.Random;
import java.util.ArrayList;
import java.awt.event.;
import java.awt.;
import javax.swing.*;
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

public class Ball extends JApplet{

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            JFrame frame = new JFrame();
            frame.setTitle("And so the ball rolls");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            initContainer(frame);
            frame.pack();
            frame.setVisible(true);
        }
    });
}
public static void initContainer(Container container){

   GraphicsPanel graphicsPanel = new GraphicsPanel();
   MainPanel mainPanel = new MainPanel(graphicsPanel);
   container.add(mainPanel);
   graphicsPanel.startTimer();

}

@Override
public void init(){
    initContainer(this);
}

} /////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// class MainPanel extends JPanel { JLabel label = new JLabel("Particles"); GraphicsPanel gPanel;

    public MainPanel(GraphicsPanel gPanel){
        this.gPanel = gPanel;
        add(gPanel);
        add(label);
    }

} /////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// class GraphicsPanel extends JPanel implements MouseListener {

    private ArrayList<Particle> ballArr = new ArrayList<Particle>();
    private String state="s";         //"s"=spiral, "p"=particle
    private int speed=10;             //~20 Hz
    private Timer timer;

    public GraphicsPanel(){
        System.out.println("echo from gpanel");
        setPreferredSize(new Dimension(500,500));
        timer = new Timer(speed, new TimerListener());
        addMouseListener(this);
    }

    public void startTimer(){
        timer.start();
    }

    @Override
    public void paintComponent(Graphics g){

        super.paintComponent(g);
         for (Particle b: ballArr){
              g.setColor(b.getColor());
              g.fillOval(b.getXCoor(),b.getYCoor(),
                         b.getTheSize(),b.getTheSize());
         }
    }

    public void mousePressed(MouseEvent e) {
        ballArr.add(new Particle(e.getX(), e.getY(), state));
    }
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e){}
    public void mouseExited(MouseEvent e){}
    public void mouseClicked(MouseEvent e) {}

    class TimerListener implements ActionListener {
        public void actionPerformed(ActionEvent e){
             for (Particle b: ballArr)
                 b.move();
             setBackground(Color.WHITE);
             repaint();

        }
    }

}

////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// class Particle { private static int instanceCount; {{instanceCount++;}} private int z = 11, t=1, u=1; private int[] RGB = new int[3]; private int[] randomizeColor = new int[3]; private double radius, theta; private int x, y, centerX, centerY, size, spiralDirection=1, ballSizeLowerBound, ballSizeUpperBound, radiusLowerBound, radiusUpperBound, mouseInputX, mouseInputY, radiusXMultiplier, radiusYMultiplier; private Color color; private String state; private Random random = new Random(); /////////////////////////////////////////////////////////////////////////// public Particle(int x, int y, int centerX, int centerY, int radius, int theta, int size, Color color){ this.x=x;this.y=y;this.centerX=centerX;this.centerY=centerY; this.radius=radius;this.theta=theta;this.size=size;this.color=color; }

public Particle(int mouseInputX, int mouseInputY, String state){ this.mouseInputX=mouseInputX; this.mouseInputY=mouseInputY; this.state=state; //randomize color RGB[0] = random.nextInt(252); RGB[1] = random.nextInt(252); RGB[2] = random.nextInt(252); randomizeColor[0] = 1+random.nextInt(3); randomizeColor[0] = 1+random.nextInt(3); randomizeColor[0] = 1+random.nextInt(3); centerX=mouseInputX; centerY=mouseInputY; if (state.equals("s")){ //setup spiral state ballSizeLowerBound=5; ballSizeUpperBound=18; radiusLowerBound=0; radiusUpperBound=50; radiusXMultiplier=1; radiusYMultiplier=1; } if (state.equals("p")){ //setup particle state ballSizeLowerBound = 15; ballSizeUpperBound =20 + random.nextInt(15); radiusLowerBound = 5; radiusUpperBound = 15+ random.nextInt(34); radiusXMultiplier=1 + random.nextInt(3); radiusYMultiplier=1 + random.nextInt(3); } size = ballSizeUpperBound-1; //ball size radius = radiusUpperBound-1; if (instanceCount %2 == 0) // alternate spiral direction spiralDirection=-spiralDirection; } /////////////////////////////////////////////////////////////////////////// public int getXCoor(){return centerX+x*spiralDirection;} public int getYCoor(){return centerY+y;} public int getTheSize(){return size;} public Color getColor(){return color;} ////////////////////////////////////////////////////////////////////////// void move(){ //spiral: dr/dt changes at bounds if (radius > radiusUpperBound || radius < radiusLowerBound) u = -u; //spiral shape formula: parametric equation for the //polar equation radius = theta x = (int) (radius * radiusXMultiplier * Math.cos(theta)); y = (int) (radius * radiusYMultiplier * Math.sin(theta)); radius += .1*u; theta += .1; //ball size formula if (size == ballSizeUpperBound || size == ballSizeLowerBound) t = -t; size += t; //ball colors change for (int i = 0; i < RGB.length; i++) if (RGB[i] >= 250 || RGB[i] <= 4) randomizeColor[i] = -randomizeColor[i]; RGB[0]+= randomizeColor[0]; RGB[1]+= randomizeColor[1]; RGB[2]+= randomizeColor[2]; color = new Color(RGB[0],RGB[1],RGB[2]); }

}

like image 978
comp sci balla Avatar asked Nov 02 '10 01:11

comp sci balla


1 Answers

Don't set a constant interval timer. Set the timer to go off once -- in the handler

  1. Get the current time (save in frameStartTime)
  2. Do your frame
  3. Set the timer to go off in: interval - (newCurrentTime - frameStartTime)

Should be smoother. If you want to go really pro (and stay in Java), I think you have to consider JavaFX.

like image 81
Lou Franco Avatar answered Oct 18 '22 16:10

Lou Franco