3

the following code draws a square with two smaller square rotating inside it. whenever you click an arrow on the keyboard, the whole system will move in that direction. however i'm having some problems with the image tearing and at times skipping (its small but still there). i was wondering if anybody knew how i could fix these issues w/o massively altering the code.

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)
    {
    }
}
Topher Fangio
  • 20,372
  • 15
  • 61
  • 94
resotpvl
  • 59
  • 1
  • 7

2 Answers2

2

You are not constructing on the EDT. An instance of javax.swing.Timer makes this easy, as the action event handler executes on the EDT.

Addendum 1: You may decide you need double buffering, but first compare the code below to yours and see. If you go that route, you might look at this tutorial.

Addendum 2: The example below shows how to maintain an offscreen buffer, but it is sometimes easier to use the JPanel(boolean isDoubleBuffered) constructor for a similar effect.

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;
                }
            }
        }
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • i thought it was automatically double buffered and that i wouldnt have to do anything and also why did you change the offset from 10 to 5 (in ur code you called it d) – resotpvl Jan 22 '10 at 22:40
  • That is my understanding. I factored out the d (delta) to see its effect and didn't reset it. Is this version an improvement on your system? – trashgod Jan 23 '10 at 03:49
  • no sorry, this has significantly more tearing and skipping than my original code – resotpvl Jan 26 '10 at 00:35
  • I have trouble seeing tearing, but the version above may help that. Skipping is a function of d. How does the invaders tutorial run? http://www.stumbleupon.com/su/8wGoxm/www.cokeandcode.com/info/tut2d.html – trashgod Jan 26 '10 at 16:51
1

I would take a look at double buffering. Basically, you draw to an offscreen graphics object and then draw the offscreen graphics object onto the JPanel's graphics object.

~Bolt

Boltimuss
  • 147
  • 2
  • 14