The foreword 0.

Here’s an example of a pinball on MDN, our little balls bounce around the screen and change color when they touch each other.

1. Practice of object-oriented programming

The official website is too long, and there are some bugs, I will improve it

let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
let width = canvas.width = window.innerWidth;
let height = canvas.height = window.innerHeight;
let balls = [];
let ran = (min,max) = >parseInt((max-min)*Math.random())+min;// Generate a random number

let Ball = function(vx,vy,x,y,r,color){/ / Ball class
	this.vx = vx;
	this.vy = vy;
	this.x = x||1;// Prevent speed from 0
	this.y = y||1;
	this.r = r;
	this.color = color;
}



Ball.prototype.draw = function(){// The method of drawing
	ctx.beginPath();
	ctx.fillStyle = this.color;
	ctx.arc(this.x,this.y,this.r,0.2*Math.PI);
	ctx.fill();
}

Ball.prototype.update = function(){// Update the method
  if((this.x + this.r) >= width) {
  	this.x = width - this.r - 5;// Prevent the body from entering the edge, loop infinitely, stick to the edge
    this.vx = -(this.vx);/ / rebound
  }

  if((this.x - this.r) <= 0) {
  	this.x = this.r + 5;
    this.vx = -(this.vx);
  }

  if((this.y + this.r) >= height) {
  	this.y = height - this.r - 5;
    this.vy = -(this.vy);
  }

  if((this.y - this.r) <= 0) {
  	this.y =  this.r + 5;
    this.vy = -(this.vy);
  }
  	this.x += this.vx;// The ball goes forward
	this.y += this.vy;
}


Ball.prototype.isCollision = function() {// Whether to collide
  for(var j = 0; j < balls.length; j++) {
    if(! (this === balls[j])) {// Make sure you don't collide with yourself, because you are also in the array
      var dx = this.x - balls[j].x;
      var dy = this.y - balls[j].y;
      var dvx = this.vx - balls[j].vx;
      var dvy = this.vy - balls[j].vy;
      var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance <= this.r + balls[j].r) {
		balls[j].x -= 7*balls[j].vx;// Prevent entanglement
		balls[j].y -= 7*balls[j].vy;
      	this.x -= 7*this.vx;
      	this.y -= 7* this.vy;
        this.vx = -this.vx;
        this.vy = -this.vy;
        this.color = "#"+ (~ ~ (Math.random()*(1<<24))).toString(16); }}}};let loop = function(){
	ctx.fillStyle = 'rgba(0,0,0,.1)';// erases the trace left by the previous animation
  	ctx.fillRect(0.0,width,height);
	while(balls.length<40) {// Generate 40 balls
		let ball = new Ball(ran(7 -.7),ran(7 -.7),ran(0,width),ran(0,height),
		ran(10.20),"#"+ (~ ~ (Math.random()*(1<<24))).toString(16));
		balls.push(ball);
	}
	for(let i = 0; i<balls.length; i++){// Call the function for each ball to keep the animation going
		balls[i].draw();
		balls[i].update();
		balls[i].isCollision();
	}
	requestAnimationFrame(loop);
}
loop();
Copy the code

2. Intertwined phenomena

In the face of the collision detection and subsequent actions, we must consider the entangled problem: if two balls are detected when the collision, and their speed is still in the collision range, just like gravity can not escape, infinite in-situ collision. At this point, other balls need to collide to dissolve the entanglement. Sometimes, maybe all three of them will go into infinite entanglement together. Judge collision – yes – speed in the opposite direction – far away – judge collision – speed in the opposite direction – close – judge collision – yes – speed in the opposite direction – far away… Infinite loop)

3. Solutions

For the boundary, to prevent it from sticking to the boundary, we can reposition it just enough to get it off the boundary, like the right boundary

This. X = width -this. r -5 //-5 to ensure that it will absolutely leave, -1 sometimes stick, but the difference between 1 and 5 is not big

The same thing for other boundaries

For the two balls, we also reset the position, and the algorithm of the reset the constant depends on the situation.

this.x -= 7*this.vx; // In my case, practice proves that the probability of entanglement is lower than 6
// And 6 frames is just the right moment for the explosion in the game. There are 6 frames for that moment so that we can feel it
// I just let him go back 6 frames, of course, the larger the ball size, the number is also larger
this.y -= 7* this.vy;
Copy the code

Solution 2: We can initialize the Ball constructor with another value :this.isleave = true; For the Ball. The prototype. IsCollision function, we change, when the collision, enclosing isleave become false

if(!this.isleave){
    if(distance> this.r + balls[j].r){
      this.isleave =true;/ / from after
    }else{
       //do something
       return; }}else if(distance <= this.r + balls[j].r){
   this.isleave = false;
  // The previous code
}
Copy the code

Simulate nuclear fission

When it hits, a new ball is formed next to it. Because the chain reaction can blow up the browser all of a sudden, so we limit the number of balls

/ / Ball. Prototype. IsCollision part changes
if (distance <= this.r + balls[j].r) {
		balls[j].x -= 7*balls[j].vx;
		balls[j].y -= 7*balls[j].vy;
      	this.x -= 7*this.vx;
      	this.y -= 7* this.vy;
        this.vx = -this.vx;
        this.vy = -this.vy;
        if(balls.length<30) {// Stop at 30
          let ball = new Ball(ran(7 -.7),ran(7 -.7),this.x- 17*this.vx,this.y- 17* this.vy,
          this.r,
          "#"+ (~ ~ (Math.random()*(1<<24))).toString(16)); balls.push(ball); }}//loop part of the change
let loop = function(){
	ctx.fillStyle = 'rgba(0,0,0,.2)';
  	ctx.fillRect(0.0,width,height);
	while(balls.length<2) {// The first two balls
		let ball = new Ball(ran(7 -.7),ran(7 -.7),ran(0,width),ran(0,height),
		ran(10.20),"#"+ (~ ~ (Math.random()*(1<<24))).toString(16));
		balls.push(ball);
	}
	for(let i = 0; i<balls.length; i++){ balls[i].draw(); balls[i].update(); balls[i].isCollision(); } requestAnimationFrame(loop); }Copy the code

Big fish eat small fish

MDN says regenerate into an Eval (this refers to the enemy that eats the ball), eats the ball. I’m setting eval to be of the same class as the ball, but its isCollision method is a little different and eats the ball. To keep the loop going forever, when five balls are consumed, eval explodes, generating the original number of balls and continuing the loop.

// Define eval
Eval.prototype.draw = function(){
  ctx.beginPath();
  ctx.fillStyle = this.color;
  ctx.arc(this.x,this.y,this.r,0.2*Math.PI);
  ctx.fill();
}
Eval.prototype.update = Ball.prototype.update;
Eval.prototype.isCollision = function(){
   for(var j = 0; j < balls.length; j++) {
      var dx = this.x - balls[j].x;
      var dy = this.y - balls[j].y;
      var distance = Math.sqrt(dx * dx + dy * dy);
      if (distance <= this.r + balls[j].r) {
          balls.splice(j,1)
          this.vx = -this.vx;
          this.vy = -this.vy;
          this.r += 1; }}}let e = new Eval(10.10,ran(0,width),ran(0,height),20.'#fff');

// Initial 30 balls
while(balls.length<30) {let ball = new Ball(ran(7 -.7),ran(7 -.7),ran(0,width),ran(0,height),
  ran(10.20),"#"+ (~ ~ (Math.random()*(1<<24))).toString(16));
  balls.push(ball);
}

/ / loop changes
let loop = function(){
	ctx.fillStyle = 'rgba(0,0,0,.2)';
  	ctx.fillRect(0.0,width,height);
  if(balls.length<5) {// Less than 5, eval is a new eval
    e = new Eval(10.10,e.x,e.y,20.'#fff');
    while(balls.length<30) {// Loop to generate 30 balls
      let ball = new Ball(ran(7 -.7),ran(7 -.7),ran(e.x,e.x),ran(e.x,e.x),
      ran(10.20),"#"+ (~ ~ (Math.random()*(1<<24))).toString(16)); balls.push(ball); }}else{
    e.draw();
    e.update();
    e.isCollision();
    
  }
  for(let i = 0; i<balls.length; i++){ balls[i].draw(); balls[i].update(); balls[i].isCollision(); } requestAnimationFrame(loop); }Copy the code

More spectacular, isn’t it?