dongseop
dongseop

Reputation: 53

How to repaint properly in JPanel inside JFrame

stackoverflow community! I am making a small GUI based game in JAVA. I am having a few troubles right now. To give you basic understanding of my program,

I have a window that shows menu(JPanel) first. When I click on "start game" button. It proceeds to another JPanel on which I can play game. ![menu_window][1]

I have a timer working on a small bullet that is moving periodically. Every time the timer works, the Panel repaints the bullet. I can see the repaint method works but it doesn't remove the previous trace.

![enter image description here][2]

This is my main class

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Main {
    private final int window_x_size = 500;
    private final int window_y_size = 500;

    private JFrame frame;
    private JPanel Menu;
    private Game myGame = new Game();//extends from JPanel



    private JButton startButton = new JButton("Game Start");
    private JButton exitButton = new JButton("Game Exit");
    private JButton showRank = new JButton("Rank");
    private JButton OneOnOne = new JButton("One on One");


    private ActionListener MyButtonListener = new MyButtonListener();
    public class MyButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            Object source = e.getSource();

            if (source == startButton) {

                frame.remove(Menu);
                frame.setContentPane(myGame);
                frame.validate();
                frame.repaint(); // prefer to write this always.

            } else if (source == exitButton) {
                System.exit(1);
            } else if (source == showRank) {

            } else {// one on one

            }

        }
    }


    public Main() {
        frame = new JFrame();

        frame.setBackground(Color.white);
        frame.setSize(window_x_size, window_y_size);
        frame.setTitle("My First GUI Game");
        frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);

        Menu = new JPanel();
        startButton.addActionListener(MyButtonListener);
        exitButton.addActionListener(MyButtonListener);
        showRank.addActionListener(MyButtonListener);
        OneOnOne.addActionListener(MyButtonListener);

        Menu.setLayout(new GridLayout(4, 1));
        Menu.add(startButton);
        Menu.add(OneOnOne);
        Menu.add(showRank);
        Menu.add(exitButton);

        frame.setContentPane(Menu);
        frame.setVisible(true);

    }

    public static void main(String[] args) {
        /*
         * This is the most important part of your GUI app, never forget 
         * to schedule a job for your event dispatcher thread : 
         * by calling the function, method or constructor, responsible
         * for creating and displaying your GUI.
         */
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {  
                new Main();
            }
        });


    }

}

This is my Game class

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Game extends JPanel {
    private Random rnd = new Random();
    private ActionListener MyBulletListener = new BulletListener();
    public KeyListener myKeyListen = new MyKeyListener();
    Timer bullet_timer;

    private boolean IsExplosion = false;
    private ImageIcon spacecraftImg = new ImageIcon("spacecraft.png");
    private Rectangle spacecraftBox = new Rectangle(5, 10,
            spacecraftImg.getIconWidth(), spacecraftImg.getIconHeight());
    private ImageIcon explosionImg = new ImageIcon("explosion.png");
    private ImageIcon bulletImg = new ImageIcon("bullet.png");
    private Rectangle bulletBox = new Rectangle(70, 200,
            bulletImg.getIconWidth(), bulletImg.getIconHeight());

    private MySound explosion_sound = new MySound();

    public Game() {

        addKeyListener(myKeyListen);
        bullet_timer = new Timer(500, MyBulletListener);
        bullet_timer.start();

    }

    public class MyKeyListener implements KeyListener {

        private int x;
        private int y;

        @Override
        public void keyPressed(KeyEvent e) {

            // TODO Auto-generated method stub
            int keyCode = e.getKeyCode();

            if (keyCode == 37) {
                x = -10;
                y = 0;

            } else if (keyCode == 38) {
                x = 0;
                y = -10;
            } else if (keyCode == 39) {
                x = 10;
                y = 0;
            } else {
                x = 0;
                y = 10;
            }
            if (spacecraftBox.x + x < 0
                    || spacecraftBox.x + x > 500
                    || spacecraftBox.y + y < 0
                    || spacecraftBox.y + y > 500) {

            } else {
                move_plane(x, y);
                repaint();
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub

        }

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

        }

    }
    public class BulletListener implements ActionListener {
        private int x;
        private int y;

        @Override
        public void actionPerformed(ActionEvent e) {
            // TODO Auto-generated method stub
            Object source = e.getSource();
            if (source == bullet_timer) {

                int dir = rnd.nextInt(4);
                if (dir == 0) {// move left
                    x = -10;
                    y = 0;
                } else if (dir == 1) {// move right
                    x = 10;
                    y = 0;
                } else if (dir == 2) {// move up
                    x = 0;
                    y = -10;
                } else {// move down
                    x = 0;
                    y = 10;
                }
                if (bulletBox.x + x < 0
                        || bulletBox.x + x > 500
                        || bulletBox.y + y < 0
                        || bulletBox.y + y > 500) {

                } else {
                    move_bullets(x, y);
                    repaint();
                }
            }

        }
    }


    @Override
    public void paint(Graphics g) {

        g.drawImage(bulletImg.getImage(), (int) bulletBox.getX(),
                (int) bulletBox.getY(), null);

        g.drawImage(spacecraftImg.getImage(), (int) spacecraftBox.getX(),
                (int) spacecraftBox.getY(), null);

        if (IsExplosion) {
            g.drawImage(explosionImg.getImage(), (int) bulletBox.getX(),
                    (int) bulletBox.getY(), null);
            MySound.play();
            IsExplosion = false;

        }

    }

    public void move_plane(int x, int y) {

        spacecraftBox.translate(x, y);

        if (spacecraftBox.intersects(bulletBox)) {
            IsExplosion = true;
        }
    }

    public void move_bullets(int x, int y) {
        bulletBox.translate(x, y);

        if (spacecraftBox.intersects(bulletBox)) {
            IsExplosion = true;
        }
    }

}

Plus I added KeyListener but it doesn't work. I have no idea how to fix it out.

I tried googling about the issue. I tried the game panel focusable in main class but it didn't work.

I would really appreciate your help.

Best Regards,

Dongseop

Upvotes: 1

Views: 1957

Answers (1)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285430

This is almost always due to your painting method not calling the super's painting method to clean up dirty pixels. Since you're overriding paint, you would need to call super.paint(g); as the first method call in your paint override.

Other issues:

  • Next I'm going to suggest that you not override paint but rather paintComponent(Graphics g), as this will give your animation automatic double buffering and make the animation smoother. Again, call super.paintComponent(g); in your method override.
  • The easiest way to swap views is to use a CardLayout.
  • Better to use KeyBindings rather than a KeyListener as this will help you get around the KeyListener's focus issue, the issue which is probably preventing your KeyListener from working. It also allows you to use reusable Actions. Please search this site on KeyListeners to see what I mean and to see examples of Key Bindings (some by me). Also Google Java Key Bindings Tutorial to see the official tutorial on how to use these.

Upvotes: 2

Related Questions