OK I have yet another homework problem. Again, help me by teaching, not writing my code for me. I have looked through the pages of threads on collision detection and found nothing to answer my question. My issue is that I have a collision detection method that should (in theory) work. I have a program that populates the screen with balls of random colors and sizes and they bounce around. There is also a button to add more balls to induce chaos. I am trying to make it so that when the balls collide, they will make a new ball so a collision calls the moreBalls method which adds another ball to the arraylist. I have put some checks in that will print strings to the terminal depending on the method as well. I think, and correct me if I'm wrong, that my for loop is comparing the p and r of the same bounding rectangle therefore it would either never collide or constantly collide. It gives no reason for me to believe that there is constant collision, though, due to my checks.
The code:

Ball Class:

package balls_lab5;

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

/**
 *
 * @author 24x24
 */
public class Ball {

    int ballxCoord;
    int ballyCoord;
    int ballHeight;
    int ballWidth;
    int ballRise;
    int ballRun;
    Color ballColor;
    BallPanel ballPanel;

    
    public Ball() {
        

        // Initialize Ball with random values
        
        ballxCoord = (int) (Math.random() * (50)); 
        ballyCoord = (int) (Math.random() * (50)); 
        ballHeight = (int) (10 + (int)(Math.random() * ((20 - 10) + 1))); 
        ballWidth = (int) (10 + (int)(Math.random() * ((20 - 10) + 1))); 
        ballRise = (int) (Math.random() * (10));   
        ballRun = (int) (Math.random() * (10));    
        
        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);
        Color randomColor = new Color(red, green, blue);
        
        ballColor = randomColor;
    }
 
    /**
        Paints the balls at their current positions within the panel.
     */
    
    public void paintComponent (Graphics g) {
       
        // Paint Ball
        g.setColor(ballColor);
        //g.setColor(Color.white);
        g.fillOval(ballxCoord, ballyCoord, ballWidth, ballHeight);
    }
    
    
    public void move (int pWidth, int pHeight){
      
        
        // Move Ball 
        // If ball is approaching a wall, reverse direction
        if (ballxCoord < (0 - ballRun) || ballxCoord > (pWidth - ballWidth)) { 
            ballRun = -ballRun; 
        }
        
	if (ballyCoord < (0 - ballRise) || ballyCoord > (pHeight - ballHeight)) { 
            ballRise = -ballRise; 
        }
        
        // "Move" ball according to values in rise and run
        ballxCoord += ballRun;
        ballyCoord += ballRise;
    }
    
    public void collision(){
           ballPanel.moreBalls();
             // A check to make sure balls collided
            System.out.println("Collision!");
           }

    public Color getBallColor() {
        return ballColor;
    }

    public void setBallColor(Color ballColor) {
        this.ballColor = ballColor;
    }

    public int getBallHeight() {
        return ballHeight;
    }

    public void setBallHeight(int ballHeight) {
        this.ballHeight = ballHeight;
    }

    public int getBallRise() {
        return ballRise;
    }

    public void setBallRise(int ballRise) {
        this.ballRise = ballRise;
    }

    public int getBallRun() {
        return ballRun;
    }

    public void setBallRun(int ballRun) {
        this.ballRun = ballRun;
    }

    public int getBallWidth() {
        return ballWidth;
    }

    public void setBallWidth(int ballWidth) {
        this.ballWidth = ballWidth;
    }

    public int getBallxCoord() {
        return ballxCoord;
    }

    public void setBallxCoord(int ballxCoord) {
        this.ballxCoord = ballxCoord;
    }

    public int getBallyCoord() {
        return ballyCoord;
    }

    public void setBallyCoord(int ballyCoord) {
        this.ballyCoord = ballyCoord;
    }
    
} // end of Ball class

BallPanel class:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package balls_lab5;

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


/**
 * @author 24x24
 */
/**
 * A panel containing two bouncing balls.  This panel may be placed in a JFrame.
 * @author ahanes
 */
public class BallPanel extends JPanel {

    ArrayList balls = new ArrayList();
    Ball ball;

    /** Creates a new instance of BallPanel */
    public BallPanel() {
        super();
        balls = new ArrayList();
         }
    
    public void moreBalls(){
            Ball ball = new Ball ();
            balls.add(ball);
            System.out.println(balls); // A check to make sure balls were added
            
           }
    
    public void detectCollision (){
        for (Object e : balls)
{
 
    Rectangle r = new Rectangle(((Ball)e).ballxCoord,((Ball)e).ballyCoord,((Ball)e).ballWidth,((Ball)e).ballHeight);
    Rectangle p = new Rectangle(((Ball)e).ballxCoord,((Ball)e).ballyCoord,((Ball)e).ballWidth,((Ball)e).ballHeight);

    if (r.intersects(p))
    {
       ball.collision(); // multiplies balls
        System.out.println("Intersection!"); // a check to make sure balls intersect
        
    }
}
    }
   
    
    public ArrayList addBalls(){
        
        String stringBalls = JOptionPane.showInputDialog("How many balls would you like?");
        int numBalls = Integer.parseInt( stringBalls );
        for (int i = 0; i < numBalls; i++){
            Ball ball = new Ball ();
            balls.add(ball);
            System.out.println(balls); // A check to make sure balls were added            
        }
        return balls;
    }

    /**
    Paints the panel and the balls.
     */
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        g.setColor(Color.black);                         // set color black
        g.fillRect(0, 0, this.getWidth(), this.getHeight()); // paint background

        for (int i = 0; i < balls.size(); i++){
            ((Ball)balls.get(i)).paintComponent(g);
        }
        
    } // end method paintComponent   

    /**
    Computes the next position for the balls and updates their positions.
     */
    public void move() {
        // Move Ball 
        // If ball is approaching a wall, reverse direction
       for (Object b : balls){
            ((Ball)b).move(getWidth(), getHeight());
            
        }

    } // end method move
    
       
}

The Test:

package balls_lab5;

import java.awt.*;       // Old library classes, you still need them
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;    // New library classes, Swing extends AWT

/**
 * Frame to hold a bouncing ball panel, implemented in the BallPanel class.
 * Controls the animation of the ball via pauses and calls to BallPanel's move and
 * paintComponent methods.
 * @author 24x24
 */
public class BallTest extends JFrame implements ActionListener {

    
    // size of the window
    private static final int WINDOW_WIDTH = 1000;
    private static final int WINDOW_HEIGHT = 500;
    ArrayList balls;
    // panel containing the bouncing ball
    private BallPanel ballPanel;

    /**
     * Pause command used to control the speed of the bouncing ball animation.
     * Currently pauses for 20 ms.  Use smaller values for faster animation and
     * vice versa.
     */
    public static void pause() {
        try {
            Thread.sleep(12); // pause for 20 ms
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
        }
    }

    /** Creates a new instance of BallTest */
    public BallTest() {
        super("Bouncing Ball");  // set frame name
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        setLayout(new BorderLayout());
        ballPanel = new BallPanel();
        add(ballPanel);
        center(this);
        setVisible(true);
        JButton moreBalls = new JButton ();
        moreBalls.setText("More Balls");
        moreBalls.addActionListener(this);
        add(moreBalls, BorderLayout.SOUTH);
        
         ballPanel.addBalls();
         
         // infinite animation loop, program halts when window is closed.
        while (true) {
            
            pause();
            ballPanel.move();
            ballPanel.repaint();

        }

    }
    
   
    /** Helper routine to center a frame on the screen (will cause problems if
    frame is bigger than the screen!)
     */
    public static void center(JFrame frame) {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Point center = ge.getCenterPoint();

        int w = frame.getWidth();
        int h = frame.getHeight();

        int x = center.x - w / 2, y = center.y - h / 2;
        frame.setBounds(x, y, w, h);
        frame.validate();
    }

    public static void main(String[] args) {
  BallTest t = new BallTest();

}

    @Override
    public void actionPerformed(ActionEvent e) {
       ballPanel.moreBalls();
    }
} // end method Main

Any help will be greatly appreciated. Not as important until its fixed but still also appreciated, if you see something I am doing backwards that could be improved let me know. We are always learning.

Recommended Answers

All 10 Replies

What are the symptoms you are getting? - you don't actually describe the problem anywhere.
Does your animation loop actually call the method to test for collisions after calling move?

I was going through my code and no, it never calls the collisionDetection method which would explain why I was getting neither an error or a printout of the check string. I put it in the game loop and I get:

Exception in thread "main" java.lang.NullPointerException
[balls_lab5.Ball@26d66426, balls_lab5.Ball@3f64b09c, balls_lab5.Ball@6b86f247, balls_lab5.Ball@688c4a77, balls_lab5.Ball@526d0040, balls_lab5.Ball@722b9406]
[balls_lab5.Ball@26d66426, balls_lab5.Ball@3f64b09c, balls_lab5.Ball@6b86f247, balls_lab5.Ball@688c4a77, balls_lab5.Ball@526d0040, balls_lab5.Ball@722b9406, balls_lab5.Ball@b035079]
at balls_lab5.BallPanel.detectCollision(BallPanel.java:57)
at balls_lab5.BallTest.<init>(BallTest.java:72)
at balls_lab5.BallTest.main(BallTest.java:103)

No balls show up on screen but they are in the array. When I comment out the ball.collision it prints Intersection constantly and balls are painted on screen so I am assuming that my initial thought was correct in thinking that it might be reading the intersection of each ball? THat or I may have left something critical out. I put the call in the while loop of BallTest right before ballPanel.move. I'll keep working on it but thanks for any further help.

1/ your problem is increasing ball by each collision, then lots of collisions produce new and new and new balls, really you overkill JVM,

2/ output is because you have to loop inside Array, not pure system.o...(myArray)

I forgot about that, check (for inspirations) this blog (really good man, as some Gurus from this Java Forum) http://sites.google.com/site/drjohnbmatthews/kineticmodel,

and I check your code, no NPE, just stop repaint at amount of 8000-10000 balls (but here is probably difference between your and my PC and Java version)

Well I commented out the moreBalls method call in the collision detection and ran only one ball and it produced a nonstop output of Intersection printed to the console. Also I had it printing the array inside of the loop as a check. I still think it is my comparison making going against itself but I don't know how to fix that.

I still think it is my comparison making going against itself but I don't know how to fix that.

Yes, that's the problem.
You loop thru all the balls taking one at a time and comparing it with... itself. (Rectangles r and p are always identical.)
You need to compare two different balls, which should instinctively suggest that you need two loops.
Remember also that if you've compared 1 with 2, the there's no need to compare 2 with 1.
So the comparisons you need to do look like this:
1 vs 2, 1 vs 3, 1 vs 4 ... 1 vs n
2 vs 3, 2 vs 4 ... 2 vs n
...
n-1 vs n
I'll leave you to think about how to code those loops.

Ok I revised detectCollision. It works, kind of. If the user enters 1 or 2 balls it crashes and it points to the r.intersects(p). Also it seems like it should intersect a lot more than it is. I entered 40 balls and it only intersected 11 times in 6 minutes. This may just be me misunderstanding the meaning of .intersect but it looks as though the balls cross paths every couple milliseconds at that rate. Here is the new collisionDetection method:

public void detectCollision() {
        Rectangle p = null;
        Rectangle r = null;
        for (Object e : balls) {

             r = new Rectangle(((Ball) e).ballxCoord, ((Ball) e).ballyCoord, ((Ball) e).ballWidth, ((Ball) e).ballHeight);
           for (int j = 1; j < balls.size()-1; j++){
              Object k =  balls.get(j); 
             p = new Rectangle(((Ball) k).ballxCoord, ((Ball) k).ballyCoord, ((Ball) k).ballWidth, ((Ball) k).ballHeight);

        
           

            }    }
         if (r.intersects(p)) {
                
                moreBalls();
         
                System.out.println("Intersection!"); // a check to make sure balls intersect
        }
    }

You're checking for intersection outside both of your loops - which means they are essentially doing nothing except adding garbage collection overhead.

Also, why not just put a boolean intersects(Ball) method on the ball class itself and check for intersection with another ball that is passed as a parameter to the method?

Ok so I fixed my loops and put the intersection check back inside of them. I like the idea of a boolean Ezzaral. I may look into that to simplify once I turn this in. Here is the new detectCollision method:

public void detectCollision() {

        for (int i = 0; i < balls.size() - 1; i++) {

            for (int j = i + 1; j < balls.size(); j++) {
                Object k = (Ball)balls.get(j);
                Object e = (Ball)balls.get(i);
                Rectangle p = new Rectangle(((Ball) k).ballxCoord, ((Ball) k).ballyCoord, ((Ball) k).ballWidth, ((Ball) k).ballHeight);
                Rectangle r = new Rectangle(((Ball) e).ballxCoord, ((Ball) e).ballyCoord, ((Ball) e).ballWidth, ((Ball) e).ballHeight);

                if (p.intersects(r)) {

                    //moreBalls();
                    balls.remove(e);

It looked like it was working by printing Intersection to the terminal and printing an addition to the array but it wasn't painting and it was intersecting a lot. After quite a lot of debugging and some brainstorming with a co-worker we decided that it was an issue with creating moreBalls. Commenting that call out proved the theory somewhat. I changed the collision effect to remove the ball that collided and now it works perfectly. I think the issue was that in calling moreBalls I was essentially creating a very bad loop. It would create the balls initially with addBalls and they would come out from top left. As soon as they came out they would collide. In colliding they would create more balls which would then collide with them as well creating yet another stream of balls. I think this was where my problem was with it not painting because my pc couldn't paint them fast enough. Anyway, I thank all of you for your help. I couldn't do half of the stuff I can without you. I'm sure I'll be posting again soon.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.