圆到圆的碰撞分辨率

问题描述 投票:0回答:1

我正在尝试解决圆到圆的碰撞。问题是,球永远不会沉到底部,它们会继续摇晃。如何使球在循环结束时沉降到底部(不晃动)?

代码笔: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);
          });

        }());


      }());

javascript html5-canvas collision-detection
1个回答
0
投票

我大幅减少了代码以找出问题所在我的目的不是为您解决问题,而是为您提供调试此类问题的工具。

  • 如果问题仅在Y轴上,则将其简化为只有两个对象和一个碰撞。

  • 显示数据,您正在处理v(速度),请向它们显示以查看发生了什么。

  • 放慢动画,我发现setInterval给了我更多调试控制权。

<canvas id="canvas" width="40" height="140"></canvas>

<script>
  var canvas = document.getElementById("canvas")
  var ctx = canvas.getContext("2d")

  var _balls = {
    numOf: 2,
    particles: [],
    init: function (x, y) {
      for (var i = 0; i < this.numOf; i++) {
        this.particles.push({ x: x, y: y + (i * 60), vy: 0.5, radius: 20, gravity: 0.3, id: i });
      }
    },
    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;
    },
    bottomReached: function (obj) {
      return (obj.y + obj.radius) >= canvas.height;
    },
    draw: function () {
      ctx.clearRect(0, 0, 200, 200)
      for (var i = 0; i < _balls.particles.length; i++) {
        var obj = _balls.particles[i];

        ctx.beginPath();
        ctx.fillStyle = "black";
        ctx.moveTo(obj.x, obj.y);
        ctx.arc(obj.x, obj.y, obj.radius, 0, 2 * Math.PI, false);
        ctx.fill();
        ctx.closePath();
        ctx.beginPath();
        ctx.fillStyle = "white";
        ctx.fillText(obj.vy, 8, obj.y);
        ctx.closePath();

        obj.y += obj.vy
        obj.vy += obj.gravity

        _balls.isBallCollision(obj);

        if (_balls.bottomReached(obj)) {
          obj.y = canvas.height - obj.radius;

          if (obj.vy > 5) {
            obj.vy *= -0.2;
          } else {
            obj.vy = 0;
          }
        }
      }
    }
  };

  _balls.init(20, 20);
  setInterval(_balls.draw, 50)
</script>
© www.soinside.com 2019 - 2024. All rights reserved.