1

I'm messing around with graphics in Java (specifically through the Swing and AWT libs), and want to move a circle across the screen (making lines) and set the x or y coordinate back to 0 when the line hits the bounds of the canvas. I have no problem with this part of the code.

However, now I'm trying to clear the screen when the coordinates are both 0 (using super.paint(Graphics)) and change the color. The screen clearing works fine, but I can't change the color. It seems, when the coordinates are both 0, an oval of the specified color appears under the streak being made at the (0, 0) position. I, however, want the color of the streak to change color until the coordinates are reset to (0, 0).

Here's my code:

@SuppressWarnings("serial")
public class Game extends JPanel
{
    static final int SCREEN_X = 300;
    static final int SCREEN_Y = 400;
    int x = 0;
    int y = 0;

    private void moveBall()
    {
        x += 1;
        y += 1;

        if (x >= SCREEN_X) x = 0;
        if (y >= SCREEN_Y) y = 0;
    }

    @Override
    public void paint(Graphics g)
    {
        Graphics2D g2d = (Graphics2D) g;

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        if (x == 0 && y == 0)
        {
            super.paint(g);

            // Generate random value in [0, 5)
            int rand_val = (new Random()).nextInt(5);

            // Init random color variable
            Color rand_color = Color.black;

            // Pick a random color
            switch (rand_val)
            {
            case 0:
                rand_color = Color.black;
                break;

            case 1:
                rand_color = Color.red;
                break;

            case 2:
                rand_color = Color.green;
                break;

            case 3:
                rand_color = Color.blue;
                break;

            case 4:
                rand_color = Color.gray;
                break;
            }

            // I'm assuming I need to do something more here?
            // This is likely where my mistake is...
            g2d.setColor(rand_color);
        }

        g2d.fillOval(x, y, 30, 30);
    }

    public static void main(String[] args) throws InterruptedException
    {
        JFrame frame = new JFrame("Mini Tennis");
        Game game = new Game();

        frame.add(game);
        frame.setSize(SCREEN_X, SCREEN_Y);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        while (true)
        {
            game.moveBall();
            game.repaint();
            Thread.sleep(10);
        }
    }
}
Drake Johnson
  • 640
  • 3
  • 19
  • That above example goes about animation(1) & custom painting incorrectly. I would advise amending it to the tried & tested approaches before focusing on anything else. 1) Animation should be invoked from a Swing `Timer` so the EDT does not become blocked. 2) In all **cases** using custom painting, the one constant should be a call to the `super` method. No `if`s or buts about it. – Andrew Thompson Dec 20 '19 at 01:52
  • @AndrewThompson The code comes from a game tutorial, so the code is not specific to animation. It is beginning to set up a game. I was simply playing around with it before going on to the next step. – Drake Johnson Dec 20 '19 at 01:54
  • *"The code comes from a game tutorial.."* GI/GO *"I was simply playing around with it before going on to the next step."* Your next step should be to toss this dross out and start again with code obtained from the Java Tutorial. – Andrew Thompson Dec 20 '19 at 01:58
  • A painting method should NOT change the state of the component. It should only paint the current state of the component. See: https://stackoverflow.com/questions/54028090/get-width-and-height-of-jpanel-outside-of-the-class/54028681#54028681 for a working example that animates multiple "ball" object using a Swing Timer. – camickr Dec 20 '19 at 03:27

2 Answers2

1

Note the changes and comments:

public class Game extends JPanel
{
    static final int SCREEN_X = 300;
    static final int SCREEN_Y = 400;
    int x = 0;
    int y = 0;

    private void moveBall()
    {
        x += 1;
        y += 1;

        if (x >= SCREEN_X) {
            x = 0;
        }
        if (y >= SCREEN_Y) {
            y = 0;
        }
    }

    @Override
    public void paintComponent(Graphics g) { //for custom painting override paint componenet

        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        if (x == 0 && y == 0)
        {
            // Generate random value in [0, 5)
            int rand_val = new Random().nextInt(5);

            // Init random color variable
            Color rand_color = Color.black;

            // Pick a random color
            switch (rand_val)
            {
            case 0:
                rand_color = Color.black;
                break;

            case 1:
                rand_color = Color.red;
                break;

            case 2:
                rand_color = Color.green;
                break;

            case 3:
                rand_color = Color.blue;
                break;

            case 4:
                rand_color = Color.gray;
                break;
            }

            g2d.setColor(rand_color);
            g2d.fillRect(0, 0, getWidth(), getHeight()); //clear screen 
        }

        g2d.fillOval(x, y, 30, 30);
    }


Side note: you can define a colors array:
private final Color[] colors = {Color.black, Color.red, Color.green, Color.blue, Color.gray};

To make the code more concise:

@Override
public void paintComponent(Graphics g) { //for custom painting override paint componenet

    Graphics2D g2d = (Graphics2D) g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    if (x == 0 && y == 0)
    {
        int rand_val = new Random().nextInt(colors.length);
        Color rand_color = colors[rand_val];
        g2d.setColor(rand_color);
        g2d.fillRect(0, 0, getWidth(), getHeight()); //clear screen
    }

    g2d.fillOval(x, y, 30, 30);
}
c0der
  • 18,467
  • 6
  • 33
  • 65
0
// I'm assuming I need to do something more here?
// This is likely where my mistake is...
g2d.setColor(rand_color);

The Graphics2D instance used for this paint is on the verge of being disposed. No good changing the color now.

Instead, change the BG or FG color of the component (the JPanel) itself, then set the color of the graphics object before each paint.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433