一本好的技术书籍往往都是由浅入深,本系列文章基本上也遵循这个原理。在上一章中我们介绍了高级的坐标旋转,并且通过它,我们实现了任意角度的碰撞反弹效果,它让物体与非水平和非垂直的面产生合乎情理的碰撞成为了可能。那么在本章我们继续深入的介绍另一个问题——物体与物体发生碰撞后应该如何处理?
也许你会说,在前面的章节中我们不是已经介绍过物体与物体之间的碰撞检测了吗?但是细细想来,其实我们只是完成了一个碰撞检测的前半部分,那么碰撞后的效果该如何处理呢?
在以前的动画中,我们对碰撞后的处理很简单,很暴力。首先是,设定位置。其次,水平和竖直方向分别乘以反弹系数(bounce)。是不是太简约,太粗暴了点。
那么,真实世界中,两个物体发生碰撞的情况是什么样呢?物体的重量不一样,速度不一样,运行方向不一样。如果发生了碰撞,之后他们的运动轨迹,速度大小可不是我们简单的乘以反弹系数就能解决的。
说了这么多的废话,本章的核心就是:如何处理物体发生碰撞后的运动,提前预告本章要用到的新的物理概念有:
动量
动量守恒
能量守恒
1.什么是动量
百度百科上这样说
动量(Momentum)又称线性动量(Linear Momentum)。在经典力学中,动量(是指国际单位制中的单位为kg·m/s ,量纲MLT⁻¹)表示为物体的质量和速度的乘积,是与物体的质量和速度相关的物理量,指的是运动物体的作用效果。动量也是矢量,它的方向与速度的方向相同
如果,你对中学的物理知识还有印象,那么这个概念应该很好理解。甚至可以轻松的写出动量的表达式
动量(p), 质量(m), 速度(v)
表达式: p = m * v
也就是说,动量是物体质量与速度的乘积。
2.动量守恒
动量守恒:如果一个系统不受外力或所受外力的矢量和为零,那么这个系统的总动量保持不变,这个结论叫做动量守恒定律
简单点来说就是,物体1与物体2如果发生碰撞,那么他们碰撞前的动量大小之和与碰撞后的动量大小之和是以不变的。
假设,物体1的质量为m1, 运动速度为v1, 碰撞后的速度v1F。物体2的质量为m2, 运动速度为v2, 碰撞后的速度v2F。 那么,他们满足
m1 * v1 + m2 * v2 = m1 * v1F + m2 * v2F
是不是很熟悉?当然这一个公式有两个未知数,是没法解出碰撞后的速度v1F和v2F的。这时候就需要能量守恒出场了。
3.能量守恒
这个概念相对来说很简单。物体以一定的速度运动,那么它的能量为多少呢?
E = 0.5 * m * v^2
同时,也有个能量守恒的规律,与动量守恒的描述相似。两物体如果发生碰撞,那么前后的能量保持不变。
(0.5 * m1 * v1^2) + (0.5 * m2 * v2^2) = (0.5 * m1 * v1F^2) + (0.5 * m2 * v2F^2)
ok,有了上面两个公式,我们就可以解出碰撞后的速度
公式1: m1 * v1 + m2 * v2 = m1 * v1F + m2 * v2F
公式2: (0.5 * m1 * v1^2) + (0.5 * m2 * v2^2) = (0.5 * m1 * v1F^2) + (0.5 * m2 * v2F^2)
解得,碰撞后的速度为
v1F = ((m1 - m2)*v1 + 2*m2*v2) / (m1+m2)
v2F = ((m2 - m1)*v2 + 2*m1*v1) / (m1+m2)
总结一句话:出来混总是要还的
4.代码实例
那么怎样把上面的内容应用到代码中呢。这里我们采用球类文件ball.js
。在上面加一行代码
this.mass = 1;
也就是设定球的初始质量为1,这就ok了,就是这么简单。然后我们先来水平方向的,先看效果图
具体代码如下:
<canvas id="canvas" width="500" height="500" style="background:#000">
<p>your browser not support canvas</p>
</canvas>
<script src="../js/utils.js"></script>
<script src="../js/ball.js"></script>
<script>
window.onload = function(){
var canvas = document.querySelector('canvas'),
context = canvas.getContext('2d'),
ball0 = new Ball(),
ball1 = new Ball(20,"red");
//球0
ball0.mass = 2; //质量为2
ball0.x = 50;
ball0.y = canvas.height/2;
ball0.vx = 1;
//球1
ball1.mass = 1; //质量为1
ball1.x = 400;
ball1.y = canvas.height/2;
ball1.vx = -1; //速度为反向,大小为1
(function drawFrame(){
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
ball0.x += ball0.vx;
ball1.x += ball1.vx;
var dist = ball1.x - ball0.x;
if(Math.abs(dist) < ball0.radius + ball1.radius){ //碰撞检测
//计算碰撞后的速度
var vx0Final = ((ball0.mass - ball1.mass)*ball0.vx + 2 * ball1.mass * ball1.vx)/(ball0.mass +ball1.mass);
var vx1Final = ((ball1.mass - ball0.mass)*ball1.vx + 2 * ball0.mass * ball0.vx)/(ball0.mass +ball1.mass);
//控制台打印
console.log(vx0Final, vx1Final);
//重新赋值
ball0.vx = vx0Final;
ball1.vx = vx1Final;
ball0.x += ball0.vx;
ball1.x += ball1.vx;
}
ball0.draw(context);
ball1.draw(context);
}())
}
</script>
在上面的代码中,我把碰撞后的代码在控制台打印出来。
带入公式算算,是否满足动量守恒。如果我们把球1的质量该的大一点呢?
ball0.mass = 3;
再看看效果
球1碰撞后的速度几乎为0,感觉是不是与自己的体型成正比啊。那么如果我们加大球2的速度呢?
ball1.vx = -3;
效果如下
难道这就是传说中的小身材大力量吗!更多的效果可以去自己尝试。
这节的内容就到这里了,主要是些基础概念性的东西。下一节,按照惯列就开始逐渐深入了,敬请期待。