Hi,
Welcome to the community!
Before trying to solve your problem, you are facing a well known problem where the structure of your code is actually describing object oriented programming :
So object oriented programming is a way to structure your code by using objects to model the data that your are manipulating. In this special case, your are creating Balls, and a Ball is described as :
- a position (
x
and y
coordinates on the screen)
- a speed (described as a vector,
xDir
and yDir
in your program)
- a diameter (here all of your balls have a diameter of
50
)
Now what is nice is that Java’s paradigm is object oriented so here is how we model a ball :
// Our class describing a ball
class Ball{
PVector position;
PVector speed;
float diameter;
// Constructor
Ball(float x, float y, float xSpeed, float ySpeed, float diameter){
position = new PVector(x, y);
speed = new PVector(xSpeed, ySpeed);
// Because the name of the variable diameter is the same in the constructor
// We use this to specify the current object
this.diameter = diameter;
}
}
(Note that here, we are using another class which is representing a vector, PVector (two coordinates x and y)).
So our ball class is holding it’s position and speed (both vectors) and also it’s diameter. Now we want to create multiple balls with different positions/speed or diameter.
This is what the constructor is made for. Constructing an object is called instantiation.
This is how we create an instance of our ball class with the keyword new :
// Create a variable holding an instance_ of Ball
// at the location [0, 0] with speed [-1, 1] and diameter 50
Ball ballInstance = new Ball(0, 0, -1, 1, 50);
When you create an object, you call the constructor function with the specific parameters for that object (its location, speed…) that we defined above in the class.
Next, what if we want to display our ball? We could define a function that takes a Ball object then display it like this :
void displayBall(Ball ball){
ellipse(ball.position.x, ball.position.y, ball.diameter, ball.diameter);
}
Note that if we want to access a certain data attribute of an object (here the position and the diameter), we use a dot. Since position
is also a class (PVector) that has two member (x
and y
), we can chain the calls with ball.position.x
to access the x
member of the position
attribute.
But the real strength of classes in Java is that you can define some methods. Methods are specific functions to a class that manipulate or use data members of that class.
So we can say :
class Ball{
// Attributes...
// Constructor...
void display(){
ellipse(position.x, position.y, diameter, diameter);
}
}
Since we now define this function in our class, we can directly access the attributes. This is really handy!
Then calling methods of objects is simple:
// Create an instance of the Ball class_
Ball ball = new Ball(...);
// Display it
ball.display();
Then you need to implement other methods to run your program (update its position with the speed, and check intersections with the borders of the screen).
Last thing is that you are working with multiple balls. Working with multiple objects means that you need to store them.
An appropriate way to store objects is by using a class called ArrayList
which is really simple to use :
// Declare a new_ list_ by specifying the class_ of the objects that are going to be stored
// Notice the usage of new_ because it's a class
ArrayList<Ball> balls = new ArrayList<Ball>();
// Add a ball to the list
balls.add(new Ball(0, 0, -1, 1, 50));
// Get the size of the list
// Should return 1
println(balls.size());
Final step : since you have multiple objects, you need to process them with a loop. Looping through an ArrayList is also quite simple (the syntax is different from a traditional for loop) :
for(Ball ball : balls){
// Do something with ball
ball.display();
}
If you put everything together, you have the following code :
ArrayList<Ball> balls;
int nbBalls = 50;
int minDiameter = 10;
int maxDiameter = 50;
int maxSpeed = 5;
void setup() {
size(500, 500);
// Initialize the ArrayList
balls = new ArrayList<Ball>();
// Create the balls and fill it
createBalls();
}
void draw() {
background(0);
// For every ball, display it and update it's location
for(Ball ball : balls){
ball.display();
ball.update();
}
}
// Our class describing a ball
class Ball {
PVector position;
PVector speed;
float diameter;
// Constructor
Ball(float x, float y, float xSpeed, float ySpeed, float diameter) {
position = new PVector(x, y);
speed = new PVector(xSpeed, ySpeed);
// Because the name of the variable diameter is the same in the constructor
// We use this to specify the current object
this.diameter = diameter;
}
void display() {
stroke(0, 255, 0);
strokeWeight(5);
fill(255, 0, 0);
ellipse(position.x, position.y, diameter, diameter);
}
void update(){
// Add the speed to the position
// This is just a vector addition
position.add(speed);
// Check collision on x
if(position.x - diameter/2 < 0 || position.x + diameter/2 > width){
speed.x *= -1;
}
// Check collision of y
if(position.y - diameter/2 < 0 || position.y + diameter/2 > height){
speed.y *= -1;
}
}
}
// Create balls at random locations and speed
void createBalls() {
for (int i = 0; i < nbBalls; i++) {
float diameter = random(minDiameter, maxDiameter);
float x = random(diameter/2, width - diameter/2);
float y = random(diameter/2, height - diameter/2);
float xSpeed = random(-maxSpeed, maxSpeed);
float ySpeed = random(-maxSpeed, maxSpeed);
// Add the ball to the ArrayList
balls.add(new Ball(x, y, xSpeed, ySpeed, diameter));
}
}
The advantages of doing this is that your code is much more extensible and robust (you want to add a feature, just add a method to your Ball class!, you want to modify the diameter? just do it! )
Finally your question haha , as homework you should check those functions :