美文网首页
JavaScript学习笔记之多小球非对心弹性碰撞

JavaScript学习笔记之多小球非对心弹性碰撞

作者: boborz | 来源:发表于2017-11-23 16:37 被阅读0次

引言

在学习JavaScript的时候,看到一个练习事例,就想到能不能做成类似Windows屏幕保护气泡那种效果,经过不断思考尝试,最后做出的效果如下图:

思路

其实上面的那种效果就是模拟理想物理环境下的多个小球非对心碰撞(对心碰撞是其特殊情况),所谓理想物理情况就是没有外力作用的封闭系统,内部遵循动量守恒定律和能量守恒定律。假设小球A和小球B的质量分别为𝑀𝑎𝑀𝖻,初始速度分别为𝑉𝑎𝑉𝖻,碰撞后的速度分别为𝑉𝑎'𝑉𝖻',两个小球的碰撞瞬间的状态如下图:

其中𝑉𝑠𝑎𝑉𝑠𝖻是两小球沿球心连线方向上的分速度,𝑉𝑡𝑎𝑉𝑡𝖻是两小球垂直球心连线方向上的分速度。碰撞后,由于两小球在垂直球心连线方向上没有力的相互作用,所以速度不变,还是𝑉𝑡𝑎𝑉𝑡𝖻,沿球心连线方向上的分速度为𝑉𝑠𝑎'𝑉𝑠𝖻'。运用以下物理公式:

能量守恒定律:
𝑀𝑎•𝑉𝑎²/2+𝑀𝖻•𝑉𝖻²/2=𝑀𝑎•𝑉𝑎'²/2+𝑀𝖻•𝑉𝖻'²/2

向量运算:
𝑉𝑎²= 𝑉𝑠𝑎²+𝑉𝑡𝑎²
𝑉𝖻²= 𝑉𝑠𝖻²+𝑉𝑡𝖻²
𝑉𝑎'²= 𝑉𝑠𝑎'²+𝑉𝑡𝑎²
𝑉𝖻'²= 𝑉𝑠𝖻'²+𝑉𝑡𝖻²

推导得出:
𝑀𝑎•𝑉𝑠𝑎²/2+𝑀𝖻•𝑉𝑠𝖻²/2=𝑀𝑎•𝑉𝑠𝑎'²/2+𝑀𝖻•𝑉𝑠𝖻'²/2

再联合动量守恒定律:
𝑀𝑎•𝑉𝑠𝑎+𝑀𝖻•𝑉𝑠𝖻=𝑀𝑎•𝑉𝑠𝑎'+𝑀𝖻•𝑉𝑠𝖻'

推导得出:
𝑉𝑠𝑎'=(𝑉𝑠𝑎•(𝑀𝑎-𝑀𝖻)+2•𝑀𝖻•𝑉𝑠𝖻)/(𝑀𝑎+𝑀𝖻)
𝑉𝑠𝖻'=(𝑉𝑠𝖻•(𝑀𝖻-𝑀𝑎)+2•𝑀𝑎•𝑉𝑠𝑎)/(𝑀𝑎+𝑀𝖻)

最后合成碰撞后的速度𝑉𝑎'𝑉𝖻'就ok了!

代码实现

// 小球对象构造函数
function Ball(x, y, speedX, speedY, color, radius, density) {
  this.x = x;
  this.y = y;
  this.speedX = speedX;
  this.speedY = speedY;
  this.color = color;
  this.radius = radius;
  this.density = density;
}

// 绘制
Ball.prototype.draw = function () {
  ctx.beginPath();
  ctx.fillStyle = this.color;
  ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
  ctx.fill();
}

// 边界碰撞检测
Ball.prototype.borderCollisionDetect = function () {
  if ((this.x + this.radius) >= width && this.speedX > 0) {
    this.speedX *= -1;
  }

  if ((this.x - this.radius) <= 0 && this.speedX < 0) {
    this.speedX *= -1;
  }

  if ((this.y + this.radius) >= height && this.speedY > 0) {
    this.speedY *= -1;
  }

  if ((this.y - this.radius) <= 0 && this.speedY < 0) {
    this.speedY *= -1;
  }
}
// 创建小球对象
function createBalls() {
  require(['utils'], function (utils) {
    while (balls.length < 8) {
      var ball = new Ball(
        utils.random(0, width),  // x
        utils.random(0, height), // y
        utils.random(1, 8),     // speedX
        utils.random(1, 8),     // speedY
        'rgb('+utils.random(0, 255) +','+ utils.random(0, 255)+','+ utils.random(0, 255) +')',
        30,                     // radius
        1                       // density
      );
      balls.push(ball);
    }
  });
}
// 更新小球速度和位置
function update() {
  for (let i = 0; i < balls.length; i++) {
    balls[i].borderCollisionDetect();

    for (let j = i + 1; j < balls.length; j++) {
      if (ballsCollisionDetect(balls[i], balls[j])) {
        collide(balls[i], balls[j]);
      }
    }

    // 更新位置
    balls[i].x += balls[i].speedX;
    balls[i].y += balls[i].speedY;
  }
}
// 碰撞检测
function ballsCollisionDetect(ball1, ball2) {
  //  当前距离
  var dx = ball1.x - ball2.x;
  var dy = ball1.y - ball2.y;
  var distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));

  //  预测下一时刻会不会碰撞
  let dx_next = ball1.x + ball1.speedX - ball2.x - ball2.speedX;
  let dy_next = ball1.y + ball1.speedY - ball2.y - ball2.speedY;
  let distance_next = Math.sqrt(Math.pow(dx_next, 2) + Math.pow(dy_next, 2));

  if (distance_next < ball1.radius + ball2.radius && distance_next < distance) {
    return true;
  }
  return false;
}

// 更新碰撞后的状态
function collide(ball1, ball2) {
  require(['Vector2d'], function (Vector2d) {
    // 初始速度向量
    let speed_ball1_initial = new Vector2d(ball1.speedX, ball1.speedY);
    let speed_ball2_initial = new Vector2d(ball2.speedX, ball2.speedY);

    // 球心方向单位向量
    let s = new Vector2d(ball2.x - ball1.x, ball2.y - ball1.y);
    s = s.normalize();

    // 垂直球心方向单位向量
    let t = s.rotate(Math.PI / 2);

    // 速度在球心向量上的分速度投影
    let speed_ball1_initial_sc = speed_ball1_initial.dotProduct(s)/s.length();
    let speed_ball2_initial_sc = speed_ball2_initial.dotProduct(s)/s.length();

    // 速度在垂直球心向量上的分速度投影
    let speed_ball1_initial_tc = speed_ball1_initial.dotProduct(t)/t.length();
    let speed_ball2_initial_tc = speed_ball2_initial.dotProduct(t)/t.length();

    // 碰撞后球心方向上的分速度
    let speed_ball1_final_sc = (speed_ball1_initial_sc * (ball1.density * Math.pow(ball1.radius,3) - ball2.density * Math.pow(ball2.radius,3)) + 2 * (ball2.density * Math.pow(ball2.radius,3)) * speed_ball2_initial_sc)
     / (ball1.density * Math.pow(ball1.radius,3) + ball2.density * Math.pow(ball2.radius,3));
    let speed_ball2_final_sc = (speed_ball2_initial_sc * (ball2.density * Math.pow(ball2.radius,3) - ball1.density * Math.pow(ball1.radius,3)) + 2 * (ball1.density * Math.pow(ball1.radius,3)) * speed_ball1_initial_sc)
     / (ball1.density * Math.pow(ball1.radius,3) + ball2.density * Math.pow(ball2.radius,3));

    // 碰撞后球心方向上的分速度向量
    let speed_ball1_final_s = s.scale(speed_ball1_final_sc);
    let speed_ball2_final_s = s.scale(speed_ball2_final_sc);

    // 碰撞后垂直球心方向上的分速度向量
    let speed_ball1_final_t = t.scale(speed_ball1_initial_tc);
    let speed_ball2_final_t = t.scale(speed_ball2_initial_tc);

    // 结束速度向量
    let speed_ball1_final = speed_ball1_final_s.add(speed_ball1_final_t);
    let speed_ball2_final = speed_ball2_final_s.add(speed_ball2_final_t);

    // 更新速度
    ball1.speedX = speed_ball1_final.x;
    ball1.speedY = speed_ball1_final.y;

    ball2.speedX = speed_ball2_final.x;
    ball2.speedY = speed_ball2_final.y;
  });
}

缺陷

本代码是通过window.requestAnimationFrame()方法循环执行来实现动画效果的,它的回调次数是每秒60次,所以对于一些速度"过快"的小球,会在撞击边界时出现"撞出去一小部分"的情况。还有本代码只考虑了两个小球相撞的情况,没有考虑三个以上小球同时相撞的场景。

结语

本代码是学习JavaScript时的实战演练,能加深对这门语言的理解和掌握。完整代码详见GitHub地址

参考链接

https://www.lyblog.net/detail/397.html
http://www.cnblogs.com/kenkofox/archive/2011/09/06/2168944.html
http://tina0152.blog.163.com/blog/static/119447958200910229109326/
http://www.51testing.com/html/66/n-861166-2.html

相关文章

  • JavaScript学习笔记之多小球非对心弹性碰撞

    引言 在学习JavaScript的时候,看到一个练习事例,就想到能不能做成类似Windows屏幕保护气泡那种效果,...

  • 轻量级Javascript游戏框架CreateJS创建精灵+P2

    使用轻量级javascript游戏框架的弹性小球实例 HTML JAVASCRIPT Create.js 创建精灵...

  • 理科生爱之表达

    完全非弹性碰撞(当A小球和小球相遇后,他们变会不离不弃,一同朝着一个方向,一起运动,一起停止。总之,一直一直在一起...

  • Java 小球碰撞

    小球碰撞的关键是 判断小球是否碰撞过:1.当两小球间距小于小球直径时发生碰撞。2.当小球碰到边缘时发生碰撞。

  • day-11-pygame的应用

    小球碰撞,大球碰击小球,小球消失

  • 非弹性碰撞 02 陌路

    三人刚坐下,就有一个女生,气冲冲的走过来。 “陆续。”女人二话不说,就坐在陆续的身边。 坐在他身边的高倚岳和刘晋都...

  • JavaScript学习笔记二

    JavaScript学习笔记二 个人学习笔记参考阮一峰的JavaScript教学学习笔记二是对学习笔记一的补充 J...

  • 小球碰撞

  • 2018-09-04 Day12-pygame小游戏

    1、小球碰撞 2、接球游戏 3、大球吃小球

  • day12-作业

    实现鼠标点击屏幕产生小球,小球自动移动,与屏幕碰撞会反弹。小球之间碰撞,会随机吃掉。 定义部分颜色的模块

网友评论

      本文标题:JavaScript学习笔记之多小球非对心弹性碰撞

      本文链接:https://www.haomeiwen.com/subject/hugzvxtx.html