Mass, Friction and Fluid Resistance

To continue on from Processing: Vectors and Forces Part 2 I decided to look into how to make forces more realistic, which as discussed by Palmer (2005) in Physics for Game Programmers would help make my final project become more immersive for the audience and result in higher engagement as objects behave according to the real life rules we already abide by. We expect objects to act in certain ways, even in a fantasy setting. For example if we see a simulation of a hover jet we expect the thrust to be in the opposite direction to the exhaust. Games that do not use realistic physics are often subject to critical reviews as the believability in the constructed world is hard to develop. In addition it also makes games far harder to control.

Like the previous sketches, I am applying forces to a Mover class. However to begin to make this class act more like a real world ball as I learned from The Nature of Code I am going to add more parameters to the class. Friction is a force that influences real world objects that forces them to eventually slow down, whilst running over a surface. Whilst the equation for friction in the real world is quite complicated, involving things like a coefficient of friction which describes the roughness of the surface and it’s angle, we can provide a rough simulation of it getting the velocity of an object. Reversing and normalizing it, and then multiplying it so it is a small fraction of the original. This is then added each frame to the object eventually slows down.

Mass also plays a part in determining how friction will influence an object, the heavier it is the more friction will slow it down and the lower acceleration it will have. Therefore by creating a mass variable in the Mover class we can divide the force by mass to get the eventual acceleration. In the below example I also made the size of the objects dependent on the mass, so bigger objects are heavier and smaller ones are lighter. This also means the lighter balls bounce more than the larger ones, which also come to a stop more abruptly.

//Main Sketch
Mover[] movers = new Mover[20];

void setup() {
  size(600, 600);
  
  for (int i = 0; i < movers.length; i++){
  movers[i] = new Mover(random(0.1,5), 0, height*0.5);
  }
  
}

void draw(){
  background(222);
 
 PVector wind  = new PVector(0.01, 0);
 PVector gravity = new PVector(0,0.1);
 
  for (int i = 0; i < movers.length; i++){
   
  float c = 0.01;
  PVector friction = movers[i].velocity.get();
  friction.mult(-1);
  friction.normalize();
  friction.mult(c);
  
  movers[i].applyForce(friction);
  movers[i].applyForce(wind);
  movers[i].applyForce(gravity);
  
  
  movers[i].update();
  movers[i].checkEdges();
  movers[i].display();
  
  }
  
   fill(0);
  text("click mouse to reset",10,30);
}


  
void mousePressed() {
  reset();
}

void reset() {
  for (int i = 0; i < movers.length; i++) {
    movers[i] = new Mover(random(0.5, 3), 0, random(0, height/2));
  }
}
//Mover Class
class Mover{
  
  PVector location;
  PVector velocity;
  PVector acceleration;
  PVector gravity;
  float mass;
 

  
 Mover(float m, float x, float y){
   location = new PVector(x,y);
   velocity = new PVector(0,0);
   acceleration = new PVector (0, 0);
   mass = m;
  
 }
  
 void applyForce(PVector force){
   PVector f = PVector.div(force,mass);
   acceleration.add(f);
 }
 
  void update(){
   
    velocity.add(acceleration);
    location.add(velocity);
   
     acceleration.mult(0);
  }
  
  void display(){
    stroke(0);
    fill(175);
    ellipse(location.x, location.y, mass*16 ,mass*16);
  }
  
 void checkEdges(){
 if (location.x>width){
     location.x = width;
     velocity.x *= -1;
   } else if (location.x < 0){
     velocity.x *= -1;
     location.x = 0;
   }
 
   if (location.y>height){
     velocity.y *= -1;
     location.y = height;
   } 
   
 }

}

A way to improve the above sketch would be to make the gravity not dependent on the size of the object. In real life, heavy and light objects fall at the same time (provided they have the same surface area.) In the above sketch the bigger balls move slower as the are hitting the ground, however this can be rectified by scaling the gravity by the mass.

Another important real world force is fluid resistance, where as an object passes through a gas or liquid it gets slowed down. Again the real world formula for this is quite complicated, but we only need a simplified version of this to replicate fluid resistance.

ch02_05

Image by the Daniel Shiffman: The Nature of Code

We apply this force to a class called Liquid which we call as the Mover objects pass through the Liquid. We can then apply it using our ApplyForce class we made earlier.

Sketch below by Daniel Shiffman

//Main Sketch
Mover[] movers = new Mover[9];

// Liquid
Liquid liquid;

void setup() {
  size(640, 360);
  reset();
  // Create liquid object
  liquid = new Liquid(0, height/2, width, height/2, 0.1);
}

void draw() {
  background(255);
  
  // Draw water
  liquid.display();

  for (int i = 0; i < movers.length; i++) {
    
    // Is the Mover in the liquid?
    if (liquid.contains(movers[i])) {
      // Calculate drag force
      PVector dragForce = liquid.drag(movers[i]);
      // Apply drag force to Mover
      movers[i].applyForce(dragForce);
    }

    // Gravity is scaled by mass here!
    PVector gravity = new PVector(0, 0.1*movers[i].mass);
    // Apply gravity
    movers[i].applyForce(gravity);
   
    // Update and display
    movers[i].update();
    movers[i].display();
    movers[i].checkEdges();
  }
  
  fill(0);
  text("click mouse to reset",10,30);
  
}

void mousePressed() {
  reset();
}

// Restart all the Mover objects randomly
void reset() {
  for (int i = 0; i < movers.length; i++) {
    movers[i] = new Mover(random(0.5, 3), 40+i*70, random(0, height/2));
  }
}
//Mover Class

class Mover {

  // location, velocity, and acceleration 
  PVector location;
  PVector velocity;
  PVector acceleration;
  
  // Mass is tied to size
  float mass;

  Mover(float m, float x, float y) {
    mass = m;
    location = new PVector(x, y);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0);
  }

  // Newton's 2nd law: F = M * A
  // or A = F / M
  void applyForce(PVector force) {
    // Divide by mass 
    PVector f = PVector.div(force, mass);
    // Accumulate all forces in acceleration
    acceleration.add(f);
  }

  void update() {
    
    // Velocity changes according to acceleration
    velocity.add(acceleration);
    // Location changes by velocity
    location.add(velocity);
    // We must clear acceleration each frame
    acceleration.mult(0);
  }
  
  // Draw Mover
  void display() {
    stroke(0);
    strokeWeight(2);
    fill(127, 200);
    rect(location.x, location.y, mass*16, mass*16);
  }
  
  // Bounce off bottom of window
  void checkEdges() {
    if (location.y > height) {
      velocity.y *= -0.9;  // A little dampening when hitting the bottom
      location.y = height;
    }
  }
}
 // Liquid class 
 class Liquid {

  
  // Liquid is a rectangle
  float x,y,w,h;
  // Coefficient of drag
  float c;

  Liquid(float x_, float y_, float w_, float h_, float c_) {
    x = x_;
    y = y_;
    w = w_;
    h = h_;
    c = c_;
  }
  
  // Is the Mover in the Liquid?
  boolean contains(Mover m) {
    PVector l = m.location;
    return l.x > x && l.x < x + w && l.y > y && l.y < y + h;
  }
  
  // Calculate drag force
  PVector drag(Mover m) {
    c = 0.3*m.mass;
    // Magnitude is coefficient * speed squared
    float speed = m.velocity.mag();
    float dragMagnitude = c * speed * speed;

    // Direction is inverse of velocity
    PVector dragForce = m.velocity.get();
    dragForce.mult(-1);
    
    // Scale according to magnitude
    // dragForce.setMag(dragMagnitude);
    dragForce.normalize();
    dragForce.mult(dragMagnitude);
    return dragForce;
  }
  
  void display() {
    noStroke();
    fill(50);
    rect(x,y,w,h);
  }

}

A way to improve this sketch would be to include two things to the dragForce variable to make it more accurate. In the real world, drag also depends on the surface area of the front of the object moving through the liquid. It also depends on the viscosity of the liquid which could be varied to make the liquid seem more realistic. However I will probably not use these kinds of physics in my interactive project and although useful to learn, I will probably not continue with research into this area.

Palmer, G., 2005. Physics for Game Programmers New York: Springer Verlag

Shiffman, D., 2012. The Nature of Code [online] Mountain View: Creative Commons

Leave a Comment