Круговое разрешение столкновения - PullRequest
0 голосов
/ 07 марта 2020

Я пытаюсь разрешить круговые столкновения. Проблема в том, что шары никогда не оседают на дно - они продолжают перемешиваться. Что я могу сделать, чтобы шарики оседали на дно в конце цикла (без покачивания)?

CodePen: https://codepen.io/KriegersVan/pen/gOpXzwE?editors=0010

var _bouncingBalls = (function() {
      document.getElementById("canvas").width = window.innerWidth;
      document.getElementById("canvas").height = window.innerHeight;

      function _randomNumBetween(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
      }

      function _negativeOrPositiveOne() {
        return Math.random() < 0.50 ? -1 : 1;
      }

      function _random10Chars() {
        return Math.random().toString(36).substr(2, 10);
      }

      var _balls = {
        numOf: 10,
        colors: ["#6a00ff", "#9500ff", "#d000ff", "#ff00ff", "#00f2ff", "#f59b42", "#e9f542"],
        particles: [],
        ctx: document.getElementById("canvas").getContext("2d"),
        lastFrameTime: 0,
        startTime: 0,
        inProgress: false,
        init: function(x, y) {
          for (var i = 0; i < this.numOf; i++) {
            this.particles.push({
              x: x+(i*(window.innerWidth * 0.02)), //So they don't overlap at the beginning
              y: y,
              vy: Math.random() * _negativeOrPositiveOne(),
              vx: 2 * Math.random() * _negativeOrPositiveOne(),
              color: this.colors[_randomNumBetween(0, this.colors.length - 1)],
              radius: window.innerWidth * 0.008,
              gravity: 0.70,
              damping: 0.50,
              id: _random10Chars() + _random10Chars()
            });
          }

          this.startTime = window.performance.now();

          if (!this.inProgress) {
            this.inProgress = true;
            window.requestAnimationFrame(this.render);
          }
        },
        isBallCollision: function(particle) {
          //https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
          for (var j = 0; j < _balls.particles.length; j++) {
            var comparisonParticle = _balls.particles[j];
            var dx = particle.x - comparisonParticle.x;
            var dy = particle.y - comparisonParticle.y;
            var distance = Math.sqrt(dx * dx + dy * dy);

            if (particle.id === comparisonParticle.id) {
              continue;
            }

           var minDist = comparisonParticle.radius + particle.radius; 
            if (distance < minDist) {
              //https://www.youtube.com/watch?v=nlwtgvZCz0k
              var distance_x = dx;
              var distance_y = dy;
              var radii_sum = particle.radius+comparisonParticle.radius;
              var length = Math.sqrt(distance_x * distance_x + distance_y * distance_y) || 1;
              var unit_x = distance_x/length;
              var unit_y = distance_y/length;

              particle.x = comparisonParticle.x+(radii_sum+1)*unit_x;
              particle.y = comparisonParticle.y+(radii_sum+1)*unit_y;

              return comparisonParticle;
            }
          }

          return null;
        },
        isBottomHit: function(obj, comparisonObj){
          return obj.y+obj.radius > comparisonObj.y-comparisonObj.radius &&
                 obj.y+obj.radius < comparisonObj.y+comparisonObj.radius;
        },
        isTopHit: function(obj, comparisonObj){
          return obj.y-obj.radius < comparisonObj.y+comparisonObj.radius &&
                 obj.y-obj.radius > comparisonObj.y-comparisonObj.radius;
        },
        isLeftHit: function(obj, comparisonObj){
          return obj.x-obj.radius < comparisonObj.x+comparisonObj.radius &&
                 obj.x-obj.radius > comparisonObj.x-comparisonObj.radius;
        },
        isRightHit: function(obj, comparisonObj){
          return obj.x+obj.radius > comparisonObj.x-comparisonObj.radius &&
                 obj.x+obj.radius < comparisonObj.x+comparisonObj.radius;
        },
        bottomReached: function(obj) {
          return (obj.y + obj.radius) >= window.innerHeight;
        },
        topReached: function(obj) {
          return (obj.y - obj.radius) <= 0;
        },
        leftSideReached: function(obj) {
          return (obj.x - obj.radius) <= 0;
        },
        rightSideReached: function(obj) {
          return (obj.x + obj.radius) >= window.innerWidth;
        },
        draw: function(deltaNum, timestamp) {
          var r = _balls;
          var ctx = r.ctx;
          var elapsed = timestamp - r.startTime;

          for (var i = 0; i < r.particles.length; i++) {
            var obj = r.particles[i];

            ctx.beginPath();
            ctx.fillStyle = obj.color;
            ctx.moveTo(
              obj.x,
              obj.y
            );
            ctx.arc(
              obj.x,
              obj.y,
              obj.radius,
              0,
              2 * Math.PI,
              false
            );
            ctx.fill();
            ctx.closePath();

            obj.y += (obj.vy * deltaNum);

            obj.vy += (obj.gravity * deltaNum);

            obj.x += (obj.vx * deltaNum);

            var otherBall = r.isBallCollision(obj);

            if (otherBall) {
                if (r.isBottomHit(obj, otherBall)){
                  //Bounce up
                  obj.vy = -Math.abs(obj.vy*obj.damping);
                } else if (r.isTopHit(obj, otherBall)){
                  //bounce down
                  obj.vy = Math.abs(obj.vy*obj.damping); 
                }

                if (r.isLeftHit(obj, otherBall)){
                  //Bounce right
                  obj.vx = Math.abs(obj.vx*obj.damping);
                } else if (r.isRightHit(obj, otherBall)){
                  //Bounce left
                  obj.vx = -Math.abs(obj.vx*obj.damping); 
                }
            } 

            if (r.bottomReached(obj)) {
              obj.y = window.innerHeight - obj.radius;

              if (obj.vy > 5){
                //Bounce
                obj.vy = -(obj.vy * 0.70);
              } else {
                //Stop bouncing
                obj.vy = 0;

                if (otherBall) {
                  obj.vx = -(obj.vx * obj.damping);
                }

                obj.vx += obj.vx > 0 ? -0.008 : 0.008;

                if (obj.vx < 0.010 && obj.vx > -0.010) {
                  obj.vx = 0;
                }
              }

            } else if (r.topReached(obj)) {
              obj.vy = Math.abs(obj.vy);
            } 

            if (r.rightSideReached(obj)) {
              obj.vx = -obj.vx;
            } else if (r.leftSideReached(obj)) {
              obj.vx = Math.abs(obj.vx);
            }             
           }
          },
          render: function(timestamp) {
            var a = _balls;
            var delta = timestamp - (a.lastFrameTime || timestamp);

            a.lastFrameTime = timestamp;

            if (a.particles.length) {
              a.ctx.clearRect(
                0,
                0,
                window.innerWidth,
                window.innerHeight
              );
              a.draw(delta / 16.6, timestamp);
            }

            window.requestAnimationFrame(a.render);
          }
        };





        var _mouseControl = (function() {
          var _el = document.getElementById("canvas");

          _el.addEventListener("click", function(e) {
            _balls.init.call(_balls, e.clientX, e.clientY);
          });

        }());


      }());

...