Search code examples
javaswingpaintcomponentrepaint

How to prevent my KeyEvent from repainting both objects?


I am working on a simple game which requires 1 player (the square) and some enemies that spawn randomly inside the play-area. I am running into an issue currently, because when I run my program, pressing any arrow key will repaint not only the player's new location, but it will also re-spawn all the enemies into the new locations.

I have gone through my code a few times and I am still stumped as to why this is happening. Any help would be greatly appreciated.

P.S. I am not a very experienced programmer, so some of this code may not be as efficient as possible and some things may be incorrect; feel free to point out any errors besides the issue at hand. Thanks!

Main Class

    public class Eat {

    public static void main(String[] args) {

        // Creating the main frame
        JFrame main = new JFrame("Eat 'Em All - Version 1.0.2");
        main.setSize(497, 599);
        main.setLocationRelativeTo(null);
        main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        main.setResizable(false);

        // Colours and borders
        Border areaBorder = new LineBorder(Color.LIGHT_GRAY, 3);

        // Creating main JPanel
        JPanel area = new JPanel();
        area.setLayout(new BoxLayout(area, BoxLayout.PAGE_AXIS));
        area.setBackground(Color.WHITE);
        main.setContentPane(area);

        // Creating the drawing/image/player
        DrawPlayer player = new DrawPlayer();
        player.setPreferredSize(new Dimension(497, 539));
        player.setOpaque(false);

        // Enemies
        DrawEnemy enemy = new DrawEnemy();
        enemy.setPreferredSize(new Dimension(497, 539));
        enemy.setBackground(Color.WHITE);

        // Creating the control panel for buttons, etc
        JPanel control = new JPanel();
        control.setPreferredSize(new Dimension(497, 60));
        control.setLayout(new GridLayout(1, 2, 0, 0));
        control.setBorder(areaBorder);

        JLabel welcome = new JLabel("  Welcome to Eat 'Em All   |--|   Press 'Start'");
        JButton start = new JButton("Start");

        // Adding it all to the frame
        main.add(enemy);
        enemy.add(player);
        control.add(welcome);
        control.add(start);
        area.add(control);

        // Adding keylistener and making button false
        player.addKeyListener(player);
        player.setFocusable(true);
        start.setFocusable(false);
        enemy.setFocusable(false);

        // Bring frame to front and visible
        main.toFront();
        main.setVisible(true);

        System.out.println(player.getWidth() / 2);
        System.out.println(player.getHeight() / 2);

    }

}

Drawing Player Class

    public class DrawPlayer extends JPanel implements KeyListener {

    long xPosition = 0;
    long yPosition = 0;

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        // Making loop to get points and move it
        // Center of area is x: 245 y: 255
        int xPoints[] = {235, 255, 255, 235, 235, 255};
        int yPoints[] = {265, 265, 245, 245, 265, 245};
        for (int i = 0; i < xPoints.length; i++) {
            xPoints[i] += xPosition;
            yPoints[i] += yPosition;
        }

        g.setColor(Color.BLUE);
        g.drawPolygon(xPoints, yPoints, xPoints.length);
    }

    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_DOWN:
                if (yPosition == 245) {
                    yPosition -= 5;
                } else {
                    yPosition += 5;
                }
                break;
            case KeyEvent.VK_UP:
                if (yPosition == -245) {
                    yPosition += 5;
                } else {
                    yPosition -= 5;
                }
                break;
            case KeyEvent.VK_LEFT:
                if (xPosition == -235) {
                    xPosition += 5;
                } else {
                    xPosition -= 5;
                }
                break;
            case KeyEvent.VK_RIGHT:
                if (xPosition == 235) {
                    xPosition -= 5;
                } else {
                    xPosition += 5;
                }
                break;
        }
        repaint();
    }

    public void keyReleased(KeyEvent e) {

    }

    public void keyTyped(KeyEvent e) {

    }
}

Drawing Enemies Class

public class DrawEnemy extends JPanel {

    public void paintComponent(Graphics f) {
        super.paintComponent(f);

        for (int i = 0; i < 10; i++ ){
            f.setColor(Color.RED);
            f.drawOval((int)(Math.random() * ((440 - 0) + 0) + 0), (int)(Math.random() * ((500 - 0) + 0) + 0), 50, 50);
        }
    }
}

Solution

  • Your have a problem here:

    public void paintComponent(Graphics f) {
        super.paintComponent(f);
    
        for (int i = 0; i < 10; i++ ){
            f.setColor(Color.RED);
            f.drawOval((int)(Math.random() * ((440 - 0) + 0) + 0), (int)(Math.random() * ((500 - 0) + 0) + 0), 50, 50);
        }
    }
    

    You've got program logic inside of a painting method, something you should never do, since you never have full control over when or even if a painting method will be called. The solution, get the randomization out of the paintComponent method and into its own separate method, one that you call if and only if you want to randomize the enemies, and not every time you repaint.

    Other issues:

    • Separate your program logic from your GUI.
    • For instance you should have a non-GUI Enemy class, one that has fields for its own position, its size, its movement, perhaps a move() method, perhaps a collision(Player p) method.
    • You should have only one JPanel that does drawing and this should be its only job.
    • Again, you don't tie movement of anything to the painting method.
    • You would want a game loop of some sort, perhaps a Swing Timer. This will generate regularly spaced ticks that will prod Enemies and Players to move.
    • Get rid of KeyListener code and favor Key Bindings. The latter is much less kludgy when it comes to component focus. Do check the tutorial for this.