Search code examples
javafxborderbounce

Java FX bounce ball bounces out of box


I am new to JavaFX and I have (hopefully) a simple question that maybe someone can help me out with. I have tried looking for answers online and nothing seems to fit in with what I have.

So basically what I am trying to do is have a ball (in my case its a face) bounce around a pane changing direction whenever it hits the wall - simple enough. I can't seem to figure out why the walls only triggers the left side and top side of the face. (In other words, the Left and top walls trigger the bounce correctly, but the bottom and right walls allow the pane to go through them, making the object bounce when the : 1. left side of face touches right wall or 2. top side of face touches bottom wall. The face itself is bouncing in a BorderPane, if that makes a difference in any of this. It is probably the logic behind the code that I have, which i actually got from a textbook.

Here is the relevant code for this matter:

public class BoxPane extends Pane {
    private double radius;
    private double x, y;
    private double dx = 1;
    private double dy = 1;
    private Pane thisPane;
    private Timeline animation;

    public BoxPane(Pane pane)
    {
        getChildren().add(pane);
        thisPane = pane;
        radius = thisPane.getWidth()/2;
        x = radius;
        y = radius;
        animation = new Timeline(new KeyFrame(Duration.millis(50), e -> moveBall()));
        animation.setCycleCount(Timeline.INDEFINITE);
        animation.play();
    }

    public void increaseSpeed()
    {
        animation.setRate(animation.getRate() + 0.1);
    }
    public void decreaseSpeed()
    {
        animation.setRate(animation.getRate()>0?animation.getRate() - 0.1 :     0);
    }

    protected void moveBall()
    {        
        if((x < radius || x > getWidth() - radius))
        {
            dx *= -1;
        }
        if(y < radius || y > getHeight() - radius) {
            dy *= -1;
        }

        x += dx;
        y += dy;
        thisPane.setTranslateX(x);
        thisPane.setTranslateY(y);
    }  
}

I can try to explain if something is not clear in my code. But basically, I took the radius of the object that I am passing in, and create an animation with this. I have methods to increase and decrease speed when a key is pressed (function calls are from a different java file) and moveBall() seems to be where my problem is. Any help is greatly appreciated, thank you! I am sorry if this is a repost of something that was already answered, I couldn't seem to find anything that helped me in my specific case. If needed, I can also provide screenshots of what the program actually looks like and where the ball is turning around. Thanks!


Solution

  • If you try to print the value of radius in moveBall() method, you will notice that its value is always 0. Because you are taking the width of the pane before it is rendered in the scenegraph. To fix this, you can change radius to DoubleProperty and bind its value to the pane width property.

    Also there is small correction required in your moveBall() logic. Please check the below corrections.

    private DoubleProperty radius = new SimpleDoubleProperty();
    
    public BoxPane(Pane pane) {
        getChildren().add(pane);
        thisPane = pane;
        radius.bind(thisPane.widthProperty().divide(2));
        x = radius.get();
        y = radius.get();
        animation = new Timeline(new KeyFrame(Duration.millis(5), e -> moveBall()));
        animation.setCycleCount(Timeline.INDEFINITE);
        animation.play();
    }
    
    protected void moveBall() {
        double faceWidth = radius.get() * 2;
        if ((x < 0 || x > getWidth() - faceWidth)) {
            dx *= -1;
            if (x < 0) {
                x = 0;
            } else if (x > getWidth() - faceWidth) {
                x = getWidth() - faceWidth;
            }
        }
        if (y < 0 || y > getHeight() - faceWidth) {
            dy *= -1;
            if (y < 0) {
                y = 0;
            } else if (y > getHeight() - faceWidth) {
                y = getHeight() - faceWidth;
            }
        }
        x += dx;
        y += dy;
        thisPane.setTranslateX(x);
        thisPane.setTranslateY(y);
    }