Jframe won't redraw at constant speed

I am trying to learn Java from an embedded C / assembly background. After a few weeks of learning, I thought it would be interesting to try and make a game, but I am having some problems repainting the JPanel with inconsistent speed.

My "game" GUI consists of a single JFrame that contains a JPanel. As you can see, the main thread for the JFrame sleeps for 30 milliseconds and then updates the game logic and redraws the JFrame and JPanel. Every time the JPanel is redrawn, I check that it took about 30 milliseconds. As you might expect, no more than 32 milliseconds are redrawn between frames.

Even though the JPanel definitely repaints every 30 milliseconds or so, the animation in it can be very choppy. In Ubuntu 14.10 this is very obvious, although the time between calls to my JPanel repaint () is still under 32ms.

The most annoying thing is that if I uncomment the lines

//this.resize(300 + a, 300);
//a = (a == 1)?(0):1;

      

in SimFrame.java, everything is perfectly smooth, which means my computer has the ability to update the JPanel at the speed I want.

Is there a way to get the JFrame to update at the speed I want without making an absurd .resize call?

Main.java

package jdemo;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Main
{
    public static void main(String[] args)
    {
        SimFrame s = new SimFrame();
        Thread t = new Thread(s);
        t.start();
    }
}

      

Sim.java

package jdemo;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.Timer;
import javax.swing.JPanel;
import javax.swing.JFrame;



public class Sim extends JPanel implements KeyListener
{
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private int keys[] = new int[1024];

        double x = 100;
        double y = 100;
        double dx = 0;
        double dy = 0;
        double theta = 0.0;

        private int size = 0;
        private int dsize = 1;
        public Sim()
        {
                this.addKeyListener(this);
                this.setFocusable(true);
        }


        long prevmillis;
        public void paintComponent(Graphics g)
        {
                super.paintComponent(g);
                Graphics2D drawing = (Graphics2D)g;

                //clear the background.
                drawing.setColor(Color.WHITE);
                drawing.fillRect(0, 0, this.getWidth() - 1, this.getHeight() - 1);

                //System.out.printf("dt = %d\n", System.currentTimeMillis() - prevmillis);
                prevmillis = System.currentTimeMillis();

                drawing.setColor(Color.BLACK);
                drawing.drawRect(0, 0, size, size);

                drawing.setColor(Color.BLACK);
                int[] xpoints = {(int)(x + 10 * Math.cos(Math.toRadians(theta))),
                                                 (int)(x + 5 * Math.cos(Math.toRadians(theta - 150))),
                                                 (int)(x + 5 * Math.cos(Math.toRadians(theta + 150)))};
                int[] ypoints = {(int)(y + 10 * Math.sin(Math.toRadians(theta))),
                                                 (int)(y + 5 * Math.sin(Math.toRadians(theta - 150))),
                                                 (int)(y + 5 * Math.sin(Math.toRadians(theta + 150)))};
                drawing.drawPolygon(xpoints, ypoints, 3);
        }

        public void updateLogic()
        {
                if(keys[KeyEvent.VK_UP] == 1)
                {
                        size++;
                }
                else if(keys[KeyEvent.VK_DOWN] == 1)
                {
                        size--;
                }

                //update theta.
                if(keys[KeyEvent.VK_LEFT] == 1)
                {
                        theta += 5;
                }
                if(keys[KeyEvent.VK_RIGHT] == 1)
                {
                        theta -= 5;
                }
                if(theta > 360.1)
                {
                        theta -= 360;
                }
                if(theta < -0.1)
                {
                        theta += 360;
                }


                //update acceleration
                if(keys[KeyEvent.VK_SPACE] == 1)
                {
                        dx += 0.08* Math.cos(Math.toRadians(theta));
                        dy += 0.08 * Math.sin(Math.toRadians(theta));
                }

                dx *= 0.99;
                dy *= 0.99;
                //update position
                x = x + dx;
                y = y + dy;
                System.out.printf("%f, %f\n", dx, dy);


                //update size
                if(size > 150)
                {
                    dsize = -1;
                }
                if(size < 10)
                {
                    dsize = 1;
                }
                size += dsize;
        }

        @Override
        public void keyPressed(KeyEvent arg0)
        {
                // TODO Auto-generated method stub
                keys[arg0.getKeyCode()] = 1;
                System.out.printf("%d\n", arg0.getKeyCode());
        }

        @Override
        public void keyReleased(KeyEvent arg0)
        {
                // TODO Auto-generated method stub
                keys[arg0.getKeyCode()] = 0;
        }

        @Override
        public void keyTyped(KeyEvent arg0)
        {
                // TODO Auto-generated method stub

        }
}

      

SimFrame.java

package jdemo;


import jdemo.Sim;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class SimFrame extends JFrame implements Runnable
{
        private Sim s;
        public SimFrame()
        {
                this.s = new Sim();
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                this.setContentPane(this.s);
                this.pack();
                this.setLocationRelativeTo(null);
                this.setSize(200, 200);
        }

        @Override
        public void run()
        {
                int a = 0;
                this.setVisible(true);
                while(true)
                {
                        //repaint
                        s.updateLogic();
                        this.getContentPane().revalidate();
                        this.repaint();
                        //this.resize(300 + a, 300);
                        //a = (a == 1)?(0):1;

                        try
                        {
                                Thread.sleep(30);
                        }
                        catch(InterruptedException e)
                        {
                                System.out.printf("failed");
                                break;
                        }
                }
        }
}

      

Many thanks.

+3


source to share


1 answer


Try this and if it works, I'll convert it to the real answer (I just can't know if it will work better on your system than your current code):

public class SimFrame extends JFrame {

    public SimFrame() {

        setContentPane(new Sim());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private class Sim extends JPanel {

        double x = 100;
        double y = 100;
        double dx = 0;
        double dy = 0;
        double theta = 0.0;
        private int size = 0;
        private int dsize = 1;

        public Sim() {

            addKeyListener(new Controller());
            setFocusable(true);
            setBackground(Color.WHITE);

            new Timer(30, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {

                    dx *= 0.99;
                    dy *= 0.99;

                    // update position
                    x = x + dx;
                    y = y + dy;

                    // update size
                    if (size > 150)
                        dsize = -1;
                    if (size < 10)
                        dsize = 1;
                    size += dsize;

                    // update theta.
                    if (theta > 360.1) {
                        theta -= 360;
                    }
                    if (theta < -0.1) {
                        theta += 360;
                    }

                    repaint();
                }
            }).start();
        }

        @Override
        public Dimension getPreferredSize() {

            return new Dimension(200, 200);
        };

        @Override
        public void paintComponent(Graphics g) {

            super.paintComponent(g);
            Graphics2D drawing = (Graphics2D) g;

            drawing.setColor(Color.BLACK);
            drawing.drawRect(0, 0, size, size);

            drawing.setColor(Color.BLACK);
            int[] xpoints = {(int) (x + 10 * Math.cos(Math.toRadians(theta))), (int) (x + 5 * Math.cos(Math.toRadians(theta - 150))),
                                (int) (x + 5 * Math.cos(Math.toRadians(theta + 150)))};
            int[] ypoints = {(int) (y + 10 * Math.sin(Math.toRadians(theta))), (int) (y + 5 * Math.sin(Math.toRadians(theta - 150))),
                                (int) (y + 5 * Math.sin(Math.toRadians(theta + 150)))};
            drawing.drawPolygon(xpoints, ypoints, 3);
        }

        private class Controller extends KeyAdapter {

            @Override
            public void keyPressed(KeyEvent evt) {

                switch (evt.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        size++;
                        break;
                    case KeyEvent.VK_DOWN:
                        size--;
                        break;
                    case KeyEvent.VK_LEFT:
                        theta += 5;
                        break;
                    case KeyEvent.VK_RIGHT:
                        theta -= 5;
                        break;
                    case KeyEvent.VK_SPACE:
                        dx += 0.08 * Math.cos(Math.toRadians(theta));
                        dy += 0.08 * Math.sin(Math.toRadians(theta));
                        break;
                }
            }
        }
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {

                new SimFrame();
            }
        });
    }
}

      

And yes, I know that the OS delay while holding the key, we get if it works.




Edit:

Simplified animation:

public class CopyOfSimFrame extends JFrame {

    public CopyOfSimFrame() {

        setContentPane(new Sim());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private class Sim extends JPanel {

        private int size = 0;
        private int dsize = 1;

        public Sim() {

            setBackground(Color.WHITE);

            new Timer(30, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {

                    // update size
                    if (size >= 150)
                        dsize = -1;
                    if (size <= 0)
                        dsize = 1;
                    size += dsize;

                    repaint();
                }
            }).start();
        }

        @Override
        public Dimension getPreferredSize() {

            return new Dimension(150, 150);
        };

        @Override
        public void paintComponent(Graphics g) {

            super.paintComponent(g);
            g.setColor(Color.BLACK);
            g.drawRect(0, 0, size, size);
        }
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {

                new CopyOfSimFrame();
            }
        });
    }
}

      

+1


source







All Articles