Skip to content

Tag Archives: vertors for flash

Flash向量-8-球和角

2009-09-04

不是所有的墙壁都会无限延伸,他们通常从某处出发,然后在某处结束。我们把这称为起点和终点。现在我要计算球与墙壁的角碰撞时的位置点和碰撞后的运动向量。

红色的是墙壁向量,灰色的是球的运动向量,可能碰到墙壁,也可能碰到墙角。在findIntersection函数里面,我们检测到距离球最近的墙壁,并找到让球“从相交状态回到紧贴状态的那个向量”。
墙壁向量为v2,球的运动向量是v1。先找到从墙壁的起点到球的中心的向量v3:

v3.vx=v1.p1.x-v2.p0.x;
v3.vy=v1.p1.y-v2.p0.y;

然后计算v3和墙壁向量v2的点乘:

var dp=v3.vx*v2.dx + v3.vy*v2.dy;

如果点乘为负数的话,说明球离墙壁的起点很近。并且那个移动回去的向量是向量v3。

if(dp0){
var v=v4;
}

然而,如果这个时候,点乘为0或者负数,那么墙壁的边缘和球很近。此时,我们从墙壁起点到球中心的向量v3在墙壁法线上进行投影。球将会从墙壁法线方向移动回去,就像上一节一样。

else{
var v=projectVector(v3, v2.lx, v2.ly);
}

最后我们返回当前的向量。

return v;

记住球的反弹向量将和球的返回向量垂直。当球碰到墙壁的时候,我们可以用墙壁向量来计算新的移动向量。当球碰到墙角的时候,从墙壁的终点到球中心的向量的法线就可以派上用场了。
尝试拖动墙壁试一试,观察球与墙壁的碰撞。

因为我们只检测了球的运动向量的终点,所以当球的速度非常快的时候,有可能球会穿过墙壁。为了避免这种情况,你应该确保球的速度永远不会超过它的半径。

你可以分别下载球与一个墙壁和多个墙壁碰撞的例子。

Flash向量-7-球和线

2009-09-03

球和线

到现在你可能已经厌倦了总是只有一个移动的点。倒不是说点本身有什么不好,它们是好的,但是毕竟,点只是点。你能在你的周围能看到多少个点?我相信不会很多。最后,我们将向前迈进一大步,开始移动一些更加真实的东东西 – 球。

我相信你在生活中看到过很多的球,所以你知道他们和点有什么不同。球是有宽度的。在我们的2D例子中,球是用圆来表示的,它有圆心坐标并且有半径。

game.myOb={r:10};
game.myOb.p0={x:150, y:100};

已知一个运动的球的半径是10,另外我们知道球的中心坐标,它的移动向量和半径,我们要计算球撞上墙壁的会在哪儿。

图中,灰色的圈圈表示球的中心与墙壁的交点。事实上球在更早的时候就碰到墙壁了。球碰到墙壁的时候,球的位置应该这样的,将墙壁向量(green)向它的法线方向移动一个半径的距离,然后求出这个新的向量和球的运动向量(红色)的交点。所以,我们可以不使用墙壁向量,而是用一个方向相同,起点位置不同的向量。

Axes method

球和线的交点也可以用别的方法来计算。下面就来研究下如何通过一些简单的投影来确定球是否与墙壁碰撞。

图中,我们可以看到,球处于他的运动向量的终点p1处。我们绘制一个蓝色的向量,从墙壁向量的起点(v2.p0)出发,到达球的中心。现在我们将这个新的向量在墙壁的法线上进行投影,得到向量v3(红色)。为了将球紧贴墙壁放置,我需要将它向墙壁的法线方向移动:

ball.r-v3.len

在示例代码中,我们计算出墙壁法线的单位向量:

v.lx = v.dy;
v.ly = -v.dx;

在runme函数里,我们检查所有的墙壁:

for(var i=1; i=0){
//move object away from the wall
ob.p1.x+=w.lx*pen;
ob.p1.y+=w.ly*pen;
//change movement
var vb=bounce(ob, w);
ob.vx=vb.vx;
ob.vy=vb.vy;
}
}

对于每一个墙壁,如果球撞进去了,我们就计算出撞进去的距离,再将球拉回来,然后用之前同样的方法去计算反弹之后的向量。

计算撞进去的距离的函数:

function findIntersection(v1, v2){
//vector between center [...]

Flash向量-6-弹性

2009-08-31

一旦碰撞发生,通常会在阻挡的地反发生反弹。当你的头撞到墙的时候是一个非常明显的例子(我不建议你这样尝试,那只会导致你头疼),但是如果观察一些事物比如球,会非常容易的发现球碰到障碍物的时候,很少继续向前或者正好停在那附近。

让我们研究下,在碰撞发生之后,如何计算新的运动向量。

图中,红线是运动向量,绿线是墙壁,黑线是墙壁的法线,而蓝线是碰撞发生后的新的运动向量。粗线是运动向量在墙壁和墙壁的法线上的投影。

原始运动向量和新的运动向量之间的不同是很明显的。看不出来吗?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源文件。

Flash向量-5-相交

2009-08-30

现在你已经知道如何来移动物体了,现实世界中,移动的对象迟早是要碰到什么东东的。举个例子,如果移动的对象就是你本人,并且你很幸运的话,你碰到的东西很软,比如床,但是有时候你可能碰到坚硬的东西,砖头墙,水泥板,关闭的门,以及这个世界上的其他的一些等待你或者其他运动的物体去碰撞的坚硬物体。所以,疼痛的一个好处就是让人们知道什么东西可以去碰撞。

我们知道对象的移动向量,我们也知道墙壁所构成的向量。我们要求得 是否,何时,何地,物体会和墙壁碰撞。换句话说就是:“2个向量在哪里相交?”

首先,让我们看看什么时候2个向量不想交。向量平行的时候不相交。并且平行向量的单位向量是相等的(方向可以不相同,它们可以使反向的):

(v1.dx==v2.dx and v1.dy==v2.dy) or
(v1.dx==-v2.dx and v1.dy==-v2.dy)

如果它们不平行,那么相交时迟早的事情。绿线是移动向量,红线是墙壁,蓝线在它们的起点之间:

交点的坐标可以这样计算:

v3={vx:v2.p0.x-v1.p0.x, vy:v2.p0.y-v1.p0.y};
var t=perP(v3, v2)/perP(v1, v2);
ip={};
ip.x=v1.p0.x+v1.vx*t;
ip.y=v1.p0.y+v1.vy*t;
function perP(va, vb){
pp = va.vx*vb.vy – va.vy*vb.vx;
return pp;
}

首先,我们计算出在它们起点之间的向量v3。上面的函数是用来计算2个向量的垂直点乘(perp dot product)【as4game注:垂直点乘这个概念,在中文的数学教科书我是没发现过的。参考网址】。
垂直点乘和点乘非常相似,不同的是用v1的法线取代v1。点乘是:

dp = v1.vx*v2.vx + v1.vy*v2.vy;

v1的法线是:

v.rx = -v.vy;
v.ry = v.vx;

然后我们用v1的法线代替v1:

pp = -v1.vy*v2.vx + v1.vx*v2.vy;

这就是垂直点乘了。

当两个垂直点乘的比例正好为1,那么交点就正好在移动向量的终点上。当为0-1之间的值时,那么交点就在v1上,当为负数交点就在向量的起点之前,当为大于1的数,交点就在向量的终点之后。
当然如果你想找到在另外一个向量上的交点,你也需要为v2计算t值:

v3={vx:v1.p0.x-v2.p0.x, vy:v1.p0.y-v2.p0.y};
var t=perP(v3, v1)/perP(v2, v1);

在下面的例子中,你可以尝试拖动那些点,然后观察向量的交点:

如果你对求交点的实际算法感兴趣,可以在这里查看。

你可以下载fla源文件。