一旦碰撞发生,通常会在阻挡的地反发生反弹。当你的头撞到墙的时候是一个非常明显的例子(我不建议你这样尝试,那只会导致你头疼),但是如果观察一些事物比如球,会非常容易的发现球碰到障碍物的时候,很少继续向前或者正好停在那附近。
让我们研究下,在碰撞发生之后,如何计算新的运动向量。
图中,红线是运动向量,绿线是墙壁,黑线是墙壁的法线,而蓝线是碰撞发生后的新的运动向量。粗线是运动向量在墙壁和墙壁的法线上的投影。
原始运动向量和新的运动向量之间的不同是很明显的。看不出来吗?ok,你可以看一下他们的投影,在墙壁上的投影是一样的,在墙壁的法线上的投影方向相反。从这一点,我们很容易构建一个运动物体与墙壁之间的反弹系统:
找到交点
找到运动向量的投影
将法线上的投影反向
投影相加
如果你已经忘记了什么是投影,你可以在基础知识章节中查看。
v1是运动向量,v2是墙壁向量,v2的左法线是:
v2.lx = v2.vy;
v2.ly = -v2.vx;
v1,v2的点乘是:
dp1 = v1.vx*v2.vx + v1.vy*v2.vy;
运动向量在v2上的投影:
proj1.vx=dp1*v2.dx;
proj1.vy=dp1*v2.dy;
v1和v2法线的点乘:
dp2 = v1.vx*v2.lx + v1.vy*v2.ly;
运动向量在v2上的投影(注意,我们需要将左法线除以长度,将其归一化)
proj2.vx=dp*(v2.lx/v2.len);
proj2.vy=dp*(v2.ly/v2.len);
将投影反向:
proj2.vx*=-1;
proj2.vy*=-1;
通过投影相加,计算出新的向量:
v1.vx=proj1.vx+proj2.vx;
v1.vy=proj1.vy+proj2.vy;
在下面的例子中,你可以拖动一些点,来观察运动向量的变化:
你可以下载fla源文件。
真实的反弹和摩擦
在真实的世界中,没有物体可以永远运动,因为摩擦会损耗能量,而弹性碰撞也不是完全的。完全弹性碰撞意味着碰撞前后运动向量的长度不变。为了将反弹过程中的能量损失考虑进来,我们需要赋予物体2个属性:“弹性系数”和“摩擦系数”:
ob.b=0.99;
ob.f=0.99;
为了计算新的运动向量,我们可以分别将投影和两个物体的b和f相乘。弹性系数影响墙壁法线上的投影,摩擦系数影响墙壁上的投影。
v1.vx=v1.f*v2.f*proj1.vx+v1.b*v2.b*proj2.vx;
v1.vy=v1.f*v2.f*proj1.vy+v1.b*v2.b*proj2.vy;
如果每个物体的f和b都等于1,会发生什么呢?将会发生完全弹性碰撞,在碰撞的过程中,什么也没有损失。运动将以同样的强度继续。举个例子,如果没有弹性(b=0),那么运动向量在发生碰撞之后将与墙壁平行,物体将粘在墙壁上。想象一下球飞到油或者胶水上的样子。
当然,你也可以将弹性系数设置为大于1,在那种情况下,碰撞将加速物体的移动。一个比较好的例子是弹球游戏中的杠子,球碰到的时候,会有增加额外的速度。
在这个例子中,球在舞台上运动,有重力,并且有许多的墙壁。
运动的点会遍历所有的墙壁,自从上次检测到现在的时间里,是否会和他们发生碰撞。如果会碰到多个墙壁,交点最近的墙壁会被选择。然后将物体的终点会设置到交点的位置,并计算出新的运动向量。然后再将终点从交点的位置向新的运动向量的方向移动,移动多少呢?用原来的运动向量的长度减去从起点到交点的长度,就是这么多。
你至少还可以从2个方面来增强这个系统:
1.因为对象上一次绘制在p0,而下一次绘制在p1,我们看不到它曾经在交点附件呆过。这可能导致对象看起来像直接从p0到p1。虽然这样做逻辑上是不正确的,但是可以让人亲眼看到点曾经碰到墙壁。
2.当对象撞到一个墙壁之后,可能撞到第2个墙壁。而目前的方式,我们只考虑了最近的墙壁的碰撞。为了更加精确一些,我们可以在碰撞发生之后,立即在检查新的向量与所有的墙壁之间的碰撞,这个时候之需要检查从交点到p1的向量(图中的蓝线)。
你可以下载fla源文件。