Zombievirus
Zombievirus

Reputation: 185

Change Color of bouncing Ball in a running Thread

I am new to Java. I started to learn by creating a bouncing ball GUI.

I have managed to do all i wanted to do with it except one thing. I can't get the ball to change its color whenever it bounces off the Frame Borders.

I know i can't use getGraphics for drawing but in this case i can also not use the paint Method as i already use it for the initial settings at the beginning of the Program. If i call the paint method from within the loop, it does change the color but all the JButtons in the North Side of the Layout mysteriously vanish.

The one Thing that makes the whole thing even more complicated for me is the fact that i am looping the ball bounce in a void run method of the Runnable Interface.

I couldn't figure out how to solve it elsewise.

You Guys can copy my code in your IDE. The one thing that doesn't work is that the ball remains black and doesn't changes it'S color. Everything else is running fine.

So Please help me figure out how i can change the ball's color whenever it bounces.

Thanks

Code:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Ball extends JFrame implements Runnable, ActionListener {

    private JButton start;
    private JButton stop;
    private JButton fast;
    private JButton slow;
    private JButton reset;
    private int ball_x;
    private int ball_y;
    private int dx = 10;
    private int dy = 20;
    private int size = 50;
    private boolean active;
    private int i = 20;
    private int R = 255;
    private int G = 0;
    private int B = 255;
    private Color rgb;


    private MyPanel screen;

    private class MyPanel extends JPanel {
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            ball_x = screen.getWidth() / 2 - 25;
            ball_y = screen.getHeight() / 2 - 25;
            i = 20;
            g.setColor(rgb);
            g.fillOval(ball_x, ball_y, size, size);
        }

        public void clr() {

            if (R <= 10) {
                R = 255;
            }
            R = R - 10;


            if (G >= 255) {
                G = 0;
            }

            G = G + 15;


            if (B <= 20) {
                B = 255;
            }

            B = B - 20;

        }
    }


    public void start() {
        active = true;
        start.setText("Start");
        start.setEnabled(false);
        start.setText("Jumping...");
        Thread th = new Thread(this); // Thread anlegen
        th.start(); // Thread starten
    }

    public void stop() {
        if (active) {
            start.setText("Continue");
        }
        active = false;
        start.setEnabled(true);
    }

    public void reset() {
        active = false;
        size = 50;
        screen.repaint();
        start.setText("Start");
        start.setEnabled(true);
    }

    public void slow() {
        i = i + 2;
        if (i >= 60) {
            i = 60;
        }
    }

    public void fast() {
        i = i - 2;
        if (i <= 5) {
            i = 5;
        }
    }

    public void actionPerformed(ActionEvent ereignis) {

        if (ereignis.getSource() == start) {
            start();
        }

        if (ereignis.getActionCommand() == "G+") {
            fast();
        }

        if (ereignis.getActionCommand() == "G-") {
            slow();
        }

        if (ereignis.getActionCommand() == "Reset") {
            reset();
        }

        if (ereignis.getActionCommand() == "Pause") {
            stop();
        }

    }

    public Ball() {

        this.setTitle("Jumping Ball \u00a9 The One");
        Container panel = new Container();
        panel.setLayout(new BorderLayout());
        JPanel subpanel = new JPanel();
        //subpanel.setLayout(new GridLayout());
        screen = new MyPanel();
        this.setVisible(true);
        this.setSize(1366, 768);
        this.setContentPane(panel);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        start = new JButton("Start");
        fast = new JButton("G+");
        slow = new JButton("G-");
        reset = new JButton("Reset");
        stop = new JButton("Pause");
        subpanel.add(start);
        subpanel.add(fast);
        subpanel.add(slow);
        subpanel.add(reset);
        subpanel.add(stop);
        panel.add(subpanel, "North");
        panel.add(screen, "Center");
        start.addActionListener(this);
        stop.addActionListener(this);
        reset.addActionListener(this);
        slow.addActionListener(this);
        fast.addActionListener(this);

    }

    public void run() {

        while (active) {
            screen.getGraphics().clearRect(ball_x, ball_y, size, size);

            if (ball_x > (screen.getWidth() - 30) || ball_x < 25) {
                dx = -dx;
                size = size + 3;
                screen.clr();

            }

            if (ball_y > (screen.getHeight() - 30) || ball_y < 25) {
                dy = -dy;
                size = size + 3;
                screen.clr();

            }

            ball_x = ball_x + dx;
            ball_y = ball_y + dy;

            rgb = new Color(R, G, B);

            screen.getGraphics().setColor(rgb);
            screen.getGraphics().fillOval(ball_x, ball_y, size, size);
            try {
                Thread.sleep(i);
            } catch (InterruptedException x) {
                x.printStackTrace();
            }
        }

    }


    public static void main(String[] args) {
        Ball bouncer = new Ball();

    } // End of Method Main


}

Thank You

Upvotes: 1

Views: 2753

Answers (2)

Zombievirus
Zombievirus

Reputation: 185

Well i got it to work but i don't think this is the best way to do it! I had to ditch Threads and go for Timer.

I have been reading everywhere that one shouldn't mix AWT and Swing but i don't see any other possibility than panel.setLayout(new BorderLayout()); or public void paintComponent(Graphics g)

This is obviously mixing AWT and Swing Components but i didn't find any pure Swing way to do it.

Anyways this is my entire Code:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Ball extends JFrame implements ActionListener {

    private JButton start;
    private JButton stop;
    private JButton fast;
    private JButton slow;
    private JButton reset;
    private int ball_x;
    private int ball_y;
    private int dx = 10;
    private int dy = 20;
    private boolean active;
    private int i = 30;
    private int R = 255;
    private int G = 255;
    private int B = 0;
    private Color rgb;
    private Timer trigger = new Timer(i, this);

    private MyPanel screen;

    private class MyPanel extends JPanel {

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (start.getText() == "Start") {
                ball_x = screen.getWidth() / 2 - 50;
                ball_y = screen.getHeight() / 2 - 50;
            }

            if (start.getText() == "Jumping...") {
                g.clearRect(ball_x, ball_y, 100, 100);
            }

            ball_x = ball_x + dx;
            ball_y = ball_y + dy;

            rgb = new Color(R, G, B);
            g.setColor(rgb);

            g.fillOval(ball_x, ball_y, 100, 100);

            if (ball_x > (screen.getWidth() - 100) || ball_x < 10) {
                dx = -dx;
                screen.clr();
            }

            if (ball_y > (screen.getHeight() - 100) || ball_y < 10) {
                dy = -dy;
                screen.clr();
            }

        }

        public void clr() {

            if (R <= 10) {
                R = 255;
            }
            R = R - 10;

            if (G <= 20) {
                G = 255;
            }

            G = G - 20;

            if (B >= 255) {
                B = 0;

            }

            B = B + 15;

        }
    }

    public void start() {

        active = true;
        start.setText("Start");
        start.setEnabled(false);
        start.setText("Jumping...");
        trigger.start();
        screen.repaint();

    }

    public void stop() {
        if (active) {
            start.setText("Continue");
        }
        active = false;
        start.setEnabled(true);
        trigger.stop();

    }

    public void reset() {
        active = false;
        start.setText("Start");
        start.setEnabled(true);
        i = 100;
        trigger.stop();
        screen.repaint();
    }

    public void slow() {
        i = i + 10;
        if (i >= 100) {
            i = 100;
        }

        trigger.setDelay(i);
    }

    public void fast() {
        i = i - 10;
        if (i <= 10) {
            i = 10;
        }
        trigger.setDelay(i);
    }

    public void actionPerformed(ActionEvent ereignis) {

        if ((ereignis.getSource() == start) || (trigger.isRunning() == true)) {
            start();
        }

        if (ereignis.getActionCommand() == "G+") {
            fast();
        }

        if (ereignis.getActionCommand() == "G-") {
            slow();
        }

        if (ereignis.getActionCommand() == "Reset") {
            reset();
        }

        if (ereignis.getActionCommand() == "Pause") {
            stop();
        }

    }

    public Ball() {

        this.setTitle("Jumping Ball \u00a9 The One");
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        this.setContentPane(panel);
        JPanel subpanel = new JPanel();
        panel.add(subpanel, "North");
        screen = new MyPanel();
        screen.setBackground(Color.WHITE);
        panel.add(screen, "Center");
        start = new JButton("Start");
        fast = new JButton("G+");
        slow = new JButton("G-");
        reset = new JButton("Reset");
        stop = new JButton("Pause");
        subpanel.add(start);
        subpanel.add(fast);
        subpanel.add(slow);
        subpanel.add(reset);
        subpanel.add(stop);
        start.addActionListener(this);
        stop.addActionListener(this);
        reset.addActionListener(this);
        slow.addActionListener(this);
        fast.addActionListener(this);
        this.setSize(1280, 720);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        Ball fenster = new Ball();

    } // Ende der Methode Main
}

I know this is not the professional way to do it but this is the best i could do and it does what i wanted it to do.

I will be more than happy if someone could point out some absolute no-go's in my Code.

Thanx

Upvotes: 1

trashgod
trashgod

Reputation: 205825

While complete, your example has several critical problems:

  • It is incorrectly synchronized.

  • It calls setVisible() prematurely.

  • It relies on the frame's geometry, rather than an enclosed panel.

Instead, start with the example cited here using a Swing Timer. Note that the color changes with each repaint(), and a sound is played with each bounce. Add a member variable, bounced.

private boolean bounced;

When the ball bounces, set bounced = true in the actionPerformed() method of the Timer.

if (ballX - RADIUS < 0) {
    …
    bounced = true;
} else if (ballX + RADIUS > BOX_WIDTH) {
    …
    bounced = true;
}

In paintComponent(), change the color accordingly.

g.setColor(clut.peek());
if (bounced) {
    clut.add(clut.remove());
    bounced = false;
}

It may be easier to see the change with a smaller clut.

private static final float N = 16;

Upvotes: 3

Related Questions