Search code examples
javascriptcanvasprocessingp5.js

How to add a bounce compression animation to a ball in P5?


my simple game created with P5.js consists in a ball that falls affected by a gravity force and bounces on the ground. I would like to add a "compression" animation to the ball when it touches the ground so that it should look more realistic.

How can I do that without making it look weird?

the code is this:

function Ball() {
  this.diameter = 50;
  this.v_speed = 0;
  this.gravity = 0.2;
  this.starty = height / 2 - 100;
  this.endy = height - this.diameter / 2;
  this.ypos = this.starty;
  this.xpos = width / 2;

  this.update = function() {

    this.v_speed = this.v_speed + this.gravity;
    this.ypos = this.ypos + this.v_speed;

    if (this.ypos >= this.endy) {
      this.ypos = this.endy;
      this.v_speed *= -1.0; // change direction
      this.v_speed = this.v_speed * 0.9;
      if (Math.abs(this.v_speed) < 0.5) {
        this.ypos = this.starty;
      }
    }
  }

  this.show = function() {
    ellipse(this.xpos, this.ypos, this.diameter);
    fill(255);
  }
}

var ball;

function setup() {
  createCanvas(600, 600);
  ball = new Ball();
}

function draw() {
  background(0);
  ball.update();
  ball.show();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.js"></script>


Solution

  • A very simple solution is to increase this.endy dynamically, by an empirically value which depends on the speed. The maximum of the value has to be less than this.diameter/2. In the example I've use this.diameter/3 for the maximum amount, but you can ply with this value. If the speed is 0, then the amount has to be 0, too:

    endy = this.endy + Math.min(Math.abs(this.v_speed), this.diameter/3);
    if (this.ypos >= endy) {
          this.ypos = endy;
          // [...]
    }
    

    This cause that the ball slightly goes below the bottom. Use this to "squeeze" the ball by the same amount:

    this.show = function() {
        h = Math.min(this.diameter, (height - this.ypos)*2)
        w = 2 * this.diameter - h;
        ellipse(this.xpos, this.ypos, w, h);
        fill(255);
    }
    

    See the example, where I applied the suggestions to the code of the question:

    function Ball() {
      this.diameter = 50;
      this.v_speed = 0;
      this.gravity = 0.2;
      this.starty = height / 2 - 100;
      this.endy = height - this.diameter / 2;
      this.ypos = this.starty;
      this.xpos = width / 2;
    
      this.update = function() {
    
        this.v_speed = this.v_speed + this.gravity;
        this.ypos = this.ypos + this.v_speed;
    
        endy = this.endy + Math.min(Math.abs(this.v_speed), this.diameter/3); 
        if (this.ypos >= endy) {
          this.ypos = endy;
          this.v_speed *= -1.0; // change direction
          this.v_speed = this.v_speed * 0.9;
          if (Math.abs(this.v_speed) < 0.5) {
            this.ypos = this.starty;
          }
        }
      }
    
      this.show = function() {
        h = Math.min(this.diameter, (height - this.ypos)*2)
        w = 2 * this.diameter - h;
        ellipse(this.xpos, this.ypos, w, h);
        fill(255);
      }
    }
    
    var ball;
    
    function setup() {
      createCanvas(600, 600);
      ball = new Ball();
    }
    
    function draw() {
      background(0);
      ball.update();
      ball.show();
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.js"></script>

    Demo