Search code examples
javafxcollision-detection

Jafafx: looping collision detection and moving objects


I'm having a difficult time making the program loop moveBall and collisionDetection so that the program always checks if there is an collision and to move the balls.

At this moment the program is just checking for collision during the first iteration, after that, it doesn't do anything.

The ideal scenario is to get the balls to always sence if they're colliding or not.

I've included the whole Ball class and the code for the start-button. I've thought about doing a while loop around this little part of code but i never managed to get it working.

            for (Ball b : balls) {
                    b.moveBall();
                    b.collisionDetection(balls);
                }

Help & suggestions are appreciated!

Ball Class

package fx;

import java.util.ArrayList;

import javax.print.attribute.standard.MediaSize.Other;

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import javafx.util.Duration;



public class Ball {

    public int id;
    public Circle circle;
    public int team;

    public Ball(int x, int y, int _id, int _team) {

        id = _id;

        Circle ball = new Circle(10, Color.BLACK);
        ball.relocate(x, y);
        circle = ball;

        team = _team;
    }

        public void moveBall() {
            System.out.println(this.team);
                //team blue
                if (this.team == 0) {
                    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),
                              new KeyValue(this.circle.layoutXProperty(), 
                                      980-((Circle)this.circle).getRadius())));

                    timeline.setAutoReverse(true);
                    timeline.setCycleCount(Timeline.INDEFINITE);
                    timeline.play();

                }//team orange
                else if (this.team == 1) {
                    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),
                              new KeyValue(this.circle.layoutXProperty(), 
                                      35-((Circle)this.circle).getRadius())));
                    timeline.setAutoReverse(true);
                    timeline.setCycleCount(Timeline.INDEFINITE);
                    timeline.play();
                }       
        }

    public Circle getCircle() {
        return this.circle;
    }

    //collision detection

    public void collisionDetection(ArrayList < Ball > balls) {
        boolean collisionDetected = false;
            for (Ball ball : balls) {
                System.out.println(ball.id + " vs " + this.id);
                if (ball.id == this.id) {
                    collisionDetected = false;
                    continue;
                } else if (ball.id != this.id) {
                    Shape intersect = Shape.intersect(this.circle, ball.circle);
                    if (intersect.getBoundsInLocal().getWidth() != -1) {
                        collisionDetected = true;
                    }
                }
            }
            if (collisionDetected == true) {
                circle.setFill(Color.BROWN);
                System.out.println("colided");
            } else {

                collisionDetected = false;
            }
    }
}

GUI Class

startBtn.setOnAction(e ->{
    startBtn.setDisable(true);


    // Get text
    // Do a check. Gör typkontroll
    int ballCount = Integer.parseInt(NrInput.getText());




        Random r = new Random();
        int Low = 20;
        int High = 420;
        int id = 0;
        //System.out.println(bounds.getMaxX());

        for (int i = 0; i < ballCount; i++) {
            Ball ball;

            // Randomize Y
            int y = r.nextInt(High-Low) + Low;

            //every other ball gets in different teams
            if (i % 2 == 0) {
                ball = new Ball(35, y, id, 0);

            } else {
                ball = new Ball(965, y, id, 1);

            }
            balls.add(ball);
            canvas.getChildren().add(ball.getCircle());
            id = id + 1;

        }

            for (Ball b : balls) {
                    b.moveBall();
                    b.collisionDetection(balls);
                }

    startBtn.setDisable(false);
});

The program looks like this

But the balls that i've circled with red will not change color even though they will collide.


Solution

  • But the balls that i've circled with red will not change color even though they will collide.

    The reason is that the color doesn't change is because Ball.collisionDetection() is evaluated once, and it is evaluated when the collision hasn't occurred yet.

    You need to use a binding to make sure that this is re-evaluated whenever the balls are being moved by the Timeline.

    public Ball(int x, int y, int _id, int _team) {
        id = _id;
    
        Circle ball = new Circle(10, Color.BLACK);
        ball.relocate(x, y);
        circle = ball;
    
        team = _team;
    
    
        // New codes here
        ball.fillProperty().bind(Bindings.createObjectBinding(() -> {
            if (ball.checkCollision(balls)) {
                return Color.BROWN;
            }
            else {
                return Color.BLACK;
            }
        }, ball.layoutXProperty()));
    }
    
    // In Ball class
    public boolean checkCollision(List<Ball> allBalls) {
        // Your collision logic
    }
    

    You can move where you would want to put the binding at, since your collision logic probably require you to pass in a list of other Ball.