canvas小球碰撞运动

问题描述

网上找了一段canvas小球碰撞运动的代码,代码很棒,但是有个问题就是当两个小球一开始就是交叉的时候,它们的运动轨迹是一样的,按理说运动轨迹是随机的,不知道为什么会出现一起跑的情况?

相关代码

window.onload = function () {
  var canvas = document.getElementById("ball");
  var cxt = canvas.getContext("2d");
  var r = 20;
  var maxNum = 5;
  var ballArray = new Array();
  var maxX = canvas.width;
  var maxY = canvas.height;
  for (var n = 0; n < maxNum; n++) {
    var x = {
      x: getRandomNumber(r, maxX - r),
      y: getRandomNumber(r, maxY - r),
      r: r,
      vX: getRandomNumber(0.5, 1),
      vY: getRandomNumber(0.5, 1),
      color: getRandomColor(),
    }
    ballArray.push(x);
  }

  function getRandomColor() {
    return (function (m, s, c) {
      return (c ? arguments.callee(m, s, c - 1) : '#') +
        s[m.floor(m.random() * 16)]
    })(Math, '0123456789abcdef', 5)
  }

  draw();

  function draw() {
    cxt.fillStyle = "#000";
    cxt.fillRect(0, 0, canvas.width, canvas.height);
    for (i in ballArray) {
      var x = i;

      ballArray[i].x += ballArray[i].vX /2;
      ballArray[i].y += ballArray[i].vY/2;

      if (ballArray[i].x >= maxX - r) {
        ballArray[i].x = maxX - r;
        ballArray[i].vX = -ballArray[i].vX;
      }
      if (ballArray[i].x <= r) {
        ballArray[i].x = r;
        ballArray[i].vX = -ballArray[i].vX;
      }
      if (ballArray[i].y >= maxY - r) {
        ballArray[i].y = maxY - r;
        ballArray[i].vY = -ballArray[i].vY;
      }
      if (ballArray[i].y <= r) {
        ballArray[i].y = r;
        ballArray[i].vY = -ballArray[i].vY;
      }

      for (var j = 0; j < maxNum; j++)
        if (j !== x) {
          if (Math.round(Math.pow(ballArray[x].x - ballArray[j].x, 2) +
              Math.pow(ballArray[x].y - ballArray[j].y, 2)) <=
            Math.round(Math.pow(r + r, 2))) {

            var tempX = ballArray[x].vX;
            var tempY = ballArray[x].vY;
            ballArray[x].vX = ballArray[j].vX;
            ballArray[j].vX = tempX;
            ballArray[x].vY = ballArray[j].vY;
            ballArray[j].vY = tempY;
          }
        }
      cxt.beginPath();

      cxt.fillStyle = ballArray[i].color;
      cxt.arc(ballArray[i].x, ballArray[i].y, ballArray[i].r, 0, Math.PI * 2, true);
      cxt.closePath();
      cxt.fill();
    }
    setTimeout(function () {
      draw();
    }, 10);
  }

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

你期待的结果是什么?实际看到的错误信息又是什么?

按理说运动轨迹是随机的,互不影响的。

阅读 3.1k
1 个回答
可以根据源码看出来

其中有一个循环,遍历了所有的小球,在循环的开始处,根据小球的速度,来为他进行一段偏移。

根据如下截图,其作用是,在每一个循环里,对这个小球和其他所有小球做一个碰撞校验,如果两个小球发生重叠,则交换这两个小球的速度(可以看出来这个程序实现的不是弹性碰撞,而是简单的交换速度)。

但是呢,这个循环是对所有小球发生的,也就是说,如果两个小球不重叠,那么没关系,其中先遍历到的小球会把两一个小球向相反的方向修改速度,然后下一个小球被循环到的时候,两个小球就会远离,由于速度是一样的,因此会修正到碰撞前的距离,不会第二次触发这个修改。

但是,如果两个小球一开始就有大量重叠,那么第一次修改完以后,遍历到第二个小球时,其速度依然不足以使其离开第一个小球,于是又触发了第二次速度交换。然后他们俩又回到了之前的速度(相当于碰撞检测失效了)

clipboard.png

这个同时也解释为什么他们的运动轨迹一样。

上面的说法稍微有点绕,我整理一下。
他们的运动位移是由循环时处理的,其流程是:

  1. 第一个小球按照第一个小球的速度位移
  2. 第一个小球进行碰撞检测,发现和第二个小球重叠,于是交换速度
  3. 第二个小球按照原来的第一个小球的速度位移(其速度被第一个小球交换了)
  4. (由于重叠区域比较大,位移结束仍然重叠),第二个小球的碰撞检测,发现和第一个小球重叠,再次交换速度。
  5. 结束循环时,第一个第二个小球都是按照第一个小球的速度进行了位移,但是相对距离和速度都没有改变
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题