jframe不会以恒定速率重新绘制

时间:2015-05-30 18:25:25

标签: java swing paintcomponent keylistener frame-rate

我正在尝试学习Java,来自C / assembly嵌入式系统背景。经过几周的学习,我认为尝试制作一款游戏会很有趣,但我遇到的问题是JPanel以不一致的速度重新粉刷。

我的“游戏”GUI由一个包含JPanel的JFrame组成。如您所见,JFrame的主线程休眠30毫秒,然后更新“游戏”逻辑并重绘JFrame和JPanel。每次重绘JPanel时,我都会检查它是否需要大约30毫秒。正如您所料,帧重绘之间的时间永远不会超过32毫秒。

尽管JPanel每30毫秒左右肯定会被重新绘制,但它中的动画可能非常不稳定。在Ubuntu 14.10上,非常明显,即使调用我的JPanel的repaint()之间的时间仍然不超过32ms。

最令人烦恼的是,如果我取消注释行

//this.resize(300 + a, 300);
//a = (a == 1)?(0):1;
在SimFrame.java中,一切都非常流畅,这意味着我的计算机具有功能以我想要的速度更新JPanel。

是否有某种方法可以强制JFrame以我想要的速率更新而不进行荒谬的.resize调用?

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;
                        }
                }
        }
}

非常感谢。

1 个答案:

答案 0 :(得分:1)

试试这个,如果有效,我会将其转换为真正的答案(我只是不知道它是否会比你当前的代码更好地在你的系统上运行):

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();
            }
        });
    }
}

是的,我知道操作系统在按住密钥时会出现延迟,如果有效,我们会接受它。

编辑:

更简单的动画:

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();
            }
        });
    }
}