【问题标题】:Skipping and Tearing in Java AnimationJava动画中的跳过和撕裂
【发布时间】:2011-01-08 00:55:28
【问题描述】:

下面的代码绘制了一个正方形,里面有两个较小的正方形旋转。每当您单击键盘上的箭头时,整个系统都会朝那个方向移动。但是我在图像撕裂和有时跳过(它很小但仍然存在)方面遇到了一些问题。我想知道是否有人知道我如何在不大量更改代码的情况下解决这些问题。

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)
    {
    }
}

【问题讨论】:

    标签: java animation tearing


    【解决方案1】:

    您没有在EDT 上进行构建。 javax.swing.Timer 的实例使这很容易,因为动作事件处理程序在 EDT 上执行。

    附录 1:您可能决定您需要 double buffering,但首先将下面的代码与您的代码进行比较并查看。如果你走那条路,你可能会看看这个tutorial

    附录 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 http://stackoverflow.com/questions/2114455 */
    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)
    • 这是我的理解。我分解了 d (delta) 来查看它的效果并且没有重置它。这个版本是对您系统的改进吗?
    • 不抱歉,这比我的原始代码更容易撕裂和跳过
    • 我很难看到撕裂,但上面的版本可能会有所帮助。跳过是 d 的函数。入侵者教程如何运行? stumbleupon.com/su/8wGoxm/www.cokeandcode.com/info/tut2d.html
    【解决方案2】:

    我会看看双缓冲。基本上,您绘制到屏幕外图形对象,然后将屏幕外图形对象绘制到 JPanel 的图形对象上。

    ~螺栓

    【讨论】:

      猜你喜欢
      • 2011-08-13
      • 2011-07-10
      • 1970-01-01
      • 2011-04-09
      • 1970-01-01
      • 2012-12-09
      • 1970-01-01
      • 2014-10-10
      • 1970-01-01
      相关资源
      最近更新 更多