0

I'm trying to use multithreading to draw balls that bounce around inside a JFrame. I can get the coordinates of each ball to update and print out, but I can't get the balls to display. I'm not very strong in graphics, and I'm not quite sure what I'm missing. I think I need to add each instance of Ball to the panel I have inside my frame, but when I tried that it didn't make a difference. I also have a class used to view the JFrame, that I've omitted. What am I missing here?

Ball

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JPanel;

public class Ball extends JPanel implements Runnable  {
JPanel pan;
private static int radius = 10;
private Color color;
private int xPos;
private int yPos;
private int dx;
private int dy;
Dimension d;

public Ball(JPanel p) {
    Random r = new Random();
    this.pan = p;
    this.d = pan.getSize();
    xPos = r.nextInt(d.width-50)+25;
    yPos = r.nextInt(d.height-50)+25;
    dx = r.nextInt(3)+1;
    dy = r.nextInt(3)+1;
    color = new Color(r.nextInt(255*255*255));
    paintComponent(pan.getGraphics());
}

public void move() {
    xPos += dx;
    yPos += dy;
    if (xPos+radius <= 0 || xPos+radius >= d.width)
        dx = -dx;
    if (yPos+radius <= 0 || yPos+radius >= d.height)
        dy = -dy;
}

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setColor(color);
    g.fillOval(xPos-radius, yPos-radius, 2*radius, 2*radius);
    g.dispose();
}

public void animate() {
    paintComponent(pan.getGraphics());
    move();
    //pan.validate();//this didn't
    //pan.repaint();// work
    try {
        Thread.sleep(40);
    } catch (InterruptedException e) {}
}

public void run() {
    while(true)
        animate();
}
}

BallTracker

import java.util.ArrayList;

public class BallTracker {
private ArrayList<Ball> balls;

public BallTracker() {
    balls = new ArrayList<Ball>();
}

public void addBall(Ball b) {
    balls.add(b);
    Thread t = new Thread(b);
    t.start();
}
}

BallFrame

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class BallFrame extends JFrame {
public static final int WIDTH = 500;
public static final int HEIGHT = 550;
private BallTracker tracker;
private JPanel ballPanel;
private JPanel buttonPanel;

public BallFrame() {
    super("BallFrame");
    tracker = new BallTracker();

    // set up ball panel
    ballPanel = new JPanel();
    ballPanel.setSize(WIDTH, 500);

    // listener to add a new ball to tracker
    class bListener implements ActionListener {
        public void actionPerformed( ActionEvent event ) {
            Ball b = new Ball(ballPanel);
            tracker.addBall(b);

        }
    }

    // set up button panel
    buttonPanel = new JPanel();
    buttonPanel.setSize(WIDTH, 50);
    JButton addBallButton = new JButton();
    addBallButton.setText("Add ball");
    addBallButton.addActionListener(new bListener());
    buttonPanel.add(addBallButton);

    // add panels to frame
    add(buttonPanel, BorderLayout.SOUTH);
    add(ballPanel, BorderLayout.CENTER);
    setSize( WIDTH, HEIGHT );
}
}
  • 3
    You should be using a single thread as the animation engine, looping through the collection balls and updating them within a single thread. Because repaints can occur out side of your control, you should also be protecting you model values while they are been updated or painted – MadProgrammer May 19 '13 at 22:07
  • Examples [here](http://stackoverflow.com/questions/14593678/multiple-bouncing-balls-thread-issue/14593761#14593761) and [here](http://stackoverflow.com/questions/13022754/java-bouncing-ball/13022788#13022788) – MadProgrammer May 19 '13 at 22:10
  • Alternatives [here](http://stackoverflow.com/questions/15078835/i-am-trying-to-make-ball-gradually-move/15079240#15079240) and [here](http://stackoverflow.com/questions/15352969/drawing-2-balls-to-move-in-different-direction-on-java-but-one-disappeared/15353052#15353052), using javax.swing.Timer instead of threads – MadProgrammer May 19 '13 at 22:12
  • @MadProgrammer I am doing this as an exercise in multithreading. I was mainly looking for issues with the way I am trying to draw here, and why I don't see a ball. – user2313498 May 19 '13 at 22:31
  • 1
    That's all well and good, but the design is wrong. The example is unscalable in it's current form. Swing and multithreads need careful design considerations which are compounding. But, it's your choice – MadProgrammer May 20 '13 at 00:02
  • the more fequent you redraw the nicer it will look. And don't pause for every ball, have a uniform update of all balls on the panel at one time. that's why a timer that repeats is good here. have it go off and draw all balls every 100-200 ms. well it just calls repaint. your paintComponents ( which you should use over paint) can loop through the ball array and draw all balls. – LanternMike May 20 '13 at 13:20

2 Answers2

1

It seems your ball extends jpanel and has a paint method, but your ballPanel would need to do the painting and your ball doesn't really seem to need to be a panel at all.

set up ball panel
ballPanel = new JPanel();
ballPanel.setSize(WIDTH, 500);
LanternMike
  • 664
  • 1
  • 5
  • 16
1

I was mainly looking for issues with the way I am trying to draw here, and why I don't see a ball

  1. The Ball isn't added to any panel.
  2. Even when you do add the ball to the panel, the Ball has no size, so there is nothing to paint.
  3. Even if you did give the panel a size only one Ball would ever show up because you Ball panel is opaque.
  4. Your code is attempting to paint the ball at a location within the ball panel. Instead you should be painting the ball at location (0, 0) within your ball panel and then set the location of the Ball panel relative to the parent container.
  5. The parent container should be using a null layout so you can randomly set the location of your Ball panel.

I'm sure there are other issues as well...

I suggest you forget about multithreading and start with the basics of custom painting and using Timers.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • I took your thoughts into consideration, all I had to do was pass the ballPanel to my Ball constructor, and use the panel to get the graphics context/dimensions. It works now, the only issue I have is streaking, basically the old position of the ball still exists. I tried to erase it by drawing a white oval, incrementing dx/dy and drawing a colored oval, but this causes the balls to flash. Is there any way to clear the panel without clearing every ball? – user2313498 May 19 '13 at 23:26
  • Knew there were other issues. Forget to mention custom painting is done in the `paintComponent()` method, not the paint() method and you need to invoke super.paintComponent(). – camickr May 20 '13 at 00:35
  • Yes I did fix that actually, but it still causes the balls to streak. updated my code. – user2313498 May 20 '13 at 02:31