Java动画中的跳帧和撕裂问题

3
以下代码绘制了一个正方形,在其中旋转两个较小的正方形。每当您按下键盘上的箭头时,整个系统都会朝该方向移动。但是我遇到了一些问题,图像会撕裂,并且有时会跳过(虽然很小但还是存在)。我想知道是否有人知道如何在不大幅更改代码的情况下解决这些问题。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import static java.awt.Color.*;

public class GUI extends JPanel implements ActionListener, KeyListener
{
    int x, y, x1, y1, x2, y2, changeX, changeY, changeX2, changeY2;
    JFrame frame;
    Runtime r;
    public static void main(String[] args)
    {
        new GUI();
    }
    public GUI()
    {
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e)
        {
            e.printStackTrace();
        }

        setSize(1020,770);
        setBackground(WHITE);
        setOpaque(true);
        setVisible(true);

        x = 0;
        y = 0;
        x1 = 0;
        y1 = 0;
        x2 = 0;
        y2 = 0;
        changeX=1;
        changeY=0;
        changeX2=1;
        changeY2=0;
        r = Runtime.getRuntime();

        frame = new JFrame();
        frame.setSize(1020,819);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setJMenuBar(createMenuBar());
        frame.validate();
        frame.setBackground(WHITE);
        frame.addKeyListener(this);
        frame.setTitle("GUI");
        frame.setContentPane(this);
        frame.setVisible(true);
        frame.createBufferStrategy(2);

        Timer t = new Timer(100,this);
        t.setActionCommand("Draw");
        t.start();

        repaint();
    }
    public JMenuBar createMenuBar()
    {
        JMenuBar menuBar = new JMenuBar();

        JMenu fileMenu = new JMenu("File");
        JMenuItem save = new JMenuItem("Save");
        save.setMnemonic(KeyEvent.VK_S);
        save.setContentAreaFilled(false);
        save.setOpaque(false);
        save.addActionListener(this);
        JMenuItem load = new JMenuItem("Load");
        load.setMnemonic(KeyEvent.VK_L);
        load.setContentAreaFilled(false);
        load.setOpaque(false);
        load.addActionListener(this);
        JMenuItem quit = new JMenuItem("Quit");
        quit.setMnemonic(KeyEvent.VK_Q);
        quit.setContentAreaFilled(false);
        quit.setOpaque(false);
        quit.addActionListener(this);
        fileMenu.add(save);
        fileMenu.add(load);
        fileMenu.addSeparator();
        fileMenu.add(quit);
        fileMenu.setContentAreaFilled(false);
        fileMenu.setBorderPainted(false);
        fileMenu.setOpaque(false);

        JMenu editMenu = new JMenu("Edit");
        JMenuItem undo = new JMenuItem("Undo");
        undo.setMnemonic(KeyEvent.VK_U);
        undo.setContentAreaFilled(false);
        undo.setOpaque(false);
        undo.addActionListener(this);
        JMenuItem redo = new JMenuItem("Redo");
        redo.setMnemonic(KeyEvent.VK_R);
        redo.setContentAreaFilled(false);
        redo.setOpaque(false);
        redo.addActionListener(this);
        editMenu.add(undo);
        editMenu.add(redo);
        editMenu.setContentAreaFilled(false);
        editMenu.setBorderPainted(false);
        editMenu.setOpaque(false);

        JMenu helpMenu = new JMenu("Help");
        JMenuItem controls = new JMenuItem("Controls");
        controls.setMnemonic(KeyEvent.VK_C);
        controls.setContentAreaFilled(false);
        controls.setOpaque(false);
        controls.addActionListener(this);
        JMenuItem about = new JMenuItem("About");
        about.setMnemonic(KeyEvent.VK_A);
        about.setContentAreaFilled(false);
        about.setOpaque(false);
        about.addActionListener(this);
        helpMenu.add(controls);
        helpMenu.addSeparator();
        helpMenu.add(about);
        helpMenu.setContentAreaFilled(false);
        helpMenu.setBorderPainted(false);
        helpMenu.setOpaque(false);

        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        menuBar.add(helpMenu);
        return menuBar;
    }
    public void paintComponent(Graphics g)
    {
        g.clearRect(0, 0, 1020, 770);
        g.setColor(BLACK);
        g.fillRect(x,y,100,100);
        g.setColor(RED);
        g.fillRect(x1,y1,50,50);
        g.setColor(BLUE);
        g.fillRect(x2,y2,25,25);
        g.dispose();
    }
    public void change()
    {
        if(x1>=x+50&&changeY==0&&changeX==1)
        {
            changeX=0;
            changeY=1;
        }
        else if(y1>=y+50&&changeX==0&&changeY==1)
        {
            changeX=-1;
            changeY=0;
        }
        else if(x1<=x&&changeX==-1&&changeY==0)
        {
            changeX=0;
            changeY=-1;
        }
        else if(y1<=y&&changeY==-1&&changeX==0)
        {
            changeX=1;
            changeY=0;
        }
        x1+=changeX*5;
        y1+=changeY*5;
    }
    public void change2()
    {
        if(x2>=x1+25&&changeY2==0&&changeX2==1)
        {
            changeX2=0;
            changeY2=1;
        }
        else if(y2>=y1+25&&changeX2==0&&changeY2==1)
        {
            changeX2=-1;
            changeY2=0;
        }
        else if(x2<=x1&&changeX2==-1&&changeY2==0)
        {
            changeX2=0;
            changeY2=-1;
        }
        else if(y2<=y1&&changeY2==-1&&changeX2==0)
        {
            changeX2=1;
            changeY2=0;
        }
        x2+=changeX2*2;
        y2+=changeY2*2;
    }
    public void actionPerformed(ActionEvent e)
    {
        if(e.getActionCommand().equalsIgnoreCase("Draw"))
        {
            r.runFinalization();
            r.gc();
            change();
            change2();
            repaint();
        }
    }
    public void keyPressed(KeyEvent e)
    {
        if(e.getKeyCode()==KeyEvent.VK_UP)
        {
            if(y-10>=0)
            {
                y-=10;
                y1-=10;
                y2-=10;
            }
        }
        if(e.getKeyCode()==KeyEvent.VK_DOWN)
        {
            if(y+110<=getHeight())
            {
                y+=10;
                y1+=10;
                y2+=10;
            }
        }
        if(e.getKeyCode()==KeyEvent.VK_LEFT)
        {
            if(x-10>=0)
            {
                x-=10;
                x1-=10;
                x2-=10;
            }
        }
        if(e.getKeyCode()==KeyEvent.VK_RIGHT)
        {
            if(x+110<=getWidth())
            {
                x+=10;
                x1+=10;
                x2+=10;
            }
        }
        repaint();
    }
    public void keyReleased(KeyEvent e)
    {
    }
    public void keyTyped(KeyEvent e)
    {
    }
}
2个回答

2
你没有在EDT上进行构建。使用javax.swing.Timer的实例可以轻松完成此操作,因为操作事件处理程序在EDT上执行。
附录1:你可能决定需要双缓冲,但首先请比较下面的代码和你自己的代码。如果你选择这条路线,你可以看一下这个教程
附录2:下面的示例展示了如何维护一个离屏缓冲区,但有时使用JPanel(boolean isDoubleBuffered)构造函数可以更容易地实现类似的效果。
import java.awt.event.KeyAdapter;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import static java.awt.Color.*;

/** @see https://dev59.com/Q0vSa4cB1Zd3GeqPenzd */
public class GUI extends JPanel implements ActionListener {

    int x, y, x1, y1, x2, y2, changeY, changeY2;
    int changeX = 1; int changeX2 = 1;
    Timer t = new Timer(100, this);

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new GUI(true).display();
            }
        });
    }

    public GUI(boolean doubleBuffered) {
        super(doubleBuffered);
        this.setPreferredSize(new Dimension(320, 240));
    }

    private void display() {
        JFrame frame = new JFrame("GUI");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addKeyListener(new KeyListener());
        frame.add(this);
        frame.pack();
        frame.setVisible(true);
        t.start();
    }

    @Override
    public void paintComponent(Graphics g) {
        g.setColor(WHITE);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(BLACK);
        g.fillRect(x, y, 100, 100);
        g.setColor(RED);
        g.fillRect(x1, y1, 50, 50);
        g.setColor(BLUE);
        g.fillRect(x2, y2, 25, 25);
    }

    public void change() {
        if (x1 >= x + 50 && changeY == 0 && changeX == 1) {
            changeX = 0;
            changeY = 1;
        } else if (y1 >= y + 50 && changeX == 0 && changeY == 1) {
            changeX = -1;
            changeY = 0;
        } else if (x1 <= x && changeX == -1 && changeY == 0) {
            changeX = 0;
            changeY = -1;
        } else if (y1 <= y && changeY == -1 && changeX == 0) {
            changeX = 1;
            changeY = 0;
        }
        x1 += changeX * 5;
        y1 += changeY * 5;
    }

    public void change2() {
        if (x2 >= x1 + 25 && changeY2 == 0 && changeX2 == 1) {
            changeX2 = 0;
            changeY2 = 1;
        } else if (y2 >= y1 + 25 && changeX2 == 0 && changeY2 == 1) {
            changeX2 = -1;
            changeY2 = 0;
        } else if (x2 <= x1 && changeX2 == -1 && changeY2 == 0) {
            changeX2 = 0;
            changeY2 = -1;
        } else if (y2 <= y1 && changeY2 == -1 && changeX2 == 0) {
            changeX2 = 1;
            changeY2 = 0;
        }
        x2 += changeX2 * 2;
        y2 += changeY2 * 2;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        change();
        change2();
        repaint();
    }

    private class KeyListener extends KeyAdapter {

        @Override
        public void keyPressed(KeyEvent e) {
            int d = 5;
            if (e.getKeyCode() == KeyEvent.VK_UP) {
                if (y - d >= 0) {
                    y -= d;
                    y1 -= d;
                    y2 -= d;
                }
            }
            if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                if (y + 100 + d <= getHeight()) {
                    y += d;
                    y1 += d;
                    y2 += d;
                }
            }
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                if (x - d >= 0) {
                    x -= d;
                    x1 -= d;
                    x2 -= d;
                }
            }
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                if (x + 100 + d <= getWidth()) {
                    x += d;
                    x1 += d;
                    x2 += d;
                }
            }
        }
    }
}

我原以为它自动双缓冲,我不需要做任何事情。 还有,你为什么把偏移量从10改成了5(在你的代码中称为d)? - resotpvl
这是我的理解。我将d(delta)分解出来以查看其效果,并没有重置它。这个版本对你的系统有改进吗? - trashgod
抱歉,这段代码比我的原始代码更容易出现撕裂和跳跃。 - resotpvl
我对撕裂现象感到困扰,但上面的版本可能会有所帮助。跳跃是d的一个功能。入侵者教程是怎么运行的?http://www.stumbleupon.com/su/8wGoxm/www.cokeandcode.com/info/tut2d.html - trashgod

1
我建议您考虑使用双缓冲技术。简单来说,您可以先将图形绘制到一个离屏图形对象上,然后再将该离屏图形对象绘制到JPanel的图形对象上。
~Bolt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接