Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
343 views
in Technique[技术] by (71.8m points)

java - Multiple bouncing balls thread issue

I created a program that makes multiple bouncing balls with random color, speed and radius. When user clicks on the screen a new random ball should appear and move around screen. But i have a multi-thread issue. When i click on the screen a ball appears and doesn't moving at all. When another click comes nothing happens.

BouncingBalls Class

public class BouncingBalls extends JPanel implements MouseListener{

private Ball ball;
protected List<Ball> balls = new ArrayList<Ball>(20);
private Container container;
private DrawCanvas canvas;
private int canvasWidth;
private int canvasHeight;
public static final int UPDATE_RATE = 30;

int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int count = 0;

public static int random(int maxRange) {
    return (int) Math.round((Math.random() * maxRange));
}

public BouncingBalls(int width, int height){

    canvasWidth = width;
    canvasHeight = height;

    ball = new Ball(x, y, speedX, speedY, radius, red, green, blue);
    container = new Container();

    canvas = new DrawCanvas();
    this.setLayout(new BorderLayout());
    this.add(canvas, BorderLayout.CENTER);
    this.addMouseListener(this);

}

public void start(){

    Thread t = new Thread(){

        public void run(){

            while(true){

                update();
                repaint();
                try {
                    Thread.sleep(1000 / UPDATE_RATE);
                } catch (InterruptedException e) {}
            }
        }
    };
    t.start();
}

public void update(){

    ball.move(container);
}

class DrawCanvas extends JPanel{

    public void paintComponent(Graphics g){

        super.paintComponent(g);
        container.draw(g);
        ball.draw(g);
    }

    public Dimension getPreferredSize(){

        return(new Dimension(canvasWidth, canvasHeight));
    }
}

public static void main(String[] args){

    javax.swing.SwingUtilities.invokeLater(new Runnable(){

        public void run(){

            JFrame f = new JFrame("Bouncing Balls");
            f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
            f.setContentPane(new BouncingBalls(500, 500));
            f.pack();
            f.setVisible(true);
        }
    });
}

@Override
public void mouseClicked(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void mouseEntered(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void mouseExited(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void mousePressed(MouseEvent e) {

    count++;
    balls.add(new Ball(x, y, speedX, speedY, radius, red, green, blue));
    balls.get(count-1).start();
    start();
}

@Override
public void mouseReleased(MouseEvent e) {
    // TODO Auto-generated method stub

}
}

Ball Class

import java.awt.Color;
import java.awt.Graphics;

public class Ball{

public static int random(int maxRange) {
    return (int) Math.round((Math.random() * maxRange));
}

private BouncingBalls balls;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int i = 0;

public Ball(int x, int y, int speedX, int speedY, int radius, int red, int green, int blue){

    this.x = x;
    this.y = y;
    this.speedX = speedX;
    this.speedY = speedY;
    this.radius = radius;
    this.red = red;
    this.green = green;
    this.blue = blue;
}

public void draw(Graphics g){

    for(Ball ball : balls){

        g.setColor(new Color(red, green, blue));
        g.fillOval((int)(x - radius), (int)(y - radius), (int)(2 * radius), (int)(2 * radius));
    }
}

public void move(Container container){

    x += speedX;
    y += speedY;

    if(x - radius < 0){

        speedX = -speedX;
        x = radius;
    }
    else if(x + radius > 500){

        speedX = -speedX;
        x = 500 - radius;
    }

    if(y - radius < 0){

        speedY = -speedY;
        y = radius;
    }
    else if(y + radius > 500){

        speedY = -speedY;
        y = 500 - radius;
    }
}
}

Container Class

import java.awt.Color;
import java.awt.Graphics;

public class Container {

private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;

public void draw(Graphics g){

    g.setColor(COLOR);
    g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You're maintaing two different references to your ball.

You have a reference to a single Ball called ball and a List of balls. Your update and paint methods only reference the single ball

Ball doesn't seem to have a start method (that I can see) so this balls.get(count-1).start(); doesn't make sense...

Updated

  • You don't need the reference to ball
  • While not a bad idea, while testing, you should probably call start in the constructor
  • Your update method in BouncingBalls should looping through the balls list, calling move on each ball in the list...
  • The paintComponent method of DrawCanvas needs access to and should make use of the balls list. This might be better achievable through a model interface
  • Do not construct a new Ball with parameters, as it's giving each ball the same properties, especially when you assign random values to it when you construct it any way...
  • Ball doesn't have (or need) a start method

enter image description here

public class BouncingBalls extends JPanel implements MouseListener {

//    private Ball ball;
    protected List<Ball> balls = new ArrayList<Ball>(20);
    private Container container;
    private DrawCanvas canvas;
    private int canvasWidth;
    private int canvasHeight;
    public static final int UPDATE_RATE = 30;
    int x = random(480);
    int y = random(480);
    int speedX = random(30);
    int speedY = random(30);
    int radius = random(20);
    int red = random(255);
    int green = random(255);
    int blue = random(255);
    int count = 0;

    public static int random(int maxRange) {
        return (int) Math.round((Math.random() * maxRange));
    }

    public BouncingBalls(int width, int height) {

        canvasWidth = width;
        canvasHeight = height;

//        ball = new Ball(x, y, speedX, speedY, radius, red, green, blue);
        container = new Container();

        canvas = new DrawCanvas();
        this.setLayout(new BorderLayout());
        this.add(canvas, BorderLayout.CENTER);
        this.addMouseListener(this);

        start();

    }

    public void start() {

        Thread t = new Thread() {
            public void run() {

                while (true) {

                    update();
                    repaint();
                    try {
                        Thread.sleep(1000 / UPDATE_RATE);
                    } catch (InterruptedException e) {
                    }
                }
            }
        };
        t.start();
    }

    public void update() {
        for (Ball ball : balls) {
            ball.move(container);
        }
    }

    class DrawCanvas extends JPanel {

        public void paintComponent(Graphics g) {

            super.paintComponent(g);
            container.draw(g);
            for (Ball ball : balls) {
                ball.draw(g);
            }
//            ball.draw(g);
        }

        public Dimension getPreferredSize() {

            return (new Dimension(canvasWidth, canvasHeight));
        }
    }

    public static void main(String[] args) {

        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame f = new JFrame("Bouncing Balls");
                f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
                f.setContentPane(new BouncingBalls(500, 500));
                f.pack();
                f.setVisible(true);
            }
        });
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void mouseExited(MouseEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void mousePressed(MouseEvent e) {

        count++;
        balls.add(new Ball());
//        balls.add(new Ball(x, y, speedX, speedY, radius, red, green, blue));
//        balls.get(count - 1).start();
//        start();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        // TODO Auto-generated method stub
    }

    public static class Ball {

        public int random(int maxRange) {
            return (int) Math.round(Math.random() * maxRange);
        }
        int x = random(480);
        int y = random(480);
        int speedX = random(30);
        int speedY = random(30);
        int radius = random(20);
        int red = random(255);
        int green = random(255);
        int blue = random(255);
        int i = 0;

        public Ball() { //int x, int y, int speedX, int speedY, int radius, int red, int green, int blue) {

//            this.x = x;
//            this.y = y;
//            this.speedX = speedX;
//            this.speedY = speedY;
//            this.radius = radius;
//            this.red = red;
//            this.green = green;
//            this.blue = blue;
        }

        public void draw(Graphics g) {

            g.setColor(new Color(red, green, blue));
            g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));

        }

        public void move(Container container) {

            x += speedX;
            y += speedY;

            if (x - radius < 0) {

                speedX = -speedX;
                x = radius;
            } else if (x + radius > 500) {

                speedX = -speedX;
                x = 500 - radius;
            }

            if (y - radius < 0) {

                speedY = -speedY;
                y = radius;
            } else if (y + radius > 500) {

                speedY = -speedY;
                y = 500 - radius;
            }
        }
    }

    public static class Container {

        private static final int HEIGHT = 500;
        private static final int WIDTH = 500;
        private static final Color COLOR = Color.WHITE;

        public void draw(Graphics g) {

            g.setColor(COLOR);
            g.fillRect(0, 0, WIDTH, HEIGHT);
        }
    }
}

Updated

As pointed out by the commentators, ArrayList is not thread safe, it's not a good idea to have multiple threads trying to access it simultaneously. While adding is slightly safer then removing, it is still bad practice.

You can either replace ArrayList with Vector, which would be the simpler solution, or synchronize the access to the list around a common monitor lock. Given your example, I'd use a java.util.Vector


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...