Case Study: Bouncing Balls
Tags: java, programming, learning, beginners
Published: 2024-07-09 21:24:07

This section presents a program that displays bouncing balls and enables the user to add and remove balls.

[Section](https://dev.to/paulike/case-study-bouncing-ball-39cj) presents a program that displays one bouncing ball. This section presents a  program that displays multiple bouncing balls. You can use two buttons to suspend and resume the movement of the balls, a scroll bar to control the ball speed, and the + or - button add or remove a ball, as shown in Figure below.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74lwy6ogf86yhi6m92h8.png)

The example in [Section](https://dev.to/paulike/case-study-bouncing-ball-39cj) only had to store one ball. How do you store the multiple balls in this example? The **Pane**’s **getChildren()** method returns an **ObservableList<Node>**, a subtype of **List<Node>**, for storing the nodes in the pane. Initially, the list is empty. When a new ball is created, add it to the end of the list. To remove a ball, simply remove the last one in the list.

Each ball has its state: the x-, y-coordinates, color, and direction to move. You can define a class named **Ball** that extends **javafx.scene.shape.Circle**. The x-, y-coordinates and the color are already defined in **Circle**. When a ball is created, it starts from the upper-left corner and moves downward to the right. A random color is assigned to a new ball.

The **MultiplBallPane** class is responsible for displaying the ball and the **MultipleBounceBall** class places the control components and implements the control. The relationship of these classes is shown in Figure below. The code below gives the program.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9mj5kigcc85u78xnl59.png)

```
package application;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;

public class MultipleBounceBall extends Application {
	@Override // Override the start method in the Application class
	public void start(Stage primaryStage) {
		MultipleBallPane ballPane = new MultipleBallPane();
		ballPane.setStyle("-fx-border-color: yellow");
		
		Button btAdd = new Button("+");
		Button btSubtract = new Button("-");
		HBox hBox = new HBox(10);
		hBox.getChildren().addAll(btAdd, btSubtract);
		hBox.setAlignment(Pos.CENTER);
		
		// Add or remove a ball
		btAdd.setOnAction(e -> ballPane.add());
		btSubtract.setOnAction(e -> ballPane.subtract());
		
		// Pause and resume animation
		ballPane.setOnMousePressed(e -> ballPane.pause());
		ballPane.setOnMouseReleased(e -> ballPane.play());
		
		// Use a scroll bar to control animation speed
		ScrollBar sbSpeed = new ScrollBar();
		sbSpeed.setMax(20);
		sbSpeed.setValue(10);
		ballPane.rateProperty().bind(sbSpeed.valueProperty());
		
		BorderPane pane = new BorderPane();
		pane.setCenter(ballPane);
		pane.setTop(sbSpeed);
		pane.setBottom(hBox);
		
		// Create a scene and place the pane in the stage
		Scene scene = new Scene(pane, 250, 150);
		primaryStage.setTitle("MultipleBounceBall"); // Set the stage title
		primaryStage.setScene(scene); // Place the scene in the stage
		primaryStage.show(); // Display the stage
	}
	
	public static void main(String[] args) {
		Application.launch(args);
	}
	
	private class MultipleBallPane extends Pane {
		private Timeline animation;
		
		public MultipleBallPane() {
			// Create an animation for moving the ball
			animation = new Timeline(new KeyFrame(Duration.millis(50), e -> moveBall()));
			animation.setCycleCount(Timeline.INDEFINITE);
			animation.play(); // Start animation
		}
		
		public void add() {
			Color color = new Color(Math.random(), Math.random(), Math.random(), 0.5);
			getChildren().add(new Ball(30, 30, 20, color));
		}
		
		public void subtract() {
			if(getChildren().size() > 0) {
				getChildren().remove(getChildren().size() - 1);
			}
		}
		
		public void play() {
			animation.play();
		}
		
		public void pause() {
			animation.pause();
		}
		
		public void increaseSpeed() {
			animation.setRate(animation.getRate() + 0.1);
		}
		
		public void decreaseSpeed() {
			animation.setRate(animation.getRate() > 0 ? animation.getRate() - 0.1 : 0);
		}
		
		public DoubleProperty rateProperty() {
			return animation.rateProperty();
		}
		
		protected void moveBall() {
			for(Node node: this.getChildren()) {
				Ball ball = (Ball)node;
				// Check boundaries
				if(ball.getCenterX() < ball.getRadius() || ball.getCenterX() > getWidth() - ball.getRadius()) {
					ball.dx *= -1; // Change ball move direction
				}
				if(ball.getCenterY() < ball.getRadius() || ball.getCenterY() > getHeight() - ball.getRadius()) {
					ball.dy *= -1; // Change ball move direction
				}
				
				// Adjust ball position
				ball.setCenterX(ball.dx + ball.getCenterX());
				ball.setCenterY(ball.dy + ball.getCenterY());
			}
		}
	}
	
	class Ball extends Circle {
		private double dx = 1, dy = 1;
		
		Ball(double x, double y, double radius, Color color) {
			super(x, y, radius);
			setFill(color); // Set ball color
		}
	}
}

```

The **add()** method creates a new ball with a random color and adds it to the pane (line 73). The pane stores all the balls in a list. The **subtract()** method removes the last ball in the list (line 78).

When the user clicks the + button, a new ball is added to the pane (line 32). When the user clicks the - button, the last ball in the array list is removed (line 33).

The **moveBall()** method in the **MultipleBallPane** class gets every ball in the pane’s list and adjusts the balls’ positions (lines 114–115).