# Balls bouncing off each other, without using Box2D or other libraries

This is my code:

``````Circle[] circles = {
new Circle(40,color(0),color(255,0,0),0,2,0,0.5,new PVector(250,300),new PVector(0,0)),
new Circle(200,color(0),color(255,0,0),0,10,0,0.5,new PVector(500,300),new PVector(0,0)),
new Circle(20,color(0),color(255,0,0),0,0.1,0,0.5,new PVector(750,300),new PVector(0,0)),
};

PVector wind = (PVector.random2D()).div(20);
PVector gravity = new PVector(0,0.2);

void setup(){
size(1000,600);
}

void draw(){
background(0);
circles[0].applyForce(wind);
circles[0].applyForce(gravity);
circles[0].update();
circles[1].applyForce(wind);
circles[1].applyForce(gravity);
circles[1].update();
circles[2].applyForce(wind);
circles[2].applyForce(gravity);
circles[2].update();
}

class Circle{
color colorStroke;
color colorFill;
float thickness;

float mass;
float friction;
float restitution;

PVector position,velocity;

Circle(float r,color cs,color cf,float t,float m,float f,float re,PVector p,PVector v){
colorStroke = cs;
colorFill = cf;
thickness = t;

mass = m;
friction = f;
restitution = re;

position = p;
velocity = v;
}

void update(){
if(thickness == 0){
noStroke();
}else{
strokeWeight(thickness);
}
stroke(colorStroke);
fill(colorFill);

if(position.y > (height - (radius / 2)) && velocity.y > 0){
velocity.y = velocity.y * -restitution;
position.y = height - (radius / 2);
}
if(position.y < (radius / 2) && velocity.y < 0){
velocity.y = velocity.y * -restitution;
}
if(position.x > (width - (radius / 2)) && velocity.x > 0){
velocity.x = velocity.x * -restitution;
position.x = width - (radius / 2);
}
if(position.x < (radius / 2) && velocity.x < 0){
velocity.x = velocity.x * -restitution;
}
}

void applyForce(PVector force){
velocity.x = velocity.x + (force.x / mass);
velocity.y = velocity.y + (force.y);
}
}
``````

The edge bouncing is finished, but I want the balls to bounce off each other. So how do I make them detect collision and bounce?

1 Like

Writing â€ścollision detectionâ€ť (or just â€ścollision detâ€ť) in the search box above produce some hits.

Hereâ€™s one which might be of interest: Collision Detection (Game Design)

1 Like

from the examples section in processing

``````/**
* Bouncy Bubbles
* based on code from Keith Peters.
*
* Multiple-object collision.
*/

int numBalls = 12;
float spring = 0.05;
float gravity = 0.03;
float friction = -0.9;
Ball[] balls = new Ball[numBalls];

void setup() {
size(640, 360);
for (int i = 0; i < numBalls; i++) {
balls[i] = new Ball(random(width), random(height), random(30, 70), i, balls);
}
noStroke();
fill(255, 204);
}

void draw() {
background(0);
for (Ball ball : balls) {
ball.collide();
ball.move();
ball.display();
}
}

class Ball {

float x, y;
float diameter;
float vx = 0;
float vy = 0;
int id;
Ball[] others;

Ball(float xin, float yin, float din, int idin, Ball[] oin) {
x = xin;
y = yin;
diameter = din;
id = idin;
others = oin;
}

void collide() {
for (int i = id + 1; i < numBalls; i++) {
float dx = others[i].x - x;
float dy = others[i].y - y;
float distance = sqrt(dx*dx + dy*dy);
float minDist = others[i].diameter/2 + diameter/2;
if (distance < minDist) {
float angle = atan2(dy, dx);
float targetX = x + cos(angle) * minDist;
float targetY = y + sin(angle) * minDist;
float ax = (targetX - others[i].x) * spring;
float ay = (targetY - others[i].y) * spring;
vx -= ax;
vy -= ay;
others[i].vx += ax;
others[i].vy += ay;
}
}
}

void move() {
vy += gravity;
x += vx;
y += vy;
if (x + diameter/2 > width) {
x = width - diameter/2;
vx *= friction;
}
else if (x - diameter/2 < 0) {
x = diameter/2;
vx *= friction;
}
if (y + diameter/2 > height) {
y = height - diameter/2;
vy *= friction;
}
else if (y - diameter/2 < 0) {
y = diameter/2;
vy *= friction;
}
}

void display() {
ellipse(x, y, diameter, diameter);
}
}
``````
1 Like

What is the array â€śotherâ€ť for?

And also the float â€śspringâ€ť ?

not sure as Iâ€™ve not had time to study it you might have to do a bit of research.

1 Like

Alright. Can you tell me where you got this script (Or you wrote it yourself)?

open the examples in your processing ide, the application includes a ton of examples.

1 Like

Itâ€™s the same as the â€śballsâ€ť array. If you look in the `collide()` method, youâ€™ll see that itâ€™s used for getting the position of the other â€śballsâ€ť objects.

Btw notice how the for loop starts at `id + 1`, to avoid collision detection twice for the same two objects.

But actually it isnâ€™t needed. You could just use `balls[i]` instead of `others[i]` in the collision() method. I guess it was done to make the code more readable.

Click to (un) expand
``````/**
* Bouncy Bubbles mod without "others" array
* based on code from Keith Peters.
*
* Multiple-object collision.
*/

int numBalls = 12;
float spring = 0.05;
float gravity = 0.03;
float friction = -0.9;
Ball[] balls = new Ball[numBalls];

void setup() {
size(640, 360);
for (int i = 0; i < numBalls; i++) {
balls[i] = new Ball(random(width), random(height), random(30, 70), i);
}
noStroke();
fill(255, 204);
}

void draw() {
background(0);
for (Ball ball : balls) {
ball.collide();
ball.move();
ball.display();
}
}

class Ball {

float x, y;
float diameter;
float vx = 0;
float vy = 0;
int id;

Ball(float xin, float yin, float din, int idin) {
x = xin;
y = yin;
diameter = din;
id = idin;
}

void collide() {
for (int i = id + 1; i < numBalls; i++) {
float dx = balls[i].x - x;
float dy = balls[i].y - y;
float distance = sqrt(dx*dx + dy*dy);
float minDist = balls[i].diameter/2 + diameter/2;
if (distance < minDist) {
float angle = atan2(dy, dx);
float targetX = x + cos(angle) * minDist;
float targetY = y + sin(angle) * minDist;
float ax = (targetX - balls[i].x) * spring;
float ay = (targetY - balls[i].y) * spring;
vx -= ax;
vy -= ay;
balls[i].vx += ax;
balls[i].vy += ay;
}
}
}

void move() {
vy += gravity;
x += vx;
y += vy;
if (x + diameter/2 > width) {
x = width - diameter/2;
vx *= friction;
}
else if (x - diameter/2 < 0) {
x = diameter/2;
vx *= friction;
}
if (y + diameter/2 > height) {
y = height - diameter/2;
vy *= friction;
}
else if (y - diameter/2 < 0) {
y = diameter/2;
vy *= friction;
}
}

void display() {
ellipse(x, y, diameter, diameter);
}
}
``````

If you play with the value, youâ€™ll see how it affects the collisions. Try a value 10x the original, then 1/10th the original, for example.

Or look in the collision() method.

``````        float ax = (targetX - others[i].x) * spring;
float ay = (targetY - others[i].y) * spring;
``````

â€śspringâ€ť is used to modify how much â€śforceâ€ť (or rather velocity) is applied at each moment (frame).

Itâ€™s essentially just Newtonâ€™s 3rd law of motion, the one about applying a force to an object, results in the object applying the same but opposite force back (action - reaction).
Basically swapping the result of the collision â€śforceâ€ť between the objects in the next lines.

``````        vx -= ax;
vy -= ay;
others[i].vx += ax;
others[i].vy += ay;
``````

Thatâ€™s what I got out of it anywayâ€¦

Since Iâ€™m about to post, Iâ€™ll answer (Btw he did say where he got it, from the Processing IDEâ€™s examples).

You should find it here: File menu > â€śExamplesâ€¦â€ť -> Topics > Motion > Bouncy bubbles.

1 Like

Thanks That explain anything

1 Like

It worked fine!

Code:

``````float fadeSpeed = 20;

float mouseStrength = 100;
float windStrength = 0.1;
float gravityStrength = 0.2;

Circle[] circles = new Circle[12];

PVector wind = (PVector.random2D()).mult(0.1);
PVector gravity = new PVector(0,gravityStrength);

void setup(){
background(0);
size(1000,600);
for(int n = 0;n < circles.length;n++){
circles[n] = new Circle(
random(20,100),
color(random(0,255),random(0,255),random(0,255),random(128,255)),
color(random(0,255),random(0,255),random(0,255),random(128,255)),
random(0,20),
random(-5.0,20.0),
random(0.0,1.0),
new PVector(random(0,width),random(0,height)),
new PVector(random(-0.2,-0.2),random(-0.2,0.2))
);
}
}

void draw(){
rect(0,0,width,height);

for(int n = 0;n < circles.length;n++){
circles[n].applyForce(wind);
circles[n].applyForce(gravity);
circles[n].update();
circles[n].display();
}

drawArrow(new PVector(500,200),80,angle(new PVector(0,0),wind),8,color(0));
drawArrow(new PVector(500,200),80,angle(new PVector(0,0),wind),5,color(255));

if(mousePressed && mouseButton == CENTER){
wind = new PVector(mouseX - 500,mouseY - 200);
wind.normalize();
wind.div(10);
}else{
wind.rotate((noise(frameCount) - 0.5) / 10);
}
}

void drawArrow(PVector position,float distance,float angle,float thickness,color colorStroke){
strokeWeight(thickness);
stroke(colorStroke);

pushMatrix();
translate(position.x,position.y);
rotate(angle);
rotate(-QUARTER_PI);
translate(distance,distance);
line(-distance,-distance,0,0);
rotate(HALF_PI + QUARTER_PI);
line(0,0,20,20);
rotate(-(PI + HALF_PI));
line(0,0,20,20);
popMatrix();
}

float angle(PVector v1, PVector v2) {
float a = atan2(v2.y, v2.x) - atan2(v1.y, v1.x);
if (a < 0) a += TWO_PI;
return a;
}

class Circle{
color colorStroke;
color colorFill;
float thickness;

float mass;
float restitution;

PVector position,velocity;

Circle(float r,color cs,color cf,float t,float m,float re,PVector p,PVector v){
colorStroke = cs;
colorFill = cf;
thickness = t;

mass = m;
restitution = re;

position = p;
velocity = v;
}

void update(){

if(position.y > (height - (radius / 2)) && velocity.y > 0){
velocity.y = velocity.y * -restitution;
position.y = height - (radius / 2);
}
if(position.y < (radius / 2) && velocity.y < 0){
velocity.y = velocity.y * -restitution;
}
if(position.x > (width - (radius / 2)) && velocity.x > 0){
velocity.x = velocity.x * -restitution;
position.x = width - (radius / 2);
}
if(position.x < (radius / 2) && velocity.x < 0){
velocity.x = velocity.x * -restitution;
}

for (int i = 1;i < circles.length;i++) {
float dx = circles[i].position.x - position.x;
float dy = circles[i].position.y - position.y;
float distance = sqrt(dx*dx + dy*dy);
if (distance < minDist) {
float angle = atan2(dy, dx);
float targetX = position.x + cos(angle) * minDist;
float targetY = position.y + sin(angle) * minDist;
float ax = (targetX - circles[i].position.x);
float ay = (targetY - circles[i].position.y);
velocity.x -= ax;
velocity.y -= ay;
circles[i].velocity.x += ax;
circles[i].velocity.y += ay;
}
}
}

void applyForce(PVector force){
velocity.x = velocity.x + (force.x / abs(mass));
if(mass < 0){
velocity.y = velocity.y - force.y;
}
if(mass == 0){
velocity.y = velocity.y;
}
if(mass > 0){
velocity.y = velocity.y + force.y;
}
}

void display(){
stroke(colorStroke);
if(thickness == 0){
noStroke();
}else{
strokeWeight(thickness);
}

fill(colorFill);