Source

let system;
let wind;
let waterLine = 400;

function setup() {
  createCanvas(600, 600);

  system = new System();



  let ball = new Ball(100, 100, 25, "red");

  system.addObject(ball);


  ball = new Ball(100, 100, 10, "blue");

  system.addObject(ball);

  for (let i = 0; i < 20; i++){
    ball = new Ball(random(10, width-10), random(10, waterLine-10), 10, "blue");

  system.addObject(ball);
  }

  const gravity = {
    base: createVector(0,1),
    applyTo(obj){
      obj.applyForce(p5.Vector.mult(this.base, obj.mass));
    }
  }

  system.addForce(gravity);

  wind = {
    base: createVector(0,0),
    applyTo(obj){
      obj.applyForce(this.base);
    }
  }

  system.addForce(wind);

  const drag = {
    dragCoefficient: .2,
    applyTo(obj){
      // don't do anything if it isn't in the water
      if (obj.radius+obj.position.y > waterLine){
        const v = obj.velocity.copy().normalize();
        v.mult(-this.dragCoefficient * obj.velocity.magSq());
        obj.applyForce(v);
      }

    }
  }

  system.addForce(drag);


 
}

function draw() {
  background(255);
   if (mouseIsPressed){
    wind.base.x = width/2 - mouseX;
    wind.base.y =  height/2 - mouseY;
    wind.base.normalize();
    wind.base.mult(30);
    console.log(wind.base)
  }else{
    wind.base.mult(0);
  }

  fill(100,100,255,100);
  rect(0, waterLine, width, height-waterLine);

  system.update();
  system.draw();

  noFill();
  stroke("black");
  rect(0,0,width, height);
}



class System{
  objects = [];
  forces = [];

  addObject(obj){
    this.objects.push(obj);
  }

  addForce(force){
    this.forces.push(force);
  }

  update(){
    for (let force of this.forces){
      for (let obj of this.objects){
        force.applyTo(obj);
      }
    }

    for (let obj of this.objects){
        obj.update();
      }

  }

  draw(){
    for (let obj of this.objects){
      obj.draw();
    }
  }


}




class Ball {
  constructor(x, y, radius, color) {
    this.radius = radius;
    this.mass = radius;
    this.color = color;
    this.position = createVector(x, y);
    this.velocity = createVector(1,0);
    this.force = createVector(0,0);
  }


  applyForce(force){
    this.force.add(force);
  }

  update(){
    const acceleration = p5.Vector.div(this.force, this.mass);
    this.force.x = 0;
    this.force.y = 0;
    this.velocity.add(acceleration);
    this.position.add(this.velocity);

    if ((this.position.x - this.radius< 0 && this.velocity.x < 0)
    || (this.position.x + this.radius >= width && this.velocity.x > 0)){
      this.velocity.x  *= -.9;
    }
    if ((this.position.y - this.radius < 0 && this.velocity.y < 0) 
    || (this.position.y + this.radius >= height && this.velocity.y > 0)){
      this.velocity.y  *= -.9;
    }
    this.position.x = min(width - this.radius, max(this.radius, this.position.x));
    this.position.y = min(height - this.radius, max(this.radius, this.position.y));

  }


  draw(){
    fill(this.color);
    circle(this.position.x, this.position.y, this.radius*2);
  }


}